2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1999-2004 The OpenLDAP Foundation.
5 * Portions Copyright 1999 Dmitry Kovalev.
6 * Portions Copyright 2004 Mark Adamson.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
18 * This work was initially developed by Dmitry Kovalev for inclusion
19 * by OpenLDAP Software.
25 #include <sys/types.h>
26 #include "ac/string.h"
30 #include "proto-sql.h"
32 static int backsql_process_filter( backsql_srch_info *bsi, Filter *f );
33 static int backsql_process_filter_eq( backsql_srch_info *bsi,
34 backsql_at_map_rec *at,
35 int casefold, struct berval *filter_value );
36 static int backsql_process_filter_like( backsql_srch_info *bsi,
37 backsql_at_map_rec *at,
38 int casefold, struct berval *filter_value );
39 static int backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f,
40 backsql_at_map_rec *at );
43 backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad )
46 AttributeName *an = NULL;
48 if ( bsi->bsi_attrs == NULL ) {
53 * clear the list (retrieve all attrs)
56 ch_free( bsi->bsi_attrs );
57 bsi->bsi_attrs = NULL;
58 bsi->bsi_flags |= BSQL_SF_ALL_ATTRS;
62 for ( ; !BER_BVISNULL( &bsi->bsi_attrs[ n_attrs ].an_name ); n_attrs++ ) {
63 an = &bsi->bsi_attrs[ n_attrs ];
65 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
66 "attribute \"%s\" is in list\n",
67 an->an_name.bv_val, 0, 0 );
69 * We can live with strcmp because the attribute
70 * list has been normalized before calling be_search
72 if ( !BACKSQL_NCMP( &an->an_name, &ad->ad_cname ) ) {
77 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
78 "adding \"%s\" to list\n", ad->ad_cname.bv_val, 0, 0 );
80 an = (AttributeName *)ch_realloc( bsi->bsi_attrs,
81 sizeof( AttributeName ) * ( n_attrs + 2 ) );
86 an[ n_attrs ].an_name = ad->ad_cname;
87 an[ n_attrs ].an_desc = ad;
88 BER_BVZERO( &an[ n_attrs + 1 ].an_name );
96 * Initializes the search structure.
98 * If get_base_id != 0, the field bsi_base_id is filled
99 * with the entryID of bsi_base_ndn; it must be freed
100 * by backsql_free_entryID() when no longer required.
102 * NOTE: base must be normalized
106 backsql_srch_info *bsi,
107 struct berval *nbase,
116 AttributeName *attrs,
120 int rc = LDAP_SUCCESS;
122 bsi->bsi_base_ndn = nbase;
123 bsi->bsi_use_subtree_shortcut = 0;
124 BER_BVZERO( &bsi->bsi_base_id.eid_dn );
125 BER_BVZERO( &bsi->bsi_base_id.eid_ndn );
126 bsi->bsi_scope = scope;
127 bsi->bsi_slimit = slimit;
128 bsi->bsi_tlimit = tlimit;
129 bsi->bsi_filter = filter;
133 bsi->bsi_flags = BSQL_SF_NONE;
138 if ( attrs == NULL ) {
139 /* also add request for all operational */
140 bsi->bsi_attrs = NULL;
141 bsi->bsi_flags |= BSQL_SF_ALL_USER;
146 bsi->bsi_attrs = (AttributeName *)ch_calloc( 1,
147 sizeof( AttributeName ) );
148 BER_BVZERO( &bsi->bsi_attrs[ 0 ].an_name );
150 for ( p = attrs; !BER_BVISNULL( &p->an_name ); p++ ) {
152 * ignore "1.1"; handle "+"
154 if ( BACKSQL_NCMP( &p->an_name, &AllUser ) == 0 ) {
155 bsi->bsi_flags |= BSQL_SF_ALL_USER;
158 } else if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 ) {
159 bsi->bsi_flags |= BSQL_SF_ALL_OPER;
162 } else if ( BACKSQL_NCMP( &p->an_name, &NoAttrs ) == 0 ) {
165 } else if ( p->an_desc == slap_schema.si_ad_objectClass ) {
169 backsql_attrlist_add( bsi, p->an_desc );
173 /* add objectClass if not present,
174 * because it is required to understand
175 * if an entry is a referral, an alias
177 backsql_attrlist_add( bsi, slap_schema.si_ad_objectClass );
181 bsi->bsi_abandon = 0;
182 bsi->bsi_id_list = NULL;
183 bsi->bsi_id_listtail = &bsi->bsi_id_list;
184 bsi->bsi_n_candidates = 0;
185 bsi->bsi_stoptime = stoptime;
186 BER_BVZERO( &bsi->bsi_sel.bb_val );
187 bsi->bsi_sel.bb_len = 0;
188 BER_BVZERO( &bsi->bsi_from.bb_val );
189 bsi->bsi_from.bb_len = 0;
190 BER_BVZERO( &bsi->bsi_join_where.bb_val );
191 bsi->bsi_join_where.bb_len = 0;
192 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
193 bsi->bsi_flt_where.bb_len = 0;
194 bsi->bsi_filter_oc = NULL;
196 if ( flags & BACKSQL_ISF_GET_ID ) {
197 assert( op->o_bd->be_private );
199 rc = backsql_dn2id( op, rs, &bsi->bsi_base_id, dbh, nbase, 1 );
202 return ( bsi->bsi_status = rc );
206 backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op )
214 backsql_strfcat( &bsi->bsi_flt_where, "c", '(' /* ) */ );
217 res = backsql_process_filter( bsi, f );
220 * TimesTen : If the query has no answers,
221 * don't bother to run the query.
232 case LDAP_FILTER_AND:
233 backsql_strfcat( &bsi->bsi_flt_where, "l",
234 (ber_len_t)STRLENOF( " AND " ),
239 backsql_strfcat( &bsi->bsi_flt_where, "l",
240 (ber_len_t)STRLENOF( " OR " ),
246 backsql_strfcat( &bsi->bsi_flt_where, "c", /* ( */ ')' );
252 backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f,
253 backsql_at_map_rec *at )
255 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
263 /* always uppercase strings by now */
264 #ifdef BACKSQL_UPPERCASE_FILTER
265 if ( f->f_sub_desc->ad_type->sat_substr &&
266 SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
267 bi->sql_caseIgnoreMatch ) )
268 #endif /* BACKSQL_UPPERCASE_FILTER */
273 if ( f->f_sub_desc->ad_type->sat_substr &&
274 SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
275 bi->sql_telephoneNumberMatch ) )
282 * to check for matching telephone numbers
283 * with intermixed chars, e.g. val='1234'
286 * val LIKE '%1%2%3%4%'
290 if ( f->f_sub_initial.bv_val ) {
291 bv.bv_len += f->f_sub_initial.bv_len;
293 if ( f->f_sub_any != NULL ) {
294 for ( a = 0; f->f_sub_any[ a ].bv_val != NULL; a++ ) {
295 bv.bv_len += f->f_sub_any[ a ].bv_len;
298 if ( f->f_sub_final.bv_val ) {
299 bv.bv_len += f->f_sub_final.bv_len;
301 bv.bv_len = 2 * bv.bv_len - 1;
302 bv.bv_val = ch_malloc( bv.bv_len + 1 );
305 if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
306 bv.bv_val[ s ] = f->f_sub_initial.bv_val[ 0 ];
307 for ( i = 1; i < f->f_sub_initial.bv_len; i++ ) {
308 bv.bv_val[ s + 2 * i - 1 ] = '%';
309 bv.bv_val[ s + 2 * i ] = f->f_sub_initial.bv_val[ i ];
311 bv.bv_val[ s + 2 * i - 1 ] = '%';
315 if ( f->f_sub_any != NULL ) {
316 for ( a = 0; !BER_BVISNULL( &f->f_sub_any[ a ] ); a++ ) {
317 bv.bv_val[ s ] = f->f_sub_any[ a ].bv_val[ 0 ];
318 for ( i = 1; i < f->f_sub_any[ a ].bv_len; i++ ) {
319 bv.bv_val[ s + 2 * i - 1 ] = '%';
320 bv.bv_val[ s + 2 * i ] = f->f_sub_any[ a ].bv_val[ i ];
322 bv.bv_val[ s + 2 * i - 1 ] = '%';
327 if ( !BER_BVISNULL( &f->f_sub_final ) ) {
328 bv.bv_val[ s ] = f->f_sub_final.bv_val[ 0 ];
329 for ( i = 1; i < f->f_sub_final.bv_len; i++ ) {
330 bv.bv_val[ s + 2 * i - 1 ] = '%';
331 bv.bv_val[ s + 2 * i ] = f->f_sub_final.bv_val[ i ];
333 bv.bv_val[ s + 2 * i - 1 ] = '%';
337 bv.bv_val[ s - 1 ] = '\0';
339 (void)backsql_process_filter_like( bsi, at, casefold, &bv );
340 ch_free( bv.bv_val );
346 * When dealing with case-sensitive strings
347 * we may omit normalization; however, normalized
348 * SQL filters are more liberal.
351 backsql_strfcat( &bsi->bsi_flt_where, "c", '(' /* ) */ );
354 Debug( LDAP_DEBUG_TRACE, "backsql_process_sub_filter(%s):\n",
355 at->bam_ad->ad_cname.bv_val, 0, 0 );
356 Debug(LDAP_DEBUG_TRACE, " expr: '%s%s%s'\n", at->bam_sel_expr.bv_val,
357 at->bam_sel_expr_u.bv_val ? "' '" : "",
358 at->bam_sel_expr_u.bv_val ? at->bam_sel_expr_u.bv_val : "" );
359 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
361 * If a pre-upper-cased version of the column
362 * or a precompiled upper function exists, use it
364 backsql_strfcat( &bsi->bsi_flt_where,
367 (ber_len_t)STRLENOF( " LIKE '" ),
371 backsql_strfcat( &bsi->bsi_flt_where, "bl",
373 (ber_len_t)STRLENOF( " LIKE '" ), " LIKE '" );
376 if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
380 Debug( LDAP_DEBUG_TRACE,
381 "==>backsql_process_sub_filter(%s): "
382 "sub_initial=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
383 f->f_sub_initial.bv_val, 0 );
384 #endif /* BACKSQL_TRACE */
386 start = bsi->bsi_flt_where.bb_val.bv_len;
387 backsql_strfcat( &bsi->bsi_flt_where, "b",
389 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
390 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
394 backsql_strfcat( &bsi->bsi_flt_where, "c", '%' );
396 if ( f->f_sub_any != NULL ) {
397 for ( i = 0; !BER_BVISNULL( &f->f_sub_any[ i ] ); i++ ) {
401 Debug( LDAP_DEBUG_TRACE,
402 "==>backsql_process_sub_filter(%s): "
403 "sub_any[%d]=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
404 i, f->f_sub_any[ i ].bv_val );
405 #endif /* BACKSQL_TRACE */
407 start = bsi->bsi_flt_where.bb_val.bv_len;
408 backsql_strfcat( &bsi->bsi_flt_where,
412 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
414 * Note: toupper('%') = '%'
416 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
421 if ( !BER_BVISNULL( &f->f_sub_final ) ) {
425 Debug( LDAP_DEBUG_TRACE,
426 "==>backsql_process_sub_filter(%s): "
427 "sub_final=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
428 f->f_sub_final.bv_val, 0 );
429 #endif /* BACKSQL_TRACE */
431 start = bsi->bsi_flt_where.bb_val.bv_len;
432 backsql_strfcat( &bsi->bsi_flt_where, "b",
434 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
435 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
439 backsql_strfcat( &bsi->bsi_flt_where, "l",
440 (ber_len_t)STRLENOF( /* (' */ "')" ), /* (' */ "')" );
446 backsql_merge_from_tbls( backsql_srch_info *bsi, struct berval *from_tbls )
448 if ( BER_BVISNULL( from_tbls ) ) {
452 if ( !BER_BVISNULL( &bsi->bsi_from.bb_val ) ) {
453 char *start, *end, *tmp;
455 tmp = ch_strdup( from_tbls->bv_val );
457 for ( start = tmp, end = strchr( start, ',' ); start; ) {
462 if ( strstr( bsi->bsi_from.bb_val.bv_val, start) == NULL )
464 backsql_strfcat( &bsi->bsi_from, "cs", ',', start );
468 /* in case there are spaces after the comma... */
469 for ( start = &end[1]; isspace( start[0] ); start++ );
471 end = strchr( start, ',' );
483 backsql_strfcat( &bsi->bsi_from, "b", from_tbls );
490 backsql_process_filter( backsql_srch_info *bsi, Filter *f )
492 backsql_at_map_rec **vat = NULL;
493 AttributeDescription *ad = NULL;
498 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter()\n", 0, 0, 0 );
499 if ( f->f_choice == SLAPD_FILTER_COMPUTED ) {
500 Debug( LDAP_DEBUG_TRACE, "backsql_process_filter(): "
501 "invalid filter\n", 0, 0, 0 );
506 switch( f->f_choice ) {
508 rc = backsql_process_filter_list( bsi, f->f_or,
513 case LDAP_FILTER_AND:
514 rc = backsql_process_filter_list( bsi, f->f_and,
519 case LDAP_FILTER_NOT:
520 backsql_strfcat( &bsi->bsi_flt_where, "l",
521 (ber_len_t)STRLENOF( "NOT (" /* ) */ ),
523 rc = backsql_process_filter( bsi, f->f_not );
524 backsql_strfcat( &bsi->bsi_flt_where, "c", /* ( */ ')' );
528 case LDAP_FILTER_PRESENT:
532 case LDAP_FILTER_EXT:
533 ad = f->f_mra->ma_desc;
534 if ( f->f_mr_dnattrs ) {
536 * if dn attrs filtering is requested, better return
537 * success and let test_filter() deal with candidate
538 * selection; otherwise we'd need to set conditions
539 * on the contents of the DN, e.g. "SELECT ... FROM
540 * ldap_entries AS attributeName WHERE attributeName.dn
541 * like '%attributeName=value%'"
543 backsql_strfcat( &bsi->bsi_flt_where, "l",
544 (ber_len_t)STRLENOF( "1=1" ), "1=1" );
545 bsi->bsi_status = LDAP_SUCCESS;
566 * Turn structuralObjectClass into objectClass
568 if ( ad == slap_schema.si_ad_objectClass
569 || ad == slap_schema.si_ad_structuralObjectClass )
572 * If the filter is LDAP_FILTER_PRESENT, then it's done;
573 * otherwise, let's see if we are lucky: filtering
574 * for "structural" objectclass or ancestor...
576 switch ( f->f_choice ) {
577 case LDAP_FILTER_EQUALITY:
579 ObjectClass *oc = oc_bvfind( &f->f_av_value );
582 Debug( LDAP_DEBUG_TRACE,
583 "backsql_process_filter(): "
584 "unknown objectClass \"%s\" "
586 f->f_av_value.bv_val, 0, 0 );
587 bsi->bsi_status = LDAP_OTHER;
593 * "structural" objectClass inheritance:
594 * - a search for "person" will also return
596 * - a search for "top" will return everything
598 if ( is_object_subclass( oc, bsi->bsi_oc->bom_oc ) ) {
599 static struct berval ldap_entry_objclasses = BER_BVC( "ldap_entry_objclasses" );
601 backsql_merge_from_tbls( bsi, &ldap_entry_objclasses );
603 backsql_strfcat( &bsi->bsi_flt_where, "lbl",
604 (ber_len_t)STRLENOF( "2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */ ),
605 "2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */,
606 &bsi->bsi_oc->bom_oc->soc_cname,
607 (ber_len_t)STRLENOF( /* (' */ "')" ),
609 bsi->bsi_status = LDAP_SUCCESS;
617 case LDAP_FILTER_PRESENT:
618 backsql_strfcat( &bsi->bsi_flt_where, "l",
619 (ber_len_t)STRLENOF( "3=3" ), "3=3" );
620 bsi->bsi_status = LDAP_SUCCESS;
624 /* FIXME: LDAP_FILTER_EXT? */
627 Debug( LDAP_DEBUG_TRACE,
628 "backsql_process_filter(): "
629 "illegal/unhandled filter "
630 "on objectClass attribute",
632 bsi->bsi_status = LDAP_OTHER;
637 } else if ( ad == slap_schema.si_ad_entryUUID ) {
639 #ifdef BACKSQL_ARBITRARY_KEY
640 struct berval keyval;
641 #else /* ! BACKSQL_ARBITRARY_KEY */
642 unsigned long keyval;
643 char keyvalbuf[] = "18446744073709551615";
644 #endif /* ! BACKSQL_ARBITRARY_KEY */
646 switch ( f->f_choice ) {
647 case LDAP_FILTER_EQUALITY:
648 backsql_entryUUID_decode( &f->f_av_value, &oc_id, &keyval );
650 if ( oc_id != bsi->bsi_oc->bom_id ) {
651 bsi->bsi_status = LDAP_SUCCESS;
656 #ifdef BACKSQL_ARBITRARY_KEY
657 backsql_strfcat( &bsi->bsi_flt_where, "bcblbc",
658 &bsi->bsi_oc->bom_keytbl, '.',
659 &bsi->bsi_oc->bom_keycol,
660 STRLENOF( " LIKE '" ), " LIKE '",
662 #else /* ! BACKSQL_ARBITRARY_KEY */
663 snprintf( keyvalbuf, sizeof( keyvalbuf ), "%lu", keyval );
664 backsql_strfcat( &bsi->bsi_flt_where, "bcbcs",
665 &bsi->bsi_oc->bom_keytbl, '.',
666 &bsi->bsi_oc->bom_keycol, '=', keyvalbuf );
667 #endif /* ! BACKSQL_ARBITRARY_KEY */
670 case LDAP_FILTER_PRESENT:
671 backsql_strfcat( &bsi->bsi_flt_where, "l",
672 (ber_len_t)STRLENOF( "4=4" ), "4=4" );
680 bsi->bsi_flags |= BSQL_SF_FILTER_ENTRYUUID;
684 #ifdef BACKSQL_SYNCPROV
685 } else if ( ad == slap_schema.si_ad_entryCSN ) {
687 * TODO: introduce appropriate entryCSN filtering
688 * to support syncrepl as producer...
690 if ( !bsi->bsi_op->o_sync ) {
691 /* unsupported at present... */
692 bsi->bsi_status = LDAP_OTHER;
697 bsi->bsi_flags |= ( BSQL_SF_FILTER_ENTRYCSN | BSQL_SF_RETURN_ENTRYUUID);
699 /* if doing a syncrepl, try to return as much as possible,
700 * and always match the filter */
701 backsql_strfcat( &bsi->bsi_flt_where, "l",
702 (ber_len_t)STRLENOF( "5=5" ), "5=5" );
704 /* save for later use in operational attributes */
705 /* FIXME: saves only the first occurrence, because
706 * the filter during updates is written as
707 * "(&(entryCSN<={contextCSN})(entryCSN>={oldContextCSN})({filter}))"
708 * so we want our fake entryCSN to match the greatest
711 if ( bsi->bsi_op->o_private == NULL ) {
712 bsi->bsi_op->o_private = &f->f_av_value;
714 bsi->bsi_status = LDAP_SUCCESS;
718 #endif /* BACKSQL_SYNCPROV */
720 } else if ( ad == slap_schema.si_ad_hasSubordinates || ad == NULL ) {
722 * FIXME: this is not robust; e.g. a filter
723 * '(!(hasSubordinates=TRUE))' fails because
724 * in SQL it would read 'NOT (1=1)' instead
726 * Note however that hasSubordinates is boolean,
727 * so a more appropriate filter would be
728 * '(hasSubordinates=FALSE)'
730 * A more robust search for hasSubordinates
731 * would * require joining the ldap_entries table
732 * selecting if there are descendants of the
735 backsql_strfcat( &bsi->bsi_flt_where, "l",
736 (ber_len_t)STRLENOF( "6=6" ), "6=6" );
737 if ( ad == slap_schema.si_ad_hasSubordinates ) {
739 * instruct candidate selection algorithm
740 * and attribute list to try to detect
741 * if an entry has subordinates
743 bsi->bsi_flags |= BSQL_SF_FILTER_HASSUBORDINATE;
747 * clear attributes to fetch, to require ALL
748 * and try extended match on all attributes
750 backsql_attrlist_add( bsi, NULL );
757 * attribute inheritance:
759 if ( backsql_supad2at( bsi->bsi_oc, ad, &vat ) ) {
760 bsi->bsi_status = LDAP_OTHER;
766 /* search anyway; other parts of the filter
768 backsql_strfcat( &bsi->bsi_flt_where, "l",
769 (ber_len_t)STRLENOF( "7=7" ), "7=7" );
770 bsi->bsi_status = LDAP_SUCCESS;
775 /* if required, open extra level of parens */
777 if ( vat[0]->bam_next || vat[1] ) {
778 backsql_strfcat( &bsi->bsi_flt_where, "c", '(' );
785 if ( backsql_process_filter_attr( bsi, f, vat[i] ) == -1 ) {
789 /* if more definitions of the same attr, apply */
790 if ( vat[i]->bam_next ) {
791 backsql_strfcat( &bsi->bsi_flt_where, "l",
792 STRLENOF( " OR " ), " OR " );
793 vat[i] = vat[i]->bam_next;
797 /* if more descendants of the same attr, apply */
800 backsql_strfcat( &bsi->bsi_flt_where, "l",
801 STRLENOF( " OR " ), " OR " );
805 /* if needed, close extra level of parens */
807 backsql_strfcat( &bsi->bsi_flt_where, "c", ')' );
817 Debug( LDAP_DEBUG_TRACE,
818 "<==backsql_process_filter() %s\n",
819 rc == 1 ? "succeeded" : "failed", 0, 0);
825 backsql_process_filter_eq( backsql_srch_info *bsi, backsql_at_map_rec *at,
826 int casefold, struct berval *filter_value )
829 * maybe we should check type of at->sel_expr here somehow,
830 * to know whether upper_func is applicable, but for now
831 * upper_func stuff is made for Oracle, where UPPER is
832 * safely applicable to NUMBER etc.
834 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
837 backsql_strfcat( &bsi->bsi_flt_where, "cbl",
840 (ber_len_t)STRLENOF( "='" ),
843 start = bsi->bsi_flt_where.bb_val.bv_len;
845 backsql_strfcat( &bsi->bsi_flt_where, "bl",
847 (ber_len_t)STRLENOF( /* (' */ "')" ),
850 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
853 backsql_strfcat( &bsi->bsi_flt_where, "cblbl",
856 (ber_len_t)STRLENOF( "='" ), "='",
858 (ber_len_t)STRLENOF( /* (' */ "')" ),
866 backsql_process_filter_like( backsql_srch_info *bsi, backsql_at_map_rec *at,
867 int casefold, struct berval *filter_value )
870 * maybe we should check type of at->sel_expr here somehow,
871 * to know whether upper_func is applicable, but for now
872 * upper_func stuff is made for Oracle, where UPPER is
873 * safely applicable to NUMBER etc.
875 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
878 backsql_strfcat( &bsi->bsi_flt_where, "cbl",
881 (ber_len_t)STRLENOF( " LIKE '%" ),
884 start = bsi->bsi_flt_where.bb_val.bv_len;
886 backsql_strfcat( &bsi->bsi_flt_where, "bl",
888 (ber_len_t)STRLENOF( /* (' */ "%')" ),
891 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
894 backsql_strfcat( &bsi->bsi_flt_where, "cblbl",
897 (ber_len_t)STRLENOF( " LIKE '%" ),
900 (ber_len_t)STRLENOF( /* (' */ "%')" ),
908 backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f, backsql_at_map_rec *at )
910 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
912 struct berval *filter_value = NULL;
913 MatchingRule *matching_rule = NULL;
914 struct berval ordering = BER_BVC("<=");
916 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter_attr(%s)\n",
917 at->bam_ad->ad_cname.bv_val, 0, 0 );
920 * need to add this attribute to list of attrs to load,
921 * so that we can do test_filter() later
923 backsql_attrlist_add( bsi, at->bam_ad );
925 backsql_merge_from_tbls( bsi, &at->bam_from_tbls );
927 if ( !BER_BVISNULL( &at->bam_join_where )
928 && strstr( bsi->bsi_join_where.bb_val.bv_val,
929 at->bam_join_where.bv_val ) == NULL )
931 backsql_strfcat( &bsi->bsi_join_where, "lb",
932 (ber_len_t)STRLENOF( " AND " ), " AND ",
933 &at->bam_join_where );
936 switch ( f->f_choice ) {
937 case LDAP_FILTER_EQUALITY:
938 filter_value = &f->f_av_value;
939 matching_rule = at->bam_ad->ad_type->sat_equality;
943 /* fail over into next case */
945 case LDAP_FILTER_EXT:
946 filter_value = &f->f_mra->ma_value;
947 matching_rule = f->f_mr_rule;
950 /* always uppercase strings by now */
951 #ifdef BACKSQL_UPPERCASE_FILTER
952 if ( SLAP_MR_ASSOCIATED( matching_rule,
953 bi->sql_caseIgnoreMatch ) )
954 #endif /* BACKSQL_UPPERCASE_FILTER */
959 if ( SLAP_MR_ASSOCIATED( matching_rule,
960 bi->sql_telephoneNumberMatch ) )
966 * to check for matching telephone numbers
967 * with intermized chars, e.g. val='1234'
970 * val LIKE '%1%2%3%4%'
973 bv.bv_len = 2 * filter_value->bv_len - 1;
974 bv.bv_val = ch_malloc( bv.bv_len + 1 );
976 bv.bv_val[ 0 ] = filter_value->bv_val[ 0 ];
977 for ( i = 1; i < filter_value->bv_len; i++ ) {
978 bv.bv_val[ 2 * i - 1 ] = '%';
979 bv.bv_val[ 2 * i ] = filter_value->bv_val[ i ];
981 bv.bv_val[ 2 * i - 1 ] = '\0';
983 (void)backsql_process_filter_like( bsi, at, casefold, &bv );
984 ch_free( bv.bv_val );
989 /* NOTE: this is required by objectClass inheritance
990 * and auxiliary objectClass use in filters for slightly
991 * more efficient candidate selection. */
992 /* FIXME: a bit too many specializations to deal with
993 * very specific cases... */
994 if ( at->bam_ad == slap_schema.si_ad_objectClass
995 || at->bam_ad == slap_schema.si_ad_structuralObjectClass )
997 backsql_strfcat( &bsi->bsi_flt_where, "lbl",
998 (ber_len_t)STRLENOF( "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */ ),
999 "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */,
1001 (ber_len_t)STRLENOF( /* (' */ "')" ),
1007 * maybe we should check type of at->sel_expr here somehow,
1008 * to know whether upper_func is applicable, but for now
1009 * upper_func stuff is made for Oracle, where UPPER is
1010 * safely applicable to NUMBER etc.
1012 (void)backsql_process_filter_eq( bsi, at, casefold, filter_value );
1015 case LDAP_FILTER_GE:
1016 ordering.bv_val = ">=";
1018 /* fall thru to next case */
1020 case LDAP_FILTER_LE:
1021 filter_value = &f->f_av_value;
1023 /* always uppercase strings by now */
1024 #ifdef BACKSQL_UPPERCASE_FILTER
1025 if ( at->bam_ad->ad_type->sat_ordering &&
1026 SLAP_MR_ASSOCIATED( at->bam_ad->ad_type->sat_ordering,
1027 bi->sql_caseIgnoreMatch ) )
1028 #endif /* BACKSQL_UPPERCASE_FILTER */
1034 * FIXME: should we uppercase the operands?
1036 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1039 backsql_strfcat( &bsi->bsi_flt_where, "cbbc",
1041 &at->bam_sel_expr_u,
1045 start = bsi->bsi_flt_where.bb_val.bv_len;
1047 backsql_strfcat( &bsi->bsi_flt_where, "bl",
1049 (ber_len_t)STRLENOF( /* (' */ "')" ),
1052 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1055 backsql_strfcat( &bsi->bsi_flt_where, "cbbcbl",
1061 (ber_len_t)STRLENOF( /* (' */ "')" ),
1066 case LDAP_FILTER_PRESENT:
1067 backsql_strfcat( &bsi->bsi_flt_where, "lbl",
1068 (ber_len_t)STRLENOF( "NOT (" /* ) */),
1071 (ber_len_t)STRLENOF( /* ( */ " IS NULL)" ),
1072 /* ( */ " IS NULL)" );
1075 case LDAP_FILTER_SUBSTRINGS:
1076 backsql_process_sub_filter( bsi, f, at );
1079 case LDAP_FILTER_APPROX:
1080 /* we do our best */
1083 * maybe we should check type of at->sel_expr here somehow,
1084 * to know whether upper_func is applicable, but for now
1085 * upper_func stuff is made for Oracle, where UPPER is
1086 * safely applicable to NUMBER etc.
1088 (void)backsql_process_filter_like( bsi, at, 1, &f->f_av_value );
1092 /* unhandled filter type; should not happen */
1094 backsql_strfcat( &bsi->bsi_flt_where, "l",
1095 (ber_len_t)STRLENOF( "8=8" ), "8=8" );
1100 Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter_attr(%s)\n",
1101 at->bam_ad->ad_cname.bv_val, 0, 0 );
1107 backsql_srch_query( backsql_srch_info *bsi, struct berval *query )
1109 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1113 BER_BVZERO( query );
1115 bsi->bsi_use_subtree_shortcut = 0;
1117 Debug( LDAP_DEBUG_TRACE, "==>backsql_srch_query()\n", 0, 0, 0 );
1118 BER_BVZERO( &bsi->bsi_sel.bb_val );
1119 BER_BVZERO( &bsi->bsi_sel.bb_val );
1120 bsi->bsi_sel.bb_len = 0;
1121 BER_BVZERO( &bsi->bsi_from.bb_val );
1122 bsi->bsi_from.bb_len = 0;
1123 BER_BVZERO( &bsi->bsi_join_where.bb_val );
1124 bsi->bsi_join_where.bb_len = 0;
1125 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1126 bsi->bsi_flt_where.bb_len = 0;
1128 backsql_strfcat( &bsi->bsi_sel, "lbcbc",
1129 (ber_len_t)STRLENOF( "SELECT DISTINCT ldap_entries.id," ),
1130 "SELECT DISTINCT ldap_entries.id,",
1131 &bsi->bsi_oc->bom_keytbl,
1133 &bsi->bsi_oc->bom_keycol,
1136 if ( !BER_BVISNULL( &bi->sql_strcast_func ) ) {
1137 backsql_strfcat( &bsi->bsi_sel, "blbl",
1138 &bi->sql_strcast_func,
1139 (ber_len_t)STRLENOF( "('" /* ') */ ),
1141 &bsi->bsi_oc->bom_oc->soc_cname,
1142 (ber_len_t)STRLENOF( /* (' */ "')" ),
1145 backsql_strfcat( &bsi->bsi_sel, "cbc",
1147 &bsi->bsi_oc->bom_oc->soc_cname,
1150 #ifdef BACKSQL_ALIASING_QUOTE
1151 backsql_strfcat( &bsi->bsi_sel, "lclcl",
1152 (ber_len_t)STRLENOF( " " BACKSQL_ALIASING ),
1153 " " BACKSQL_ALIASING,
1154 BACKSQL_ALIASING_QUOTE,
1155 (ber_len_t)STRLENOF( "objectClass" ),
1157 BACKSQL_ALIASING_QUOTE,
1158 (ber_len_t)STRLENOF( ",ldap_entries.dn " BACKSQL_ALIASING "dn" ),
1159 ",ldap_entries.dn " BACKSQL_ALIASING "dn" );
1160 #else /* ! BACKSQL_ALIASING_QUOTE */
1161 backsql_strfcat( &bsi->bsi_sel, "l",
1162 (ber_len_t)STRLENOF( " " BACKSQL_ALIASING "objectClass,ldap_entries.dn " BACKSQL_ALIASING "dn" ),
1163 " " BACKSQL_ALIASING "objectClass,ldap_entries.dn " BACKSQL_ALIASING "dn" );
1164 #endif /* ! BACKSQL_ALIASING_QUOTE */
1166 backsql_strfcat( &bsi->bsi_from, "lb",
1167 (ber_len_t)STRLENOF( " FROM ldap_entries," ),
1168 " FROM ldap_entries,",
1169 &bsi->bsi_oc->bom_keytbl );
1171 backsql_strfcat( &bsi->bsi_join_where, "lbcbl",
1172 (ber_len_t)STRLENOF( " WHERE " ), " WHERE ",
1173 &bsi->bsi_oc->bom_keytbl,
1175 &bsi->bsi_oc->bom_keycol,
1176 (ber_len_t)STRLENOF( "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ),
1177 "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " );
1179 switch ( bsi->bsi_scope ) {
1180 case LDAP_SCOPE_BASE:
1181 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1182 backsql_strfcat( &bsi->bsi_join_where, "bl",
1183 &bi->sql_upper_func,
1184 (ber_len_t)STRLENOF( "(ldap_entries.dn)=?" ),
1185 "(ldap_entries.dn)=?" );
1187 backsql_strfcat( &bsi->bsi_join_where, "l",
1188 (ber_len_t)STRLENOF( "ldap_entries.dn=?" ),
1189 "ldap_entries.dn=?" );
1193 case BACKSQL_SCOPE_BASE_LIKE:
1194 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1195 backsql_strfcat( &bsi->bsi_join_where, "bl",
1196 &bi->sql_upper_func,
1197 (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1198 "(ldap_entries.dn) LIKE ?" );
1200 backsql_strfcat( &bsi->bsi_join_where, "l",
1201 (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1202 "ldap_entries.dn LIKE ?" );
1206 case LDAP_SCOPE_ONELEVEL:
1207 backsql_strfcat( &bsi->bsi_join_where, "l",
1208 (ber_len_t)STRLENOF( "ldap_entries.parent=?" ),
1209 "ldap_entries.parent=?" );
1212 #ifdef LDAP_SCOPE_SUBORDINATE
1213 case LDAP_SCOPE_SUBORDINATE:
1214 #endif /* LDAP_SCOPE_SUBORDINATE */
1215 case LDAP_SCOPE_SUBTREE:
1216 if ( BACKSQL_USE_SUBTREE_SHORTCUT( bi ) ) {
1218 BackendDB *bd = bsi->bsi_op->o_bd;
1220 assert( bd->be_nsuffix );
1222 for ( i = 0; !BER_BVISNULL( &bd->be_nsuffix[ i ] ); i++ )
1224 if ( dn_match( &bd->be_nsuffix[ i ],
1225 bsi->bsi_base_ndn ) )
1227 /* pass this to the candidate selection
1228 * routine so that the DN is not bound
1229 * to the select statement */
1230 bsi->bsi_use_subtree_shortcut = 1;
1236 if ( bsi->bsi_use_subtree_shortcut ) {
1237 /* Skip the base DN filter, as every entry will match it */
1238 backsql_strfcat( &bsi->bsi_join_where, "l",
1239 (ber_len_t)STRLENOF( "9=9"), "9=9");
1241 } else if ( !BER_BVISNULL( &bi->sql_subtree_cond ) ) {
1242 backsql_strfcat( &bsi->bsi_join_where, "b", &bi->sql_subtree_cond );
1244 } else if ( BACKSQL_CANUPPERCASE( bi ) ) {
1245 backsql_strfcat( &bsi->bsi_join_where, "bl",
1246 &bi->sql_upper_func,
1247 (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1248 "(ldap_entries.dn) LIKE ?" );
1251 backsql_strfcat( &bsi->bsi_join_where, "l",
1252 (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1253 "ldap_entries.dn LIKE ?" );
1262 rc = backsql_process_filter( bsi, bsi->bsi_filter );
1264 struct berbuf bb = BB_NULL;
1266 backsql_strfcat( &bb, "bbblb",
1267 &bsi->bsi_sel.bb_val,
1268 &bsi->bsi_from.bb_val,
1269 &bsi->bsi_join_where.bb_val,
1270 (ber_len_t)STRLENOF( " AND " ), " AND ",
1271 &bsi->bsi_flt_where.bb_val );
1275 } else if ( rc < 0 ) {
1277 * Indicates that there's no possible way the filter matches
1278 * anything. No need to issue the query
1280 free( query->bv_val );
1281 BER_BVZERO( query );
1284 free( bsi->bsi_sel.bb_val.bv_val );
1285 BER_BVZERO( &bsi->bsi_sel.bb_val );
1286 bsi->bsi_sel.bb_len = 0;
1287 free( bsi->bsi_from.bb_val.bv_val );
1288 BER_BVZERO( &bsi->bsi_from.bb_val );
1289 bsi->bsi_from.bb_len = 0;
1290 free( bsi->bsi_join_where.bb_val.bv_val );
1291 BER_BVZERO( &bsi->bsi_join_where.bb_val );
1292 bsi->bsi_join_where.bb_len = 0;
1293 free( bsi->bsi_flt_where.bb_val.bv_val );
1294 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1295 bsi->bsi_flt_where.bb_len = 0;
1297 Debug( LDAP_DEBUG_TRACE, "<==backsql_srch_query() returns %s\n",
1298 query->bv_val ? query->bv_val : "NULL", 0, 0 );
1300 return ( rc <= 0 ? 1 : 0 );
1304 backsql_oc_get_candidates( void *v_oc, void *v_bsi )
1306 backsql_oc_map_rec *oc = v_oc;
1307 backsql_srch_info *bsi = v_bsi;
1308 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1309 struct berval query;
1310 SQLHSTMT sth = SQL_NULL_HSTMT;
1313 BACKSQL_ROW_NTS row;
1316 int n_candidates = bsi->bsi_n_candidates;
1319 * + 1 because we need room for '%';
1320 * + 1 because we need room for ',' for LDAP_SCOPE_SUBORDINATE;
1321 * this makes a subtree
1322 * search for a DN BACKSQL_MAX_DN_LEN long legal
1323 * if it returns that DN only
1325 char tmp_base_ndn[ BACKSQL_MAX_DN_LEN + 1 + 1 ];
1327 bsi->bsi_status = LDAP_SUCCESS;
1329 Debug( LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc=\"%s\"\n",
1330 BACKSQL_OC_NAME( oc ), 0, 0 );
1332 if ( bsi->bsi_n_candidates == -1 ) {
1333 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1334 "unchecked limit has been overcome\n", 0, 0, 0 );
1335 /* should never get here */
1337 bsi->bsi_status = LDAP_ADMINLIMIT_EXCEEDED;
1338 return BACKSQL_AVL_STOP;
1342 res = backsql_srch_query( bsi, &query );
1344 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1345 "error while constructing query for objectclass \"%s\"\n",
1346 oc->bom_oc->soc_cname.bv_val, 0, 0 );
1348 * FIXME: need to separate errors from legally
1349 * impossible filters
1351 switch ( bsi->bsi_status ) {
1353 case LDAP_UNDEFINED_TYPE:
1354 case LDAP_NO_SUCH_OBJECT:
1355 /* we are conservative... */
1357 bsi->bsi_status = LDAP_SUCCESS;
1359 return BACKSQL_AVL_CONTINUE;
1361 case LDAP_ADMINLIMIT_EXCEEDED:
1363 /* don't try any more */
1364 return BACKSQL_AVL_STOP;
1368 if ( BER_BVISNULL( &query ) ) {
1369 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1370 "could not construct query for objectclass \"%s\"\n",
1371 oc->bom_oc->soc_cname.bv_val, 0, 0 );
1372 bsi->bsi_status = LDAP_SUCCESS;
1373 return BACKSQL_AVL_CONTINUE;
1376 Debug( LDAP_DEBUG_TRACE, "Constructed query: %s\n",
1377 query.bv_val, 0, 0 );
1379 rc = backsql_Prepare( bsi->bsi_dbh, &sth, query.bv_val, 0 );
1380 free( query.bv_val );
1381 BER_BVZERO( &query );
1382 if ( rc != SQL_SUCCESS ) {
1383 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1384 "error preparing query\n", 0, 0, 0 );
1385 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1386 bsi->bsi_status = LDAP_OTHER;
1387 return BACKSQL_AVL_CONTINUE;
1390 Debug( LDAP_DEBUG_TRACE, "id: '%ld'\n", bsi->bsi_oc->bom_id, 0, 0 );
1392 rc = backsql_BindParamInt( sth, 1, SQL_PARAM_INPUT,
1393 &bsi->bsi_oc->bom_id );
1394 if ( rc != SQL_SUCCESS ) {
1395 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1396 "error binding objectclass id parameter\n", 0, 0, 0 );
1397 bsi->bsi_status = LDAP_OTHER;
1398 return BACKSQL_AVL_CONTINUE;
1401 switch ( bsi->bsi_scope ) {
1402 case LDAP_SCOPE_BASE:
1403 case BACKSQL_SCOPE_BASE_LIKE:
1405 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1406 * however this should be handled earlier
1408 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1409 bsi->bsi_status = LDAP_OTHER;
1410 return BACKSQL_AVL_CONTINUE;
1413 AC_MEMCPY( tmp_base_ndn, bsi->bsi_base_ndn->bv_val,
1414 bsi->bsi_base_ndn->bv_len + 1 );
1416 /* uppercase DN only if the stored DN can be uppercased
1418 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1419 ldap_pvt_str2upper( tmp_base_ndn );
1422 Debug( LDAP_DEBUG_TRACE, "(base)dn: \"%s\"\n",
1423 tmp_base_ndn, 0, 0 );
1425 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1426 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1427 if ( rc != SQL_SUCCESS ) {
1428 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1429 "error binding base_ndn parameter\n", 0, 0, 0 );
1430 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh,
1432 bsi->bsi_status = LDAP_OTHER;
1433 return BACKSQL_AVL_CONTINUE;
1437 #ifdef LDAP_SCOPE_SUBORDINATE
1438 case LDAP_SCOPE_SUBORDINATE:
1439 #endif /* LDAP_SCOPE_SUBORDINATE */
1440 case LDAP_SCOPE_SUBTREE:
1442 /* if short-cutting the search base,
1443 * don't bind any parameter */
1444 if ( bsi->bsi_use_subtree_shortcut ) {
1449 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1450 * however this should be handled earlier
1452 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1453 bsi->bsi_status = LDAP_OTHER;
1454 return BACKSQL_AVL_CONTINUE;
1458 * Sets the parameters for the SQL built earlier
1459 * NOTE that all the databases could actually use
1460 * the TimesTen version, which would be cleaner
1461 * and would also eliminate the need for the
1462 * subtree_cond line in the configuration file.
1463 * For now, I'm leaving it the way it is,
1464 * so non-TimesTen databases use the original code.
1465 * But at some point this should get cleaned up.
1467 * If "dn" is being used, do a suffix search.
1468 * If "dn_ru" is being used, do a prefix search.
1470 if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
1471 tmp_base_ndn[ 0 ] = '\0';
1473 for ( i = 0, j = bsi->bsi_base_ndn->bv_len - 1;
1475 tmp_base_ndn[ i ] = bsi->bsi_base_ndn->bv_val[ j ];
1478 #ifdef LDAP_SCOPE_SUBORDINATE
1479 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1480 tmp_base_ndn[ i++ ] = ',';
1482 #endif /* LDAP_SCOPE_SUBORDINATE */
1484 tmp_base_ndn[ i ] = '%';
1485 tmp_base_ndn[ i + 1 ] = '\0';
1490 tmp_base_ndn[ i++ ] = '%';
1492 #ifdef LDAP_SCOPE_SUBORDINATE
1493 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1494 tmp_base_ndn[ i++ ] = ',';
1496 #endif /* LDAP_SCOPE_SUBORDINATE */
1498 AC_MEMCPY( &tmp_base_ndn[ i ], bsi->bsi_base_ndn->bv_val,
1499 bsi->bsi_base_ndn->bv_len + 1 );
1502 /* uppercase DN only if the stored DN can be uppercased
1504 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1505 ldap_pvt_str2upper( tmp_base_ndn );
1508 #ifdef LDAP_SCOPE_SUBORDINATE
1509 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1510 Debug( LDAP_DEBUG_TRACE, "(children)dn: \"%s\"\n",
1511 tmp_base_ndn, 0, 0 );
1513 #endif /* LDAP_SCOPE_SUBORDINATE */
1515 Debug( LDAP_DEBUG_TRACE, "(sub)dn: \"%s\"\n",
1516 tmp_base_ndn, 0, 0 );
1519 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1520 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1521 if ( rc != SQL_SUCCESS ) {
1522 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1523 "error binding base_ndn parameter (2)\n",
1525 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh,
1527 bsi->bsi_status = LDAP_OTHER;
1528 return BACKSQL_AVL_CONTINUE;
1533 case LDAP_SCOPE_ONELEVEL:
1534 assert( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) );
1536 #ifdef BACKSQL_ARBITRARY_KEY
1537 Debug( LDAP_DEBUG_TRACE, "(one)id: \"%s\"\n",
1538 bsi->bsi_base_id.eid_id.bv_val, 0, 0 );
1539 #else /* ! BACKSQL_ARBITRARY_KEY */
1540 Debug( LDAP_DEBUG_TRACE, "(one)id: '%lu'\n",
1541 bsi->bsi_base_id.eid_id, 0, 0 );
1542 #endif /* ! BACKSQL_ARBITRARY_KEY */
1543 rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT,
1544 &bsi->bsi_base_id.eid_id );
1545 if ( rc != SQL_SUCCESS ) {
1546 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1547 "error binding base id parameter\n", 0, 0, 0 );
1548 bsi->bsi_status = LDAP_OTHER;
1549 return BACKSQL_AVL_CONTINUE;
1554 rc = SQLExecute( sth );
1555 if ( !BACKSQL_SUCCESS( rc ) ) {
1556 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1557 "error executing query\n", 0, 0, 0 );
1558 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1559 SQLFreeStmt( sth, SQL_DROP );
1560 bsi->bsi_status = LDAP_OTHER;
1561 return BACKSQL_AVL_CONTINUE;
1564 backsql_BindRowAsStrings( sth, &row );
1565 rc = SQLFetch( sth );
1566 for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
1567 struct berval dn, pdn, ndn;
1568 backsql_entryID *c_id = NULL;
1571 ber_str2bv( row.cols[ 3 ], 0, 0, &dn );
1573 if ( backsql_api_odbc2dn( bsi->bsi_op, bsi->bsi_rs, &dn ) ) {
1577 ret = dnPrettyNormal( NULL, &dn, &pdn, &ndn, NULL );
1578 if ( dn.bv_val != row.cols[ 3 ] ) {
1582 if ( ret != LDAP_SUCCESS ) {
1586 if ( bi->sql_baseObject && dn_match( &ndn, &bi->sql_baseObject->e_nname ) ) {
1592 c_id = (backsql_entryID *)ch_calloc( 1,
1593 sizeof( backsql_entryID ) );
1594 #ifdef BACKSQL_ARBITRARY_KEY
1595 ber_str2bv( row.cols[ 0 ], 0, 1, &c_id->eid_id );
1596 ber_str2bv( row.cols[ 1 ], 0, 1, &c_id->eid_keyval );
1597 #else /* ! BACKSQL_ARBITRARY_KEY */
1598 c_id->eid_id = strtol( row.cols[ 0 ], NULL, 0 );
1599 c_id->eid_keyval = strtol( row.cols[ 1 ], NULL, 0 );
1600 #endif /* ! BACKSQL_ARBITRARY_KEY */
1601 c_id->eid_oc_id = bsi->bsi_oc->bom_id;
1604 c_id->eid_ndn = ndn;
1606 /* append at end of list ... */
1607 c_id->eid_next = NULL;
1608 *bsi->bsi_id_listtail = c_id;
1609 bsi->bsi_id_listtail = &c_id->eid_next;
1611 #ifdef BACKSQL_ARBITRARY_KEY
1612 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1613 "added entry id=%s, keyval=%s dn=\"%s\"\n",
1614 c_id->eid_id.bv_val, c_id->eid_keyval.bv_val,
1616 #else /* ! BACKSQL_ARBITRARY_KEY */
1617 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1618 "added entry id=%ld, keyval=%ld dn=\"%s\"\n",
1619 c_id->eid_id, c_id->eid_keyval, row.cols[ 3 ] );
1620 #endif /* ! BACKSQL_ARBITRARY_KEY */
1622 /* count candidates, for unchecked limit */
1623 bsi->bsi_n_candidates--;
1624 if ( bsi->bsi_n_candidates == -1 ) {
1628 backsql_FreeRow( &row );
1629 SQLFreeStmt( sth, SQL_DROP );
1631 Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates(): %d\n",
1632 n_candidates - bsi->bsi_n_candidates, 0, 0 );
1634 return ( bsi->bsi_n_candidates == -1 ? BACKSQL_AVL_STOP : BACKSQL_AVL_CONTINUE );
1638 backsql_search( Operation *op, SlapReply *rs )
1640 backsql_info *bi = (backsql_info *)op->o_bd->be_private;
1641 SQLHDBC dbh = SQL_NULL_HDBC;
1643 Entry user_entry = { 0 };
1645 time_t stoptime = 0;
1646 backsql_srch_info bsi;
1647 backsql_entryID *eid = NULL;
1648 struct berval nbase = BER_BVNULL,
1649 realndn = BER_BVNULL;
1651 manageDSAit = get_manageDSAit( op );
1653 Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): "
1654 "base=\"%s\", filter=\"%s\", scope=%d,",
1655 op->o_req_ndn.bv_val,
1656 op->ors_filterstr.bv_val ? op->ors_filterstr.bv_val : "(no filter)",
1658 Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, "
1659 "attributes to load: %s\n",
1662 op->ors_attrs == NULL ? "all" : "custom list" );
1664 if ( op->o_req_ndn.bv_len > BACKSQL_MAX_DN_LEN ) {
1665 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1666 "search base length (%ld) exceeds max length (%d)\n",
1667 op->o_req_ndn.bv_len, BACKSQL_MAX_DN_LEN, 0 );
1669 * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate
1670 * since it is impossible that such a long DN exists
1673 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1674 send_ldap_result( op, rs );
1678 sres = backsql_get_db_conn( op, &dbh );
1679 if ( sres != LDAP_SUCCESS ) {
1680 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1681 "could not get connection handle - exiting\n",
1684 rs->sr_text = sres == LDAP_OTHER ? "SQL-backend error" : NULL;
1685 send_ldap_result( op, rs );
1689 /* compute it anyway; root does not use it */
1690 stoptime = op->o_time + op->ors_tlimit;
1692 realndn = op->o_req_ndn;
1693 if ( backsql_api_dn2odbc( op, rs, &realndn ) ) {
1694 Debug( LDAP_DEBUG_TRACE, " backsql_search(\"%s\"): "
1695 "backsql_api_dn2odbc(\"%s\") failed\n",
1696 op->o_req_ndn.bv_val, realndn.bv_val, 0 );
1697 rs->sr_err = LDAP_OTHER;
1698 rs->sr_text = "SQL-backend error";
1699 send_ldap_result( op, rs );
1704 rs->sr_err = backsql_init_search( &bsi, &realndn,
1706 op->ors_slimit, op->ors_tlimit,
1707 stoptime, op->ors_filter,
1708 dbh, op, rs, op->ors_attrs,
1709 BACKSQL_ISF_GET_ID );
1710 if ( rs->sr_err != LDAP_SUCCESS ) {
1711 send_ldap_result( op, rs );
1715 bsi.bsi_n_candidates =
1716 ( op->ors_limit == NULL /* isroot == TRUE */ ? -2 :
1717 ( op->ors_limit->lms_s_unchecked == -1 ? -2 :
1718 ( op->ors_limit->lms_s_unchecked ) ) );
1720 switch ( bsi.bsi_scope ) {
1721 case LDAP_SCOPE_BASE:
1722 case BACKSQL_SCOPE_BASE_LIKE:
1724 * probably already found...
1726 bsi.bsi_id_list = &bsi.bsi_base_id;
1727 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
1730 case LDAP_SCOPE_SUBTREE:
1732 * if baseObject is defined, and if it is the root
1733 * of the search, add it to the candidate list
1735 if ( bi->sql_baseObject && BACKSQL_IS_BASEOBJECT_ID( &bsi.bsi_base_id.eid_id ) )
1737 bsi.bsi_id_list = &bsi.bsi_base_id;
1738 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
1745 * for each objectclass we try to construct query which gets IDs
1746 * of entries matching LDAP query filter and scope (or at least
1747 * candidates), and get the IDs
1749 avl_apply( bi->sql_oc_by_oc, backsql_oc_get_candidates,
1750 &bsi, BACKSQL_AVL_STOP, AVL_INORDER );
1753 if ( op->ors_limit != NULL /* isroot == FALSE */
1754 && op->ors_limit->lms_s_unchecked != -1
1755 && bsi.bsi_n_candidates == -1 )
1757 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1758 send_ldap_result( op, rs );
1763 * now we load candidate entries (only those attributes
1764 * mentioned in attrs and filter), test it against full filter
1765 * and then send to client; don't free entry_id if baseObject...
1767 for ( eid = bsi.bsi_id_list;
1769 eid = backsql_free_entryID( eid, eid == &bsi.bsi_base_id ? 0 : 1 ) )
1772 Attribute *a_hasSubordinate = NULL,
1773 *a_entryUUID = NULL,
1778 /* check for abandon */
1779 if ( op->o_abandon ) {
1783 /* check time limit */
1784 if ( op->ors_tlimit != SLAP_NO_LIMIT
1785 && slap_get_time() > stoptime )
1787 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
1788 rs->sr_ctrls = NULL;
1789 rs->sr_ref = rs->sr_v2ref;
1790 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
1792 send_ldap_result( op, rs );
1796 #ifdef BACKSQL_ARBITRARY_KEY
1797 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
1798 "for entry id=%s, oc_id=%ld, keyval=%s\n",
1799 eid->eid_id.bv_val, eid->eid_oc_id,
1800 eid->eid_keyval.bv_val );
1801 #else /* ! BACKSQL_ARBITRARY_KEY */
1802 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
1803 "for entry id=%ld, oc_id=%ld, keyval=%ld\n",
1804 eid->eid_id, eid->eid_oc_id, eid->eid_keyval );
1805 #endif /* ! BACKSQL_ARBITRARY_KEY */
1808 switch ( op->ors_scope ) {
1809 case LDAP_SCOPE_BASE:
1810 case BACKSQL_SCOPE_BASE_LIKE:
1811 if ( !dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
1816 case LDAP_SCOPE_ONE:
1818 struct berval rdn = eid->eid_ndn;
1820 rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
1821 if ( !dnIsOneLevelRDN( &rdn ) ) {
1827 #ifdef LDAP_SCOPE_SUBORDINATE
1828 case LDAP_SCOPE_SUBORDINATE:
1829 /* discard the baseObject entry */
1830 if ( dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
1834 #endif /* LDAP_SCOPE_SUBORDINATE */
1836 case LDAP_SCOPE_SUBTREE:
1837 /* FIXME: this should never fail... */
1838 if ( !dnIsSuffix( &eid->eid_ndn, &op->o_req_ndn ) ) {
1844 /* don't recollect baseObject ... */
1845 if ( BACKSQL_IS_BASEOBJECT_ID( &eid->eid_id ) ) {
1846 e = bi->sql_baseObject;
1849 bsi.bsi_e = &user_entry;
1850 rc = backsql_id2entry( &bsi, eid );
1851 if ( rc != LDAP_SUCCESS ) {
1852 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1853 "error %d in backsql_id2entry() "
1854 "- skipping\n", rc, 0, 0 );
1861 if ( !manageDSAit &&
1862 op->ors_scope != LDAP_SCOPE_BASE &&
1863 op->ors_scope != BACKSQL_SCOPE_BASE_LIKE &&
1864 is_entry_referral( e ) )
1868 refs = get_entry_referrals( op, e );
1870 backsql_srch_info bsi2 = { 0 };
1871 Entry user_entry2 = { 0 };
1873 /* retry with the full entry... */
1874 (void)backsql_init_search( &bsi2,
1877 SLAP_NO_LIMIT, SLAP_NO_LIMIT,
1879 dbh, op, rs, NULL, 0 );
1880 bsi2.bsi_e = &user_entry2;
1881 rc = backsql_id2entry( &bsi2, eid );
1882 if ( rc == LDAP_SUCCESS ) {
1883 if ( is_entry_referral( &user_entry2 ) )
1885 refs = get_entry_referrals( op,
1887 } /* else: FIXME: inconsistency! */
1888 entry_clean( &user_entry2 );
1893 rs->sr_ref = referral_rewrite( refs,
1897 ber_bvarray_free( refs );
1900 if ( !rs->sr_ref ) {
1901 rs->sr_text = "bad_referral object";
1905 rs->sr_err = LDAP_REFERRAL;
1906 rs->sr_matched = user_entry.e_name.bv_val;
1907 send_search_reference( op, rs );
1909 ber_bvarray_free( rs->sr_ref );
1911 rs->sr_matched = NULL;
1912 rs->sr_entry = NULL;
1918 * We use this flag since we need to parse the filter
1919 * anyway; we should have used the frontend API function
1920 * filter_has_subordinates()
1922 if ( bsi.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) {
1923 rc = backsql_has_children( bi, dbh, &e->e_nname );
1926 case LDAP_COMPARE_TRUE:
1927 case LDAP_COMPARE_FALSE:
1928 a_hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE );
1929 if ( a_hasSubordinate != NULL ) {
1930 for ( ap = &user_entry.e_attrs;
1932 ap = &(*ap)->a_next );
1934 *ap = a_hasSubordinate;
1940 Debug(LDAP_DEBUG_TRACE,
1941 "backsql_search(): "
1942 "has_children failed( %d)\n",
1949 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYUUID ) {
1950 a_entryUUID = backsql_operational_entryUUID( bi, eid );
1951 if ( a_entryUUID != NULL ) {
1953 ap = &user_entry.e_attrs;
1956 for ( ; *ap; ap = &(*ap)->a_next );
1962 #ifdef BACKSQL_SYNCPROV
1963 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYCSN ) {
1964 a_entryCSN = backsql_operational_entryCSN( op );
1965 if ( a_entryCSN != NULL ) {
1967 ap = &user_entry.e_attrs;
1970 for ( ; *ap; ap = &(*ap)->a_next );
1975 #endif /* BACKSQL_SYNCPROV */
1977 if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE )
1979 rs->sr_attrs = op->ors_attrs;
1980 rs->sr_operational_attrs = NULL;
1982 if ( e == &user_entry ) {
1983 rs->sr_flags = REP_ENTRY_MODIFIABLE;
1985 sres = send_search_entry( op, rs );
1986 rs->sr_entry = NULL;
1987 rs->sr_attrs = NULL;
1988 rs->sr_operational_attrs = NULL;
1996 * FIXME: send_search_entry failed;
2000 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2001 "connection lost\n", 0, 0, 0 );
2007 entry_clean( &user_entry );
2010 if ( op->ors_slimit != SLAP_NO_LIMIT
2011 && rs->sr_nentries >= op->ors_slimit )
2013 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
2014 send_ldap_result( op, rs );
2020 /* in case we got here accidentally */
2021 entry_clean( &user_entry );
2023 if ( rs->sr_nentries > 0 ) {
2024 rs->sr_ref = rs->sr_v2ref;
2025 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
2029 rs->sr_err = bsi.bsi_status;
2031 send_ldap_result( op, rs );
2033 if ( rs->sr_v2ref ) {
2034 ber_bvarray_free( rs->sr_v2ref );
2035 rs->sr_v2ref = NULL;
2038 #ifdef BACKSQL_SYNCPROV
2040 Operation op2 = *op;
2041 SlapReply rs2 = { 0 };
2043 slap_callback cb = { 0 };
2045 op2.o_tag = LDAP_REQ_ADD;
2046 op2.o_bd = select_backend( &op->o_bd->be_nsuffix[0], 0, 0 );
2048 op2.o_callback = &cb;
2050 e.e_name = op->o_bd->be_suffix[0];
2051 e.e_nname = op->o_bd->be_nsuffix[0];
2053 cb.sc_response = slap_null_cb;
2055 op2.o_bd->be_add( &op2, &rs2 );
2057 #endif /* BACKSQL_SYNCPROV */
2060 if ( !BER_BVISNULL( &realndn ) && realndn.bv_val != op->o_req_ndn.bv_val ) {
2061 ch_free( realndn.bv_val );
2064 if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
2065 (void)backsql_free_entryID( &bsi.bsi_base_id, 0 );
2068 if ( bsi.bsi_attrs ) {
2069 ch_free( bsi.bsi_attrs );
2072 if ( !BER_BVISNULL( &nbase )
2073 && nbase.bv_val != op->o_req_ndn.bv_val )
2075 ch_free( nbase.bv_val );
2078 /* restore scope ... FIXME: this should be done before ANY
2079 * frontend call that uses op */
2080 if ( op->ors_scope == BACKSQL_SCOPE_BASE_LIKE ) {
2081 op->ors_scope = LDAP_SCOPE_BASE;
2084 Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n", 0, 0, 0 );
2088 /* return LDAP_SUCCESS IFF we can retrieve the specified entry.
2095 AttributeDescription *at,
2099 backsql_srch_info bsi;
2100 SQLHDBC dbh = SQL_NULL_HDBC;
2102 SlapReply rs = { 0 };
2103 AttributeName anlist[ 2 ];
2105 rc = backsql_get_db_conn( op, &dbh );
2111 anlist[ 0 ].an_name = at->ad_cname;
2112 anlist[ 0 ].an_desc = at;
2113 BER_BVZERO( &anlist[ 1 ].an_name );
2116 rc = backsql_init_search( &bsi,
2119 SLAP_NO_LIMIT, SLAP_NO_LIMIT,
2121 dbh, op, &rs, at ? anlist : NULL,
2122 BACKSQL_ISF_GET_ID );
2123 if ( rc != LDAP_SUCCESS ) {
2127 bsi.bsi_e = ch_malloc( sizeof( Entry ) );
2128 rc = backsql_id2entry( &bsi, &bsi.bsi_base_id );
2130 if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
2131 (void)backsql_free_entryID( &bsi.bsi_base_id, 0 );
2134 if ( rc == LDAP_SUCCESS ) {
2136 #if 0 /* not supported at present */
2137 /* find attribute values */
2138 if ( is_entry_alias( bsi.bsi_e ) ) {
2139 Debug( LDAP_DEBUG_ACL,
2140 "<= backsql_entry_get: entry is an alias\n",
2142 rc = LDAP_ALIAS_PROBLEM;
2143 goto return_results;
2147 if ( is_entry_referral( bsi.bsi_e ) ) {
2148 Debug( LDAP_DEBUG_ACL,
2149 "<= backsql_entry_get: entry is a referral\n",
2152 goto return_results;
2155 if ( oc && !is_entry_objectclass( bsi.bsi_e, oc, 0 ) ) {
2156 Debug( LDAP_DEBUG_ACL,
2157 "<= backsql_entry_get: "
2158 "failed to find objectClass\n",
2160 rc = LDAP_NO_SUCH_ATTRIBUTE;
2161 goto return_results;
2168 if ( rc != LDAP_SUCCESS ) {
2170 entry_free( bsi.bsi_e );