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.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
12 * A copy of this license is available in the file LICENSE in the
13 * top-level directory of the distribution or, alternatively, at
14 * <http://www.OpenLDAP.org/license.html>.
17 * This work was initially developed by Dmitry Kovalev for inclusion
18 * by OpenLDAP Software.
24 #include <sys/types.h>
25 #include "ac/string.h"
29 #include "proto-sql.h"
31 static int backsql_process_filter( backsql_srch_info *bsi, Filter *f );
32 static int backsql_process_filter_eq( backsql_srch_info *bsi,
33 backsql_at_map_rec *at,
34 int casefold, struct berval *filter_value );
35 static int backsql_process_filter_like( backsql_srch_info *bsi,
36 backsql_at_map_rec *at,
37 int casefold, struct berval *filter_value );
38 static int backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f,
39 backsql_at_map_rec *at );
42 backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad )
45 AttributeName *an = NULL;
47 if ( bsi->bsi_attrs == NULL ) {
52 * clear the list (retrieve all attrs)
55 ch_free( bsi->bsi_attrs );
56 bsi->bsi_attrs = NULL;
57 bsi->bsi_flags |= BSQL_SF_ALL_ATTRS;
61 for ( ; !BER_BVISNULL( &bsi->bsi_attrs[ n_attrs ].an_name ); n_attrs++ ) {
62 an = &bsi->bsi_attrs[ n_attrs ];
64 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
65 "attribute \"%s\" is in list\n",
66 an->an_name.bv_val, 0, 0 );
68 * We can live with strcmp because the attribute
69 * list has been normalized before calling be_search
71 if ( !BACKSQL_NCMP( &an->an_name, &ad->ad_cname ) ) {
76 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
77 "adding \"%s\" to list\n", ad->ad_cname.bv_val, 0, 0 );
79 an = (AttributeName *)ch_realloc( bsi->bsi_attrs,
80 sizeof( AttributeName ) * ( n_attrs + 2 ) );
85 an[ n_attrs ].an_name = ad->ad_cname;
86 an[ n_attrs ].an_desc = ad;
87 BER_BVZERO( &an[ n_attrs + 1 ].an_name );
95 * Initializes the search structure.
97 * If get_base_id != 0, the field bsi_base_id is filled
98 * with the entryID of bsi_base_ndn; it must be freed
99 * by backsql_free_entryID() when no longer required.
101 * NOTE: base must be normalized
105 backsql_srch_info *bsi,
106 struct berval *nbase,
115 AttributeName *attrs,
119 int rc = LDAP_SUCCESS;
121 bsi->bsi_base_ndn = nbase;
122 BER_BVZERO( &bsi->bsi_base_id.eid_dn );
123 BER_BVZERO( &bsi->bsi_base_id.eid_ndn );
124 bsi->bsi_scope = scope;
125 bsi->bsi_slimit = slimit;
126 bsi->bsi_tlimit = tlimit;
127 bsi->bsi_filter = filter;
131 bsi->bsi_flags = BSQL_SF_NONE;
136 if ( attrs == NULL ) {
137 /* also add request for all operational */
138 bsi->bsi_attrs = NULL;
139 bsi->bsi_flags |= BSQL_SF_ALL_USER;
144 bsi->bsi_attrs = (AttributeName *)ch_calloc( 1,
145 sizeof( AttributeName ) );
146 BER_BVZERO( &bsi->bsi_attrs[ 0 ].an_name );
148 for ( p = attrs; !BER_BVISNULL( &p->an_name ); p++ ) {
150 * ignore "1.1"; handle "+"
152 if ( BACKSQL_NCMP( &p->an_name, &AllUser ) == 0 ) {
153 bsi->bsi_flags |= BSQL_SF_ALL_USER;
156 } else if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 ) {
157 bsi->bsi_flags |= BSQL_SF_ALL_OPER;
160 } else if ( BACKSQL_NCMP( &p->an_name, &NoAttrs ) == 0 ) {
163 } else if ( p->an_desc == slap_schema.si_ad_objectClass ) {
167 backsql_attrlist_add( bsi, p->an_desc );
171 /* add objectClass if not present,
172 * because it is required to understand
173 * if an entry is a referral, an alias
175 backsql_attrlist_add( bsi, slap_schema.si_ad_objectClass );
179 bsi->bsi_abandon = 0;
180 bsi->bsi_id_list = NULL;
181 bsi->bsi_id_listtail = &bsi->bsi_id_list;
182 bsi->bsi_n_candidates = 0;
183 bsi->bsi_stoptime = stoptime;
184 BER_BVZERO( &bsi->bsi_sel.bb_val );
185 bsi->bsi_sel.bb_len = 0;
186 BER_BVZERO( &bsi->bsi_from.bb_val );
187 bsi->bsi_from.bb_len = 0;
188 BER_BVZERO( &bsi->bsi_join_where.bb_val );
189 bsi->bsi_join_where.bb_len = 0;
190 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
191 bsi->bsi_flt_where.bb_len = 0;
192 bsi->bsi_filter_oc = NULL;
194 if ( flags & BACKSQL_ISF_GET_ID ) {
195 assert( op->o_bd->be_private );
197 rc = backsql_dn2id( op, rs, &bsi->bsi_base_id, dbh, nbase,
198 ( flags & BACKSQL_ISF_MUCK ) );
201 return ( bsi->bsi_status = rc );
205 backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op )
213 backsql_strfcat( &bsi->bsi_flt_where, "c", '(' /* ) */ );
216 res = backsql_process_filter( bsi, f );
219 * TimesTen : If the query has no answers,
220 * don't bother to run the query.
231 case LDAP_FILTER_AND:
232 backsql_strfcat( &bsi->bsi_flt_where, "l",
233 (ber_len_t)STRLENOF( " AND " ),
238 backsql_strfcat( &bsi->bsi_flt_where, "l",
239 (ber_len_t)STRLENOF( " OR " ),
245 backsql_strfcat( &bsi->bsi_flt_where, "c", /* ( */ ')' );
251 backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f,
252 backsql_at_map_rec *at )
254 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
262 /* always uppercase strings by now */
263 #ifdef BACKSQL_UPPERCASE_FILTER
264 if ( SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
265 bi->sql_caseIgnoreMatch ) )
266 #endif /* BACKSQL_UPPERCASE_FILTER */
271 if ( SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
272 bi->sql_telephoneNumberMatch ) )
279 * to check for matching telephone numbers
280 * with intermixed chars, e.g. val='1234'
283 * val LIKE '%1%2%3%4%'
287 if ( f->f_sub_initial.bv_val ) {
288 bv.bv_len += f->f_sub_initial.bv_len;
290 if ( f->f_sub_any != NULL ) {
291 for ( a = 0; f->f_sub_any[ a ].bv_val != NULL; a++ ) {
292 bv.bv_len += f->f_sub_any[ a ].bv_len;
295 if ( f->f_sub_final.bv_val ) {
296 bv.bv_len += f->f_sub_final.bv_len;
298 bv.bv_len = 2 * bv.bv_len - 1;
299 bv.bv_val = ch_malloc( bv.bv_len + 1 );
302 if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
303 bv.bv_val[ s ] = f->f_sub_initial.bv_val[ 0 ];
304 for ( i = 1; i < f->f_sub_initial.bv_len; i++ ) {
305 bv.bv_val[ s + 2 * i - 1 ] = '%';
306 bv.bv_val[ s + 2 * i ] = f->f_sub_initial.bv_val[ i ];
308 bv.bv_val[ s + 2 * i - 1 ] = '%';
312 if ( f->f_sub_any != NULL ) {
313 for ( a = 0; !BER_BVISNULL( &f->f_sub_any[ a ] ); a++ ) {
314 bv.bv_val[ s ] = f->f_sub_any[ a ].bv_val[ 0 ];
315 for ( i = 1; i < f->f_sub_any[ a ].bv_len; i++ ) {
316 bv.bv_val[ s + 2 * i - 1 ] = '%';
317 bv.bv_val[ s + 2 * i ] = f->f_sub_any[ a ].bv_val[ i ];
319 bv.bv_val[ s + 2 * i - 1 ] = '%';
324 if ( !BER_BVISNULL( &f->f_sub_final ) ) {
325 bv.bv_val[ s ] = f->f_sub_final.bv_val[ 0 ];
326 for ( i = 1; i < f->f_sub_final.bv_len; i++ ) {
327 bv.bv_val[ s + 2 * i - 1 ] = '%';
328 bv.bv_val[ s + 2 * i ] = f->f_sub_final.bv_val[ i ];
330 bv.bv_val[ s + 2 * i - 1 ] = '%';
334 bv.bv_val[ s - 1 ] = '\0';
336 (void)backsql_process_filter_like( bsi, at, casefold, &bv );
337 ch_free( bv.bv_val );
343 * When dealing with case-sensitive strings
344 * we may omit normalization; however, normalized
345 * SQL filters are more liberal.
348 backsql_strfcat( &bsi->bsi_flt_where, "c", '(' /* ) */ );
351 Debug( LDAP_DEBUG_TRACE, "backsql_process_sub_filter(%s):\n",
352 at->bam_ad->ad_cname.bv_val, 0, 0 );
353 Debug(LDAP_DEBUG_TRACE, " expr: '%s%s%s'\n", at->bam_sel_expr.bv_val,
354 at->bam_sel_expr_u.bv_val ? "' '" : "",
355 at->bam_sel_expr_u.bv_val ? at->bam_sel_expr_u.bv_val : "" );
356 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
358 * If a pre-upper-cased version of the column
359 * or a precompiled upper function exists, use it
361 backsql_strfcat( &bsi->bsi_flt_where,
364 (ber_len_t)STRLENOF( " LIKE '" ),
368 backsql_strfcat( &bsi->bsi_flt_where, "bl",
370 (ber_len_t)STRLENOF( " LIKE '" ), " LIKE '" );
373 if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
377 Debug( LDAP_DEBUG_TRACE,
378 "==>backsql_process_sub_filter(%s): "
379 "sub_initial=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
380 f->f_sub_initial.bv_val, 0 );
381 #endif /* BACKSQL_TRACE */
383 start = bsi->bsi_flt_where.bb_val.bv_len;
384 backsql_strfcat( &bsi->bsi_flt_where, "b",
386 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
387 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
391 backsql_strfcat( &bsi->bsi_flt_where, "c", '%' );
393 if ( f->f_sub_any != NULL ) {
394 for ( i = 0; !BER_BVISNULL( &f->f_sub_any[ i ] ); i++ ) {
398 Debug( LDAP_DEBUG_TRACE,
399 "==>backsql_process_sub_filter(%s): "
400 "sub_any[%d]=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
401 i, f->f_sub_any[ i ].bv_val );
402 #endif /* BACKSQL_TRACE */
404 start = bsi->bsi_flt_where.bb_val.bv_len;
405 backsql_strfcat( &bsi->bsi_flt_where,
409 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
411 * Note: toupper('%') = '%'
413 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
418 if ( !BER_BVISNULL( &f->f_sub_final ) ) {
422 Debug( LDAP_DEBUG_TRACE,
423 "==>backsql_process_sub_filter(%s): "
424 "sub_final=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
425 f->f_sub_final.bv_val, 0 );
426 #endif /* BACKSQL_TRACE */
428 start = bsi->bsi_flt_where.bb_val.bv_len;
429 backsql_strfcat( &bsi->bsi_flt_where, "b",
431 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
432 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
436 backsql_strfcat( &bsi->bsi_flt_where, "l",
437 (ber_len_t)STRLENOF( /* (' */ "')" ), /* (' */ "')" );
443 backsql_merge_from_tbls( backsql_srch_info *bsi, struct berval *from_tbls )
445 if ( BER_BVISNULL( from_tbls ) ) {
449 if ( !BER_BVISNULL( &bsi->bsi_from.bb_val ) ) {
450 char *start, *end, *tmp;
452 tmp = ch_strdup( from_tbls->bv_val );
454 for ( start = tmp, end = strchr( start, ',' ); start; ) {
459 if ( strstr( bsi->bsi_from.bb_val.bv_val, start) == NULL )
461 backsql_strfcat( &bsi->bsi_from, "cs", ',', start );
465 /* in case there are spaces after the comma... */
466 for ( start = &end[1]; isspace( start[0] ); start++ );
468 end = strchr( start, ',' );
480 backsql_strfcat( &bsi->bsi_from, "b", from_tbls );
487 backsql_process_filter( backsql_srch_info *bsi, Filter *f )
489 backsql_at_map_rec **vat = NULL;
490 AttributeDescription *ad = NULL;
495 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter()\n", 0, 0, 0 );
496 if ( f->f_choice == SLAPD_FILTER_COMPUTED ) {
497 Debug( LDAP_DEBUG_TRACE, "backsql_process_filter(): "
498 "invalid filter\n", 0, 0, 0 );
503 switch( f->f_choice ) {
505 rc = backsql_process_filter_list( bsi, f->f_or,
510 case LDAP_FILTER_AND:
511 rc = backsql_process_filter_list( bsi, f->f_and,
516 case LDAP_FILTER_NOT:
517 backsql_strfcat( &bsi->bsi_flt_where, "l",
518 (ber_len_t)STRLENOF( "NOT (" /* ) */ ),
520 rc = backsql_process_filter( bsi, f->f_not );
521 backsql_strfcat( &bsi->bsi_flt_where, "c", /* ( */ ')' );
525 case LDAP_FILTER_PRESENT:
529 case LDAP_FILTER_EXT:
530 ad = f->f_mra->ma_desc;
531 if ( f->f_mr_dnattrs ) {
533 * if dn attrs filtering is requested, better return
534 * success and let test_filter() deal with candidate
535 * selection; otherwise we'd need to set conditions
536 * on the contents of the DN, e.g. "SELECT ... FROM
537 * ldap_entries AS attributeName WHERE attributeName.dn
538 * like '%attributeName=value%'"
540 backsql_strfcat( &bsi->bsi_flt_where, "l",
541 (ber_len_t)STRLENOF( "1=1" ), "1=1" );
542 bsi->bsi_status = LDAP_SUCCESS;
563 * Turn structuralObjectClass into objectClass
565 if ( ad == slap_schema.si_ad_objectClass
566 || ad == slap_schema.si_ad_structuralObjectClass )
569 * If the filter is LDAP_FILTER_PRESENT, then it's done;
570 * otherwise, let's see if we are lucky: filtering
571 * for "structural" objectclass or ancestor...
573 switch ( f->f_choice ) {
574 case LDAP_FILTER_EQUALITY:
576 ObjectClass *oc = oc_bvfind( &f->f_av_value );
579 Debug( LDAP_DEBUG_TRACE,
580 "backsql_process_filter(): "
581 "unknown objectClass \"%s\" "
583 f->f_av_value.bv_val, 0, 0 );
584 bsi->bsi_status = LDAP_OTHER;
590 * "structural" objectClass inheritance:
591 * - a search for "person" will also return
593 * - a search for "top" will return everything
595 if ( is_object_subclass( oc, bsi->bsi_oc->bom_oc ) ) {
596 static struct berval ldap_entry_objclasses = BER_BVC( "ldap_entry_objclasses" );
598 backsql_merge_from_tbls( bsi, &ldap_entry_objclasses );
600 backsql_strfcat( &bsi->bsi_flt_where, "lbl",
601 (ber_len_t)STRLENOF( "1=1 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */ ),
602 "1=1 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */,
603 &bsi->bsi_oc->bom_oc->soc_cname,
604 (ber_len_t)STRLENOF( /* (' */ "')" ),
606 bsi->bsi_status = LDAP_SUCCESS;
614 case LDAP_FILTER_PRESENT:
615 backsql_strfcat( &bsi->bsi_flt_where, "l",
616 (ber_len_t)STRLENOF( "1=1" ), "1=1" );
617 bsi->bsi_status = LDAP_SUCCESS;
621 /* FIXME: LDAP_FILTER_EXT? */
624 Debug( LDAP_DEBUG_TRACE,
625 "backsql_process_filter(): "
626 "illegal/unhandled filter "
627 "on objectClass attribute",
629 bsi->bsi_status = LDAP_OTHER;
634 } else if ( ad == slap_schema.si_ad_entryUUID ) {
636 #ifdef BACKSQL_ARBITRARY_KEY
637 struct berval keyval;
638 #else /* ! BACKSQL_ARBITRARY_KEY */
639 unsigned long keyval;
640 char keyvalbuf[] = "18446744073709551615";
641 #endif /* ! BACKSQL_ARBITRARY_KEY */
643 switch ( f->f_choice ) {
644 case LDAP_FILTER_EQUALITY:
645 backsql_entryUUID_decode( &f->f_av_value, &oc_id, &keyval );
647 if ( oc_id != bsi->bsi_oc->bom_id ) {
648 bsi->bsi_status = LDAP_SUCCESS;
653 #ifdef BACKSQL_ARBITRARY_KEY
654 backsql_strfcat( &bsi->bsi_flt_where, "bcblbc",
655 &bsi->bsi_oc->bom_keytbl, '.',
656 &bsi->bsi_oc->bom_keycol,
657 STRLENOF( " LIKE '" ), " LIKE '",
659 #else /* ! BACKSQL_ARBITRARY_KEY */
660 snprintf( keyvalbuf, sizeof( keyvalbuf ), "%lu", keyval );
661 backsql_strfcat( &bsi->bsi_flt_where, "bcbcs",
662 &bsi->bsi_oc->bom_keytbl, '.',
663 &bsi->bsi_oc->bom_keycol, '=', keyvalbuf );
664 #endif /* ! BACKSQL_ARBITRARY_KEY */
667 case LDAP_FILTER_PRESENT:
668 backsql_strfcat( &bsi->bsi_flt_where, "l",
669 (ber_len_t)STRLENOF( "1=1" ), "1=1" );
677 bsi->bsi_flags |= BSQL_SF_FILTER_ENTRYUUID;
681 #ifdef BACKSQL_SYNCPROV
682 } else if ( ad == slap_schema.si_ad_entryCSN ) {
684 * TODO: introduce appropriate entryCSN filtering
685 * to support syncrepl as producer...
687 if ( !bsi->bsi_op->o_sync ) {
688 /* unsupported at present... */
689 bsi->bsi_status = LDAP_OTHER;
694 bsi->bsi_flags |= ( BSQL_SF_FILTER_ENTRYCSN | BSQL_SF_RETURN_ENTRYUUID);
696 /* if doing a syncrepl, try to return as much as possible,
697 * and always match the filter */
698 backsql_strfcat( &bsi->bsi_flt_where, "l",
699 (ber_len_t)STRLENOF( "1=1" ), "1=1" );
701 /* save for later use in operational attributes */
702 /* FIXME: saves only the first occurrence, because
703 * the filter during updates is written as
704 * "(&(entryCSN<={contextCSN})(entryCSN>={oldContextCSN})({filter}))"
705 * so we want our fake entryCSN to match the greatest
708 if ( bsi->bsi_op->o_private == NULL ) {
709 bsi->bsi_op->o_private = &f->f_av_value;
711 bsi->bsi_status = LDAP_SUCCESS;
715 #endif /* BACKSQL_SYNCPROV */
717 } else if ( ad == slap_schema.si_ad_hasSubordinates || ad == NULL ) {
719 * FIXME: this is not robust; e.g. a filter
720 * '(!(hasSubordinates=TRUE))' fails because
721 * in SQL it would read 'NOT (1=1)' instead
723 * Note however that hasSubordinates is boolean,
724 * so a more appropriate filter would be
725 * '(hasSubordinates=FALSE)'
727 * A more robust search for hasSubordinates
728 * would * require joining the ldap_entries table
729 * selecting if there are descendants of the
732 backsql_strfcat( &bsi->bsi_flt_where, "l",
733 (ber_len_t)STRLENOF( "1=1" ), "1=1" );
734 if ( ad == slap_schema.si_ad_hasSubordinates ) {
736 * instruct candidate selection algorithm
737 * and attribute list to try to detect
738 * if an entry has subordinates
740 bsi->bsi_flags |= BSQL_SF_FILTER_HASSUBORDINATE;
744 * clear attributes to fetch, to require ALL
745 * and try extended match on all attributes
747 backsql_attrlist_add( bsi, NULL );
754 * attribute inheritance:
756 if ( backsql_supad2at( bsi->bsi_oc, ad, &vat ) ) {
757 bsi->bsi_status = LDAP_OTHER;
763 /* search anyway; other parts of the filter
765 backsql_strfcat( &bsi->bsi_flt_where, "l",
766 (ber_len_t)STRLENOF( "1=1" ), "1=1" );
767 bsi->bsi_status = LDAP_SUCCESS;
772 /* if required, open extra level of parens */
774 if ( vat[0]->bam_next || vat[1] ) {
775 backsql_strfcat( &bsi->bsi_flt_where, "c", '(' );
782 if ( backsql_process_filter_attr( bsi, f, vat[i] ) == -1 ) {
786 /* if more definitions of the same attr, apply */
787 if ( vat[i]->bam_next ) {
788 backsql_strfcat( &bsi->bsi_flt_where, "l",
789 STRLENOF( " OR " ), " OR " );
790 vat[i] = vat[i]->bam_next;
794 /* if more descendants of the same attr, apply */
797 backsql_strfcat( &bsi->bsi_flt_where, "l",
798 STRLENOF( " OR " ), " OR " );
802 /* if needed, close extra level of parens */
804 backsql_strfcat( &bsi->bsi_flt_where, "c", ')' );
814 Debug( LDAP_DEBUG_TRACE,
815 "<==backsql_process_filter() %s\n",
816 rc == 1 ? "succeeded" : "failed", 0, 0);
822 backsql_process_filter_eq( backsql_srch_info *bsi, backsql_at_map_rec *at,
823 int casefold, struct berval *filter_value )
826 * maybe we should check type of at->sel_expr here somehow,
827 * to know whether upper_func is applicable, but for now
828 * upper_func stuff is made for Oracle, where UPPER is
829 * safely applicable to NUMBER etc.
831 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
834 backsql_strfcat( &bsi->bsi_flt_where, "cbl",
837 (ber_len_t)STRLENOF( "='" ),
840 start = bsi->bsi_flt_where.bb_val.bv_len;
842 backsql_strfcat( &bsi->bsi_flt_where, "bl",
844 (ber_len_t)STRLENOF( /* (' */ "')" ),
847 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
850 backsql_strfcat( &bsi->bsi_flt_where, "cblbl",
853 (ber_len_t)STRLENOF( "='" ), "='",
855 (ber_len_t)STRLENOF( /* (' */ "')" ),
863 backsql_process_filter_like( backsql_srch_info *bsi, backsql_at_map_rec *at,
864 int casefold, struct berval *filter_value )
867 * maybe we should check type of at->sel_expr here somehow,
868 * to know whether upper_func is applicable, but for now
869 * upper_func stuff is made for Oracle, where UPPER is
870 * safely applicable to NUMBER etc.
872 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
875 backsql_strfcat( &bsi->bsi_flt_where, "cbl",
878 (ber_len_t)STRLENOF( " LIKE '%" ),
881 start = bsi->bsi_flt_where.bb_val.bv_len;
883 backsql_strfcat( &bsi->bsi_flt_where, "bl",
885 (ber_len_t)STRLENOF( /* (' */ "%')" ),
888 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
891 backsql_strfcat( &bsi->bsi_flt_where, "cblbl",
894 (ber_len_t)STRLENOF( " LIKE '%" ),
897 (ber_len_t)STRLENOF( /* (' */ "%')" ),
905 backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f, backsql_at_map_rec *at )
907 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
909 struct berval *filter_value = NULL;
910 MatchingRule *matching_rule = NULL;
911 struct berval ordering = BER_BVC("<=");
913 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter_attr(%s)\n",
914 at->bam_ad->ad_cname.bv_val, 0, 0 );
917 * need to add this attribute to list of attrs to load,
918 * so that we can do test_filter() later
920 backsql_attrlist_add( bsi, at->bam_ad );
922 backsql_merge_from_tbls( bsi, &at->bam_from_tbls );
924 if ( !BER_BVISNULL( &at->bam_join_where )
925 && strstr( bsi->bsi_join_where.bb_val.bv_val,
926 at->bam_join_where.bv_val ) == NULL )
928 backsql_strfcat( &bsi->bsi_join_where, "lb",
929 (ber_len_t)STRLENOF( " AND " ), " AND ",
930 &at->bam_join_where );
933 switch ( f->f_choice ) {
934 case LDAP_FILTER_EQUALITY:
935 filter_value = &f->f_av_value;
936 matching_rule = at->bam_ad->ad_type->sat_equality;
940 /* fail over into next case */
942 case LDAP_FILTER_EXT:
943 filter_value = &f->f_mra->ma_value;
944 matching_rule = f->f_mr_rule;
947 /* always uppercase strings by now */
948 #ifdef BACKSQL_UPPERCASE_FILTER
949 if ( SLAP_MR_ASSOCIATED( matching_rule,
950 bi->sql_caseIgnoreMatch ) )
951 #endif /* BACKSQL_UPPERCASE_FILTER */
956 if ( SLAP_MR_ASSOCIATED( matching_rule,
957 bi->sql_telephoneNumberMatch ) )
963 * to check for matching telephone numbers
964 * with intermized chars, e.g. val='1234'
967 * val LIKE '%1%2%3%4%'
970 bv.bv_len = 2 * filter_value->bv_len - 1;
971 bv.bv_val = ch_malloc( bv.bv_len + 1 );
973 bv.bv_val[ 0 ] = filter_value->bv_val[ 0 ];
974 for ( i = 1; i < filter_value->bv_len; i++ ) {
975 bv.bv_val[ 2 * i - 1 ] = '%';
976 bv.bv_val[ 2 * i ] = filter_value->bv_val[ i ];
978 bv.bv_val[ 2 * i - 1 ] = '\0';
980 (void)backsql_process_filter_like( bsi, at, casefold, &bv );
981 ch_free( bv.bv_val );
986 /* NOTE: this is required by objectClass inheritance
987 * and auxiliary objectClass use in filters for slightly
988 * more efficient candidate selection. */
989 /* FIXME: a bit too many specializations to deal with
990 * very specific cases... */
991 if ( at->bam_ad == slap_schema.si_ad_objectClass
992 || at->bam_ad == slap_schema.si_ad_structuralObjectClass )
994 backsql_strfcat( &bsi->bsi_flt_where, "lbl",
995 (ber_len_t)STRLENOF( "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */ ),
996 "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */,
998 (ber_len_t)STRLENOF( /* (' */ "')" ),
1004 * maybe we should check type of at->sel_expr here somehow,
1005 * to know whether upper_func is applicable, but for now
1006 * upper_func stuff is made for Oracle, where UPPER is
1007 * safely applicable to NUMBER etc.
1009 (void)backsql_process_filter_eq( bsi, at, casefold, filter_value );
1012 case LDAP_FILTER_GE:
1013 ordering.bv_val = ">=";
1015 /* fall thru to next case */
1017 case LDAP_FILTER_LE:
1018 /* always uppercase strings by now */
1019 #ifdef BACKSQL_UPPERCASE_FILTER
1020 if ( SLAP_MR_ASSOCIATED( at->bam_ad->ad_type->sat_ordering,
1021 bi->sql_caseIgnoreMatch ) )
1022 #endif /* BACKSQL_UPPERCASE_FILTER */
1028 * FIXME: should we uppercase the operands?
1030 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1033 backsql_strfcat( &bsi->bsi_flt_where, "cbbc",
1035 &at->bam_sel_expr_u,
1039 start = bsi->bsi_flt_where.bb_val.bv_len;
1041 backsql_strfcat( &bsi->bsi_flt_where, "bl",
1043 (ber_len_t)STRLENOF( /* (' */ "')" ),
1046 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1049 backsql_strfcat( &bsi->bsi_flt_where, "cbbcbl",
1055 (ber_len_t)STRLENOF( /* (' */ "')" ),
1060 case LDAP_FILTER_PRESENT:
1061 backsql_strfcat( &bsi->bsi_flt_where, "lbl",
1062 (ber_len_t)STRLENOF( "NOT (" /* ) */),
1065 (ber_len_t)STRLENOF( /* ( */ " IS NULL)" ),
1066 /* ( */ " IS NULL)" );
1069 case LDAP_FILTER_SUBSTRINGS:
1070 backsql_process_sub_filter( bsi, f, at );
1073 case LDAP_FILTER_APPROX:
1074 /* we do our best */
1077 * maybe we should check type of at->sel_expr here somehow,
1078 * to know whether upper_func is applicable, but for now
1079 * upper_func stuff is made for Oracle, where UPPER is
1080 * safely applicable to NUMBER etc.
1082 (void)backsql_process_filter_like( bsi, at, 1, &f->f_av_value );
1086 /* unhandled filter type; should not happen */
1088 backsql_strfcat( &bsi->bsi_flt_where, "l",
1089 (ber_len_t)STRLENOF( "1=1" ), "1=1" );
1094 Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter_attr(%s)\n",
1095 at->bam_ad->ad_cname.bv_val, 0, 0 );
1101 backsql_srch_query( backsql_srch_info *bsi, struct berval *query )
1103 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1107 BER_BVZERO( query );
1109 Debug( LDAP_DEBUG_TRACE, "==>backsql_srch_query()\n", 0, 0, 0 );
1110 BER_BVZERO( &bsi->bsi_sel.bb_val );
1111 BER_BVZERO( &bsi->bsi_sel.bb_val );
1112 bsi->bsi_sel.bb_len = 0;
1113 BER_BVZERO( &bsi->bsi_from.bb_val );
1114 bsi->bsi_from.bb_len = 0;
1115 BER_BVZERO( &bsi->bsi_join_where.bb_val );
1116 bsi->bsi_join_where.bb_len = 0;
1117 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1118 bsi->bsi_flt_where.bb_len = 0;
1120 backsql_strfcat( &bsi->bsi_sel, "lbcbc",
1121 (ber_len_t)STRLENOF( "SELECT DISTINCT ldap_entries.id," ),
1122 "SELECT DISTINCT ldap_entries.id,",
1123 &bsi->bsi_oc->bom_keytbl,
1125 &bsi->bsi_oc->bom_keycol,
1128 if ( !BER_BVISNULL( &bi->sql_strcast_func ) ) {
1129 backsql_strfcat( &bsi->bsi_sel, "blbl",
1130 &bi->sql_strcast_func,
1131 (ber_len_t)STRLENOF( "('" /* ') */ ),
1133 &bsi->bsi_oc->bom_oc->soc_cname,
1134 (ber_len_t)STRLENOF( /* (' */ "')" ),
1137 backsql_strfcat( &bsi->bsi_sel, "cbc",
1139 &bsi->bsi_oc->bom_oc->soc_cname,
1142 #ifdef BACKSQL_ALIASING_QUOTE
1143 backsql_strfcat( &bsi->bsi_sel, "lclcl",
1144 (ber_len_t)STRLENOF( " " BACKSQL_ALIASING ),
1145 " " BACKSQL_ALIASING,
1146 BACKSQL_ALIASING_QUOTE,
1147 (ber_len_t)STRLENOF( "objectClass" ),
1149 BACKSQL_ALIASING_QUOTE,
1150 (ber_len_t)STRLENOF( ",ldap_entries.dn " BACKSQL_ALIASING "dn" ),
1151 ",ldap_entries.dn " BACKSQL_ALIASING "dn" );
1152 #else /* ! BACKSQL_ALIASING_QUOTE */
1153 backsql_strfcat( &bsi->bsi_sel, "l",
1154 (ber_len_t)STRLENOF( " " BACKSQL_ALIASING "objectClass,ldap_entries.dn " BACKSQL_ALIASING "dn" ),
1155 " " BACKSQL_ALIASING "objectClass,ldap_entries.dn " BACKSQL_ALIASING "dn" );
1156 #endif /* ! BACKSQL_ALIASING_QUOTE */
1158 backsql_strfcat( &bsi->bsi_from, "lb",
1159 (ber_len_t)STRLENOF( " FROM ldap_entries," ),
1160 " FROM ldap_entries,",
1161 &bsi->bsi_oc->bom_keytbl );
1163 backsql_strfcat( &bsi->bsi_join_where, "lbcbl",
1164 (ber_len_t)STRLENOF( " WHERE " ), " WHERE ",
1165 &bsi->bsi_oc->bom_keytbl,
1167 &bsi->bsi_oc->bom_keycol,
1168 (ber_len_t)STRLENOF( "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ),
1169 "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " );
1171 switch ( bsi->bsi_scope ) {
1172 case LDAP_SCOPE_BASE:
1173 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1174 backsql_strfcat( &bsi->bsi_join_where, "bl",
1175 &bi->sql_upper_func,
1176 (ber_len_t)STRLENOF( "(ldap_entries.dn)=?" ),
1177 "(ldap_entries.dn)=?" );
1179 backsql_strfcat( &bsi->bsi_join_where, "l",
1180 (ber_len_t)STRLENOF( "ldap_entries.dn=?" ),
1181 "ldap_entries.dn=?" );
1185 case BACKSQL_SCOPE_BASE_LIKE:
1186 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1187 backsql_strfcat( &bsi->bsi_join_where, "bl",
1188 &bi->sql_upper_func,
1189 (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1190 "(ldap_entries.dn) LIKE ?" );
1192 backsql_strfcat( &bsi->bsi_join_where, "l",
1193 (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1194 "ldap_entries.dn LIKE ?" );
1198 case LDAP_SCOPE_ONELEVEL:
1199 backsql_strfcat( &bsi->bsi_join_where, "l",
1200 (ber_len_t)STRLENOF( "ldap_entries.parent=?" ),
1201 "ldap_entries.parent=?" );
1204 #ifdef LDAP_SCOPE_SUBORDINATE
1205 case LDAP_SCOPE_SUBORDINATE:
1206 #endif /* LDAP_SCOPE_SUBORDINATE */
1207 case LDAP_SCOPE_SUBTREE:
1208 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1209 backsql_strfcat( &bsi->bsi_join_where, "bl",
1210 &bi->sql_upper_func,
1211 (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1212 "(ldap_entries.dn) LIKE ?" );
1214 backsql_strfcat( &bsi->bsi_join_where, "l",
1215 (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1216 "ldap_entries.dn LIKE ?" );
1225 rc = backsql_process_filter( bsi, bsi->bsi_filter );
1227 struct berbuf bb = BB_NULL;
1229 backsql_strfcat( &bb, "bbblb",
1230 &bsi->bsi_sel.bb_val,
1231 &bsi->bsi_from.bb_val,
1232 &bsi->bsi_join_where.bb_val,
1233 (ber_len_t)STRLENOF( " AND " ), " AND ",
1234 &bsi->bsi_flt_where.bb_val );
1238 } else if ( rc < 0 ) {
1240 * Indicates that there's no possible way the filter matches
1241 * anything. No need to issue the query
1243 free( query->bv_val );
1244 BER_BVZERO( query );
1247 free( bsi->bsi_sel.bb_val.bv_val );
1248 BER_BVZERO( &bsi->bsi_sel.bb_val );
1249 bsi->bsi_sel.bb_len = 0;
1250 free( bsi->bsi_from.bb_val.bv_val );
1251 BER_BVZERO( &bsi->bsi_from.bb_val );
1252 bsi->bsi_from.bb_len = 0;
1253 free( bsi->bsi_join_where.bb_val.bv_val );
1254 BER_BVZERO( &bsi->bsi_join_where.bb_val );
1255 bsi->bsi_join_where.bb_len = 0;
1256 free( bsi->bsi_flt_where.bb_val.bv_val );
1257 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1258 bsi->bsi_flt_where.bb_len = 0;
1260 Debug( LDAP_DEBUG_TRACE, "<==backsql_srch_query() returns %s\n",
1261 query->bv_val ? query->bv_val : "NULL", 0, 0 );
1263 return ( rc <= 0 ? 1 : 0 );
1267 backsql_oc_get_candidates( void *v_oc, void *v_bsi )
1269 backsql_oc_map_rec *oc = v_oc;
1270 backsql_srch_info *bsi = v_bsi;
1271 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1272 struct berval query;
1276 BACKSQL_ROW_NTS row;
1279 int n_candidates = bsi->bsi_n_candidates;
1282 * + 1 because we need room for '%';
1283 * + 1 because we need room for ',' for LDAP_SCOPE_SUBORDINATE;
1284 * this makes a subtree
1285 * search for a DN BACKSQL_MAX_DN_LEN long legal
1286 * if it returns that DN only
1288 char tmp_base_ndn[ BACKSQL_MAX_DN_LEN + 1 + 1 ];
1290 bsi->bsi_status = LDAP_SUCCESS;
1292 Debug( LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc=\"%s\"\n",
1293 BACKSQL_OC_NAME( oc ), 0, 0 );
1295 if ( bsi->bsi_n_candidates == -1 ) {
1296 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1297 "unchecked limit has been overcome\n", 0, 0, 0 );
1298 /* should never get here */
1300 bsi->bsi_status = LDAP_ADMINLIMIT_EXCEEDED;
1301 return BACKSQL_AVL_STOP;
1305 res = backsql_srch_query( bsi, &query );
1307 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1308 "error while constructing query for objectclass \"%s\"\n",
1309 oc->bom_oc->soc_cname.bv_val, 0, 0 );
1311 * FIXME: need to separate errors from legally
1312 * impossible filters
1314 switch ( bsi->bsi_status ) {
1316 case LDAP_UNDEFINED_TYPE:
1317 case LDAP_NO_SUCH_OBJECT:
1318 /* we are conservative... */
1320 bsi->bsi_status = LDAP_SUCCESS;
1322 return BACKSQL_AVL_CONTINUE;
1324 case LDAP_ADMINLIMIT_EXCEEDED:
1326 /* don't try any more */
1327 return BACKSQL_AVL_STOP;
1331 if ( BER_BVISNULL( &query ) ) {
1332 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1333 "could not construct query for objectclass \"%s\"\n",
1334 oc->bom_oc->soc_cname.bv_val, 0, 0 );
1335 bsi->bsi_status = LDAP_SUCCESS;
1336 return BACKSQL_AVL_CONTINUE;
1339 Debug( LDAP_DEBUG_TRACE, "Constructed query: %s\n",
1340 query.bv_val, 0, 0 );
1342 rc = backsql_Prepare( bsi->bsi_dbh, &sth, query.bv_val, 0 );
1343 free( query.bv_val );
1344 BER_BVZERO( &query );
1345 if ( rc != SQL_SUCCESS ) {
1346 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1347 "error preparing query\n", 0, 0, 0 );
1348 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1349 bsi->bsi_status = LDAP_OTHER;
1350 return BACKSQL_AVL_CONTINUE;
1353 Debug( LDAP_DEBUG_TRACE, "id: '%ld'\n", bsi->bsi_oc->bom_id, 0, 0 );
1355 rc = backsql_BindParamInt( sth, 1, SQL_PARAM_INPUT,
1356 &bsi->bsi_oc->bom_id );
1357 if ( rc != SQL_SUCCESS ) {
1358 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1359 "error binding objectclass id parameter\n", 0, 0, 0 );
1360 bsi->bsi_status = LDAP_OTHER;
1361 return BACKSQL_AVL_CONTINUE;
1364 switch ( bsi->bsi_scope ) {
1365 case LDAP_SCOPE_BASE:
1366 case BACKSQL_SCOPE_BASE_LIKE:
1368 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1369 * however this should be handled earlier
1371 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1372 bsi->bsi_status = LDAP_OTHER;
1373 return BACKSQL_AVL_CONTINUE;
1376 AC_MEMCPY( tmp_base_ndn, bsi->bsi_base_ndn->bv_val,
1377 bsi->bsi_base_ndn->bv_len + 1 );
1379 /* uppercase DN only if the stored DN can be uppercased
1381 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1382 ldap_pvt_str2upper( tmp_base_ndn );
1385 Debug( LDAP_DEBUG_TRACE, "(base)dn: \"%s\"\n",
1386 tmp_base_ndn, 0, 0 );
1388 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1389 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1390 if ( rc != SQL_SUCCESS ) {
1391 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1392 "error binding base_ndn parameter\n", 0, 0, 0 );
1393 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh,
1395 bsi->bsi_status = LDAP_OTHER;
1396 return BACKSQL_AVL_CONTINUE;
1400 #ifdef LDAP_SCOPE_SUBORDINATE
1401 case LDAP_SCOPE_SUBORDINATE:
1402 #endif /* LDAP_SCOPE_SUBORDINATE */
1403 case LDAP_SCOPE_SUBTREE:
1406 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1407 * however this should be handled earlier
1409 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1410 bsi->bsi_status = LDAP_OTHER;
1411 return BACKSQL_AVL_CONTINUE;
1415 * Sets the parameters for the SQL built earlier
1416 * NOTE that all the databases could actually use
1417 * the TimesTen version, which would be cleaner
1418 * and would also eliminate the need for the
1419 * subtree_cond line in the configuration file.
1420 * For now, I'm leaving it the way it is,
1421 * so non-TimesTen databases use the original code.
1422 * But at some point this should get cleaned up.
1424 * If "dn" is being used, do a suffix search.
1425 * If "dn_ru" is being used, do a prefix search.
1427 if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
1428 tmp_base_ndn[ 0 ] = '\0';
1430 for ( i = 0, j = bsi->bsi_base_ndn->bv_len - 1;
1432 tmp_base_ndn[ i ] = bsi->bsi_base_ndn->bv_val[ j ];
1435 #ifdef LDAP_SCOPE_SUBORDINATE
1436 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1437 tmp_base_ndn[ i++ ] = ',';
1439 #endif /* LDAP_SCOPE_SUBORDINATE */
1441 tmp_base_ndn[ i ] = '%';
1442 tmp_base_ndn[ i + 1 ] = '\0';
1447 tmp_base_ndn[ i++ ] = '%';
1449 #ifdef LDAP_SCOPE_SUBORDINATE
1450 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1451 tmp_base_ndn[ i++ ] = ',';
1453 #endif /* LDAP_SCOPE_SUBORDINATE */
1455 AC_MEMCPY( &tmp_base_ndn[ i ], bsi->bsi_base_ndn->bv_val,
1456 bsi->bsi_base_ndn->bv_len + 1 );
1459 /* uppercase DN only if the stored DN can be uppercased
1461 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1462 ldap_pvt_str2upper( tmp_base_ndn );
1465 #ifdef LDAP_SCOPE_SUBORDINATE
1466 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1467 Debug( LDAP_DEBUG_TRACE, "(children)dn: \"%s\"\n",
1468 tmp_base_ndn, 0, 0 );
1470 #endif /* LDAP_SCOPE_SUBORDINATE */
1472 Debug( LDAP_DEBUG_TRACE, "(sub)dn: \"%s\"\n",
1473 tmp_base_ndn, 0, 0 );
1476 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1477 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1478 if ( rc != SQL_SUCCESS ) {
1479 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1480 "error binding base_ndn parameter (2)\n",
1482 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh,
1484 bsi->bsi_status = LDAP_OTHER;
1485 return BACKSQL_AVL_CONTINUE;
1490 case LDAP_SCOPE_ONELEVEL:
1491 assert( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) );
1493 #ifdef BACKSQL_ARBITRARY_KEY
1494 Debug( LDAP_DEBUG_TRACE, "(one)id: \"%s\"\n",
1495 bsi->bsi_base_id.eid_id.bv_val, 0, 0 );
1496 #else /* ! BACKSQL_ARBITRARY_KEY */
1497 Debug( LDAP_DEBUG_TRACE, "(one)id: '%lu'\n",
1498 bsi->bsi_base_id.eid_id, 0, 0 );
1499 #endif /* ! BACKSQL_ARBITRARY_KEY */
1500 rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT,
1501 &bsi->bsi_base_id.eid_id );
1502 if ( rc != SQL_SUCCESS ) {
1503 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1504 "error binding base id parameter\n", 0, 0, 0 );
1505 bsi->bsi_status = LDAP_OTHER;
1506 return BACKSQL_AVL_CONTINUE;
1511 rc = SQLExecute( sth );
1512 if ( !BACKSQL_SUCCESS( rc ) ) {
1513 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1514 "error executing query\n", 0, 0, 0 );
1515 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1516 SQLFreeStmt( sth, SQL_DROP );
1517 bsi->bsi_status = LDAP_OTHER;
1518 return BACKSQL_AVL_CONTINUE;
1521 backsql_BindRowAsStrings( sth, &row );
1522 rc = SQLFetch( sth );
1523 for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
1524 struct berval dn, pdn, ndn;
1525 backsql_entryID *c_id = NULL;
1528 ber_str2bv( row.cols[ 3 ], 0, 0, &dn );
1530 if ( backsql_api_odbc2dn( bsi->bsi_op, bsi->bsi_rs, &dn ) ) {
1534 ret = dnPrettyNormal( NULL, &dn, &pdn, &ndn, NULL );
1535 if ( dn.bv_val != row.cols[ 3 ] ) {
1539 if ( ret != LDAP_SUCCESS ) {
1543 if ( bi->sql_baseObject && dn_match( &ndn, &bi->sql_baseObject->e_nname ) ) {
1549 c_id = (backsql_entryID *)ch_calloc( 1,
1550 sizeof( backsql_entryID ) );
1551 #ifdef BACKSQL_ARBITRARY_KEY
1552 ber_str2bv( row.cols[ 0 ], 0, 1, &c_id->eid_id );
1553 ber_str2bv( row.cols[ 1 ], 0, 1, &c_id->eid_keyval );
1554 #else /* ! BACKSQL_ARBITRARY_KEY */
1555 c_id->eid_id = strtol( row.cols[ 0 ], NULL, 0 );
1556 c_id->eid_keyval = strtol( row.cols[ 1 ], NULL, 0 );
1557 #endif /* ! BACKSQL_ARBITRARY_KEY */
1558 c_id->eid_oc_id = bsi->bsi_oc->bom_id;
1561 c_id->eid_ndn = ndn;
1563 /* append at end of list ... */
1564 c_id->eid_next = NULL;
1565 *bsi->bsi_id_listtail = c_id;
1566 bsi->bsi_id_listtail = &c_id->eid_next;
1568 #ifdef BACKSQL_ARBITRARY_KEY
1569 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1570 "added entry id=%s, keyval=%s dn=\"%s\"\n",
1571 c_id->eid_id.bv_val, c_id->eid_keyval.bv_val,
1573 #else /* ! BACKSQL_ARBITRARY_KEY */
1574 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1575 "added entry id=%ld, keyval=%ld dn=\"%s\"\n",
1576 c_id->eid_id, c_id->eid_keyval, row.cols[ 3 ] );
1577 #endif /* ! BACKSQL_ARBITRARY_KEY */
1579 /* count candidates, for unchecked limit */
1580 bsi->bsi_n_candidates--;
1581 if ( bsi->bsi_n_candidates == -1 ) {
1585 backsql_FreeRow( &row );
1586 SQLFreeStmt( sth, SQL_DROP );
1588 Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates(): %d\n",
1589 n_candidates - bsi->bsi_n_candidates, 0, 0 );
1591 return ( bsi->bsi_n_candidates == -1 ? BACKSQL_AVL_STOP : BACKSQL_AVL_CONTINUE );
1595 backsql_search( Operation *op, SlapReply *rs )
1597 backsql_info *bi = (backsql_info *)op->o_bd->be_private;
1600 Entry user_entry = { 0 };
1602 time_t stoptime = 0;
1603 backsql_srch_info bsi;
1604 backsql_entryID *eid = NULL;
1605 struct berval nbase = BER_BVNULL,
1606 realndn = BER_BVNULL;
1608 manageDSAit = get_manageDSAit( op );
1610 Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): "
1611 "base=\"%s\", filter=\"%s\", scope=%d,",
1612 op->o_req_ndn.bv_val,
1613 op->ors_filterstr.bv_val,
1615 Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, "
1616 "attributes to load: %s\n",
1619 op->ors_attrs == NULL ? "all" : "custom list" );
1621 if ( op->o_req_ndn.bv_len > BACKSQL_MAX_DN_LEN ) {
1622 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1623 "search base length (%ld) exceeds max length (%d)\n",
1624 op->o_req_ndn.bv_len, BACKSQL_MAX_DN_LEN, 0 );
1626 * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate
1627 * since it is impossible that such a long DN exists
1630 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1631 send_ldap_result( op, rs );
1635 sres = backsql_get_db_conn( op, &dbh );
1636 if ( sres != LDAP_SUCCESS ) {
1637 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1638 "could not get connection handle - exiting\n",
1641 rs->sr_text = sres == LDAP_OTHER ? "SQL-backend error" : NULL;
1642 send_ldap_result( op, rs );
1646 /* compute it anyway; root does not use it */
1647 stoptime = op->o_time + op->ors_tlimit;
1649 realndn = op->o_req_ndn;
1650 if ( backsql_api_dn2odbc( op, rs, &realndn ) ) {
1651 Debug( LDAP_DEBUG_TRACE, " backsql_search(\"%s\"): "
1652 "backsql_api_dn2odbc(\"%s\") failed\n",
1653 op->o_req_ndn.bv_val, realndn.bv_val, 0 );
1654 rs->sr_err = LDAP_OTHER;
1655 rs->sr_text = "SQL-backend error";
1656 send_ldap_result( op, rs );
1661 rs->sr_err = backsql_init_search( &bsi, &realndn,
1663 op->ors_slimit, op->ors_tlimit,
1664 stoptime, op->ors_filter,
1665 dbh, op, rs, op->ors_attrs,
1666 ( BACKSQL_ISF_GET_ID | BACKSQL_ISF_MUCK ) );
1667 if ( rs->sr_err != LDAP_SUCCESS ) {
1668 send_ldap_result( op, rs );
1672 bsi.bsi_n_candidates =
1673 ( op->ors_limit == NULL /* isroot == TRUE */ ? -2 :
1674 ( op->ors_limit->lms_s_unchecked == -1 ? -2 :
1675 ( op->ors_limit->lms_s_unchecked ) ) );
1677 switch ( bsi.bsi_scope ) {
1678 case LDAP_SCOPE_BASE:
1679 case BACKSQL_SCOPE_BASE_LIKE:
1681 * probably already found...
1683 bsi.bsi_id_list = &bsi.bsi_base_id;
1684 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
1687 case LDAP_SCOPE_SUBTREE:
1689 * if baseObject is defined, and if it is the root
1690 * of the search, add it to the candidate list
1692 if ( bi->sql_baseObject && BACKSQL_IS_BASEOBJECT_ID( &bsi.bsi_base_id.eid_id ) )
1694 bsi.bsi_id_list = &bsi.bsi_base_id;
1695 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
1702 * for each objectclass we try to construct query which gets IDs
1703 * of entries matching LDAP query filter and scope (or at least
1704 * candidates), and get the IDs
1706 avl_apply( bi->sql_oc_by_oc, backsql_oc_get_candidates,
1707 &bsi, BACKSQL_AVL_STOP, AVL_INORDER );
1710 if ( op->ors_limit != NULL /* isroot == FALSE */
1711 && op->ors_limit->lms_s_unchecked != -1
1712 && bsi.bsi_n_candidates == -1 )
1714 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1715 send_ldap_result( op, rs );
1720 * now we load candidate entries (only those attributes
1721 * mentioned in attrs and filter), test it against full filter
1722 * and then send to client; don't free entry_id if baseObject...
1724 for ( eid = bsi.bsi_id_list;
1726 eid = backsql_free_entryID( eid, eid == &bsi.bsi_base_id ? 0 : 1 ) )
1729 Attribute *a_hasSubordinate = NULL,
1730 *a_entryUUID = NULL,
1735 /* check for abandon */
1736 if ( op->o_abandon ) {
1740 /* check time limit */
1741 if ( op->ors_tlimit != SLAP_NO_LIMIT
1742 && slap_get_time() > stoptime )
1744 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
1745 rs->sr_ctrls = NULL;
1746 rs->sr_ref = rs->sr_v2ref;
1747 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
1749 send_ldap_result( op, rs );
1753 #ifdef BACKSQL_ARBITRARY_KEY
1754 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
1755 "for entry id=%s, oc_id=%ld, keyval=%s\n",
1756 eid->eid_id.bv_val, eid->eid_oc_id,
1757 eid->eid_keyval.bv_val );
1758 #else /* ! BACKSQL_ARBITRARY_KEY */
1759 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
1760 "for entry id=%ld, oc_id=%ld, keyval=%ld\n",
1761 eid->eid_id, eid->eid_oc_id, eid->eid_keyval );
1762 #endif /* ! BACKSQL_ARBITRARY_KEY */
1765 switch ( op->ors_scope ) {
1766 case LDAP_SCOPE_BASE:
1767 case BACKSQL_SCOPE_BASE_LIKE:
1768 if ( !dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
1773 case LDAP_SCOPE_ONE:
1775 struct berval rdn = eid->eid_ndn;
1777 rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
1778 if ( !dnIsOneLevelRDN( &rdn ) ) {
1784 #ifdef LDAP_SCOPE_SUBORDINATE
1785 case LDAP_SCOPE_SUBORDINATE:
1786 /* discard the baseObject entry */
1787 if ( dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
1791 #endif /* LDAP_SCOPE_SUBORDINATE */
1793 case LDAP_SCOPE_SUBTREE:
1794 /* FIXME: this should never fail... */
1795 if ( !dnIsSuffix( &eid->eid_ndn, &op->o_req_ndn ) ) {
1801 /* don't recollect baseObject ... */
1802 if ( BACKSQL_IS_BASEOBJECT_ID( &eid->eid_id ) ) {
1803 e = bi->sql_baseObject;
1806 bsi.bsi_e = &user_entry;
1807 rc = backsql_id2entry( &bsi, eid );
1808 if ( rc != LDAP_SUCCESS ) {
1809 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1810 "error %d in backsql_id2entry() "
1811 "- skipping\n", rc, 0, 0 );
1818 if ( !manageDSAit &&
1819 op->ors_scope != LDAP_SCOPE_BASE &&
1820 op->ors_scope != BACKSQL_SCOPE_BASE_LIKE &&
1821 is_entry_referral( e ) )
1825 refs = get_entry_referrals( op, e );
1827 backsql_srch_info bsi2 = { 0 };
1828 Entry user_entry2 = { 0 };
1830 /* retry with the full entry... */
1831 (void)backsql_init_search( &bsi2,
1837 bsi2.bsi_e = &user_entry2;
1838 rc = backsql_id2entry( &bsi2, eid );
1839 if ( rc == LDAP_SUCCESS ) {
1840 if ( is_entry_referral( &user_entry2 ) )
1842 refs = get_entry_referrals( op,
1844 } /* else: FIXME: inconsistency! */
1845 entry_clean( &user_entry2 );
1850 rs->sr_ref = referral_rewrite( refs,
1854 ber_bvarray_free( refs );
1857 if ( !rs->sr_ref ) {
1858 rs->sr_text = "bad_referral object";
1862 rs->sr_err = LDAP_REFERRAL;
1863 rs->sr_matched = user_entry.e_name.bv_val;
1864 send_search_reference( op, rs );
1866 ber_bvarray_free( rs->sr_ref );
1868 rs->sr_matched = NULL;
1869 rs->sr_entry = NULL;
1875 * We use this flag since we need to parse the filter
1876 * anyway; we should have used the frontend API function
1877 * filter_has_subordinates()
1879 if ( bsi.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) {
1880 rc = backsql_has_children( bi, dbh, &e->e_nname );
1883 case LDAP_COMPARE_TRUE:
1884 case LDAP_COMPARE_FALSE:
1885 a_hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE );
1886 if ( a_hasSubordinate != NULL ) {
1887 for ( ap = &user_entry.e_attrs;
1889 ap = &(*ap)->a_next );
1891 *ap = a_hasSubordinate;
1897 Debug(LDAP_DEBUG_TRACE,
1898 "backsql_search(): "
1899 "has_children failed( %d)\n",
1906 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYUUID ) {
1907 a_entryUUID = backsql_operational_entryUUID( bi, eid );
1908 if ( a_entryUUID != NULL ) {
1910 ap = &user_entry.e_attrs;
1913 for ( ; *ap; ap = &(*ap)->a_next );
1919 #ifdef BACKSQL_SYNCPROV
1920 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYCSN ) {
1921 a_entryCSN = backsql_operational_entryCSN( op );
1922 if ( a_entryCSN != NULL ) {
1924 ap = &user_entry.e_attrs;
1927 for ( ; *ap; ap = &(*ap)->a_next );
1932 #endif /* BACKSQL_SYNCPROV */
1934 if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE )
1936 rs->sr_attrs = op->ors_attrs;
1937 rs->sr_operational_attrs = NULL;
1939 if ( e == &user_entry ) {
1940 rs->sr_flags = REP_ENTRY_MODIFIABLE;
1942 sres = send_search_entry( op, rs );
1943 rs->sr_entry = NULL;
1944 rs->sr_attrs = NULL;
1945 rs->sr_operational_attrs = NULL;
1953 * FIXME: send_search_entry failed;
1957 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1958 "connection lost\n", 0, 0, 0 );
1964 entry_clean( &user_entry );
1967 if ( op->ors_slimit != SLAP_NO_LIMIT
1968 && rs->sr_nentries >= op->ors_slimit )
1970 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
1971 send_ldap_result( op, rs );
1977 /* in case we got here accidentally */
1978 entry_clean( &user_entry );
1980 if ( rs->sr_nentries > 0 ) {
1981 rs->sr_ref = rs->sr_v2ref;
1982 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
1986 rs->sr_err = bsi.bsi_status;
1988 send_ldap_result( op, rs );
1990 if ( rs->sr_v2ref ) {
1991 ber_bvarray_free( rs->sr_v2ref );
1992 rs->sr_v2ref = NULL;
1995 #ifdef BACKSQL_SYNCPROV
1997 Operation op2 = *op;
1998 SlapReply rs2 = { 0 };
2000 slap_callback cb = { 0 };
2002 op2.o_tag = LDAP_REQ_ADD;
2003 op2.o_bd = select_backend( &op->o_bd->be_nsuffix[0], 0, 0 );
2005 op2.o_callback = &cb;
2007 e.e_name = op->o_bd->be_suffix[0];
2008 e.e_nname = op->o_bd->be_nsuffix[0];
2010 cb.sc_response = slap_null_cb;
2012 op2.o_bd->be_add( &op2, &rs2 );
2014 #endif /* BACKSQL_SYNCPROV */
2017 if ( !BER_BVISNULL( &realndn ) && realndn.bv_val != op->o_req_ndn.bv_val ) {
2018 ch_free( realndn.bv_val );
2021 if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
2022 (void)backsql_free_entryID( &bsi.bsi_base_id, 0 );
2025 if ( bsi.bsi_attrs ) {
2026 ch_free( bsi.bsi_attrs );
2029 if ( !BER_BVISNULL( &nbase )
2030 && nbase.bv_val != op->o_req_ndn.bv_val )
2032 ch_free( nbase.bv_val );
2035 /* restore scope ... FIXME: this should be done before ANY
2036 * frontend call that uses op */
2037 if ( op->ors_scope == BACKSQL_SCOPE_BASE_LIKE ) {
2038 op->ors_scope = LDAP_SCOPE_BASE;
2041 Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n", 0, 0, 0 );
2045 /* return LDAP_SUCCESS IFF we can retrieve the specified entry.
2052 AttributeDescription *at,
2056 backsql_srch_info bsi;
2059 SlapReply rs = { 0 };
2060 AttributeName anlist[ 2 ];
2062 rc = backsql_get_db_conn( op, &dbh );
2068 anlist[ 0 ].an_name = at->ad_cname;
2069 anlist[ 0 ].an_desc = at;
2070 BER_BVZERO( &anlist[ 1 ].an_name );
2073 rc = backsql_init_search( &bsi,
2076 SLAP_NO_LIMIT, SLAP_NO_LIMIT, -1, NULL,
2077 dbh, op, &rs, at ? anlist : NULL,
2078 ( BACKSQL_ISF_GET_ID | BACKSQL_ISF_MUCK ) );
2079 if ( rc != LDAP_SUCCESS ) {
2083 bsi.bsi_e = ch_malloc( sizeof( Entry ) );
2084 rc = backsql_id2entry( &bsi, &bsi.bsi_base_id );
2086 if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
2087 (void)backsql_free_entryID( &bsi.bsi_base_id, 0 );
2090 if ( rc == LDAP_SUCCESS ) {
2092 #if 0 /* not supported at present */
2093 /* find attribute values */
2094 if ( is_entry_alias( bsi.bsi_e ) ) {
2095 Debug( LDAP_DEBUG_ACL,
2096 "<= backsql_entry_get: entry is an alias\n",
2098 rc = LDAP_ALIAS_PROBLEM;
2099 goto return_results;
2103 if ( is_entry_referral( bsi.bsi_e ) ) {
2104 Debug( LDAP_DEBUG_ACL,
2105 "<= backsql_entry_get: entry is a referral\n",
2108 goto return_results;
2111 if ( oc && !is_entry_objectclass( bsi.bsi_e, oc, 0 ) ) {
2112 Debug( LDAP_DEBUG_ACL,
2113 "<= backsql_entry_get: "
2114 "failed to find objectClass\n",
2116 rc = LDAP_NO_SUCH_ATTRIBUTE;
2117 goto return_results;
2124 if ( rc != LDAP_SUCCESS ) {
2126 entry_free( bsi.bsi_e );