2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1999-2009 The OpenLDAP Foundation.
5 * Portions Copyright 1999 Dmitry Kovalev.
6 * Portions Copyright 2002 Pierangelo Masarati.
7 * Portions Copyright 2004 Mark Adamson.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
19 * This work was initially developed by Dmitry Kovalev for inclusion
20 * by OpenLDAP Software. Additional significant contributors include
21 * Pierangelo Masarati and Mark Adamson.
27 #include <sys/types.h>
28 #include "ac/string.h"
33 #include "proto-sql.h"
35 static int backsql_process_filter( backsql_srch_info *bsi, Filter *f );
36 static int backsql_process_filter_eq( backsql_srch_info *bsi,
37 backsql_at_map_rec *at,
38 int casefold, struct berval *filter_value );
39 static int backsql_process_filter_like( backsql_srch_info *bsi,
40 backsql_at_map_rec *at,
41 int casefold, struct berval *filter_value );
42 static int backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f,
43 backsql_at_map_rec *at );
45 /* For LDAP_CONTROL_PAGEDRESULTS, a 32 bit cookie is available to keep track of
46 the state of paged results. The ldap_entries.id and oc_map_id values of the
47 last entry returned are used as the cookie, so 6 bits are used for the OC id
48 and the other 26 for ldap_entries ID number. If your max(oc_map_id) is more
49 than 63, you will need to steal more bits from ldap_entries ID number and
50 put them into the OC ID part of the cookie. */
52 /* NOTE: not supported when BACKSQL_ARBITRARY_KEY is defined */
53 #ifndef BACKSQL_ARBITRARY_KEY
54 #define SQL_TO_PAGECOOKIE(id, oc) (((id) << 6 ) | ((oc) & 0x3F))
55 #define PAGECOOKIE_TO_SQL_ID(pc) ((pc) >> 6)
56 #define PAGECOOKIE_TO_SQL_OC(pc) ((pc) & 0x3F)
58 static int parse_paged_cookie( Operation *op, SlapReply *rs );
60 static void send_paged_response(
64 #endif /* ! BACKSQL_ARBITRARY_KEY */
67 backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad )
70 AttributeName *an = NULL;
72 if ( bsi->bsi_attrs == NULL ) {
77 * clear the list (retrieve all attrs)
80 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs, bsi->bsi_op->o_tmpmemctx );
81 bsi->bsi_attrs = NULL;
82 bsi->bsi_flags |= BSQL_SF_ALL_ATTRS;
87 if ( slap_ad_is_binary( ad ) ) {
88 ad = ad->ad_type->sat_ad;
91 for ( ; !BER_BVISNULL( &bsi->bsi_attrs[ n_attrs ].an_name ); n_attrs++ ) {
92 an = &bsi->bsi_attrs[ n_attrs ];
94 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
95 "attribute \"%s\" is in list\n",
96 an->an_name.bv_val, 0, 0 );
98 * We can live with strcmp because the attribute
99 * list has been normalized before calling be_search
101 if ( !BACKSQL_NCMP( &an->an_name, &ad->ad_cname ) ) {
106 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
107 "adding \"%s\" to list\n", ad->ad_cname.bv_val, 0, 0 );
109 an = (AttributeName *)bsi->bsi_op->o_tmprealloc( bsi->bsi_attrs,
110 sizeof( AttributeName ) * ( n_attrs + 2 ),
111 bsi->bsi_op->o_tmpmemctx );
116 an[ n_attrs ].an_name = ad->ad_cname;
117 an[ n_attrs ].an_desc = ad;
118 BER_BVZERO( &an[ n_attrs + 1 ].an_name );
126 * Initializes the search structure.
128 * If get_base_id != 0, the field bsi_base_id is filled
129 * with the entryID of bsi_base_ndn; it must be freed
130 * by backsql_free_entryID() when no longer required.
132 * NOTE: base must be normalized
136 backsql_srch_info *bsi,
137 struct berval *nbase,
144 AttributeName *attrs,
147 backsql_info *bi = (backsql_info *)op->o_bd->be_private;
148 int rc = LDAP_SUCCESS;
150 bsi->bsi_base_ndn = nbase;
151 bsi->bsi_use_subtree_shortcut = 0;
152 BER_BVZERO( &bsi->bsi_base_id.eid_dn );
153 BER_BVZERO( &bsi->bsi_base_id.eid_ndn );
154 bsi->bsi_scope = scope;
155 bsi->bsi_filter = filter;
159 bsi->bsi_flags = BSQL_SF_NONE;
161 bsi->bsi_attrs = NULL;
163 if ( BACKSQL_FETCH_ALL_ATTRS( bi ) ) {
165 * if requested, simply try to fetch all attributes
167 bsi->bsi_flags |= BSQL_SF_ALL_ATTRS;
170 if ( BACKSQL_FETCH_ALL_USERATTRS( bi ) ) {
171 bsi->bsi_flags |= BSQL_SF_ALL_USER;
173 } else if ( BACKSQL_FETCH_ALL_OPATTRS( bi ) ) {
174 bsi->bsi_flags |= BSQL_SF_ALL_OPER;
177 if ( attrs == NULL ) {
178 /* NULL means all user attributes */
179 bsi->bsi_flags |= BSQL_SF_ALL_USER;
185 bsi->bsi_attrs = (AttributeName *)bsi->bsi_op->o_tmpalloc(
186 sizeof( AttributeName ),
187 bsi->bsi_op->o_tmpmemctx );
188 BER_BVZERO( &bsi->bsi_attrs[ 0 ].an_name );
190 for ( p = attrs; !BER_BVISNULL( &p->an_name ); p++ ) {
191 if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_user_attrs ) == 0 ) {
193 bsi->bsi_flags |= BSQL_SF_ALL_USER;
195 /* if all attrs are requested, there's
196 * no need to continue */
197 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
198 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
199 bsi->bsi_op->o_tmpmemctx );
200 bsi->bsi_attrs = NULL;
205 } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_operational_attrs ) == 0 ) {
207 bsi->bsi_flags |= BSQL_SF_ALL_OPER;
209 /* if all attrs are requested, there's
210 * no need to continue */
211 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
212 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
213 bsi->bsi_op->o_tmpmemctx );
214 bsi->bsi_attrs = NULL;
219 } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_no_attrs ) == 0 ) {
223 } else if ( p->an_desc == slap_schema.si_ad_objectClass ) {
227 backsql_attrlist_add( bsi, p->an_desc );
230 if ( got_oc == 0 && !( bsi->bsi_flags & BSQL_SF_ALL_USER ) ) {
231 /* add objectClass if not present,
232 * because it is required to understand
233 * if an entry is a referral, an alias
235 backsql_attrlist_add( bsi, slap_schema.si_ad_objectClass );
239 if ( !BSQL_ISF_ALL_ATTRS( bsi ) && bi->sql_anlist ) {
242 /* use hints if available */
243 for ( p = bi->sql_anlist; !BER_BVISNULL( &p->an_name ); p++ ) {
244 if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_user_attrs ) == 0 ) {
246 bsi->bsi_flags |= BSQL_SF_ALL_USER;
248 /* if all attrs are requested, there's
249 * no need to continue */
250 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
251 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
252 bsi->bsi_op->o_tmpmemctx );
253 bsi->bsi_attrs = NULL;
258 } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_operational_attrs ) == 0 ) {
260 bsi->bsi_flags |= BSQL_SF_ALL_OPER;
262 /* if all attrs are requested, there's
263 * no need to continue */
264 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
265 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
266 bsi->bsi_op->o_tmpmemctx );
267 bsi->bsi_attrs = NULL;
273 backsql_attrlist_add( bsi, p->an_desc );
279 bsi->bsi_id_list = NULL;
280 bsi->bsi_id_listtail = &bsi->bsi_id_list;
281 bsi->bsi_n_candidates = 0;
282 bsi->bsi_stoptime = stoptime;
283 BER_BVZERO( &bsi->bsi_sel.bb_val );
284 bsi->bsi_sel.bb_len = 0;
285 BER_BVZERO( &bsi->bsi_from.bb_val );
286 bsi->bsi_from.bb_len = 0;
287 BER_BVZERO( &bsi->bsi_join_where.bb_val );
288 bsi->bsi_join_where.bb_len = 0;
289 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
290 bsi->bsi_flt_where.bb_len = 0;
291 bsi->bsi_filter_oc = NULL;
293 if ( BACKSQL_IS_GET_ID( flags ) ) {
294 int matched = BACKSQL_IS_MATCHED( flags );
295 int getentry = BACKSQL_IS_GET_ENTRY( flags );
298 assert( op->o_bd->be_private != NULL );
300 rc = backsql_dn2id( op, rs, dbh, nbase, &bsi->bsi_base_id,
303 /* the entry is collected either if requested for by getentry
304 * or if get noSuchObject and requested to climb the tree,
305 * so that a matchedDN or a referral can be returned */
306 if ( ( rc == LDAP_NO_SUCH_OBJECT && matched ) || getentry ) {
307 if ( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) ) {
308 assert( bsi->bsi_e != NULL );
310 if ( dn_match( nbase, &bsi->bsi_base_id.eid_ndn ) )
316 * let's see if it is a referral and, in case, get it
318 backsql_attrlist_add( bsi, slap_schema.si_ad_ref );
319 rc = backsql_id2entry( bsi, &bsi->bsi_base_id );
320 if ( rc == LDAP_SUCCESS ) {
321 if ( is_entry_referral( bsi->bsi_e ) )
323 BerVarray erefs = get_entry_referrals( op, bsi->bsi_e );
325 rc = rs->sr_err = LDAP_REFERRAL;
326 rs->sr_ref = referral_rewrite( erefs,
327 &bsi->bsi_e->e_nname,
330 ber_bvarray_free( erefs );
333 rc = rs->sr_err = LDAP_OTHER;
334 rs->sr_text = "bad referral object";
337 } else if ( !gotit ) {
338 rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
347 if ( gotit && BACKSQL_IS_GET_OC( flags ) ) {
348 bsi->bsi_base_id.eid_oc = backsql_id2oc( bi,
349 bsi->bsi_base_id.eid_oc_id );
350 if ( bsi->bsi_base_id.eid_oc == NULL ) {
352 backsql_free_entryID( &bsi->bsi_base_id, 1,
354 rc = rs->sr_err = LDAP_OTHER;
359 bsi->bsi_status = rc;
367 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
368 bsi->bsi_op->o_tmpmemctx );
376 backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op )
384 backsql_strfcat_x( &bsi->bsi_flt_where,
385 bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */ );
388 res = backsql_process_filter( bsi, f );
391 * TimesTen : If the query has no answers,
392 * don't bother to run the query.
403 case LDAP_FILTER_AND:
404 backsql_strfcat_x( &bsi->bsi_flt_where,
405 bsi->bsi_op->o_tmpmemctx, "l",
406 (ber_len_t)STRLENOF( " AND " ),
411 backsql_strfcat_x( &bsi->bsi_flt_where,
412 bsi->bsi_op->o_tmpmemctx, "l",
413 (ber_len_t)STRLENOF( " OR " ),
419 backsql_strfcat_x( &bsi->bsi_flt_where,
420 bsi->bsi_op->o_tmpmemctx, "c", /* ( */ ')' );
426 backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f,
427 backsql_at_map_rec *at )
429 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
437 /* always uppercase strings by now */
438 #ifdef BACKSQL_UPPERCASE_FILTER
439 if ( f->f_sub_desc->ad_type->sat_substr &&
440 SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
441 bi->sql_caseIgnoreMatch ) )
442 #endif /* BACKSQL_UPPERCASE_FILTER */
447 if ( f->f_sub_desc->ad_type->sat_substr &&
448 SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
449 bi->sql_telephoneNumberMatch ) )
456 * to check for matching telephone numbers
457 * with intermixed chars, e.g. val='1234'
460 * val LIKE '%1%2%3%4%'
464 if ( f->f_sub_initial.bv_val ) {
465 bv.bv_len += f->f_sub_initial.bv_len;
467 if ( f->f_sub_any != NULL ) {
468 for ( a = 0; f->f_sub_any[ a ].bv_val != NULL; a++ ) {
469 bv.bv_len += f->f_sub_any[ a ].bv_len;
472 if ( f->f_sub_final.bv_val ) {
473 bv.bv_len += f->f_sub_final.bv_len;
475 bv.bv_len = 2 * bv.bv_len - 1;
476 bv.bv_val = ch_malloc( bv.bv_len + 1 );
479 if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
480 bv.bv_val[ s ] = f->f_sub_initial.bv_val[ 0 ];
481 for ( i = 1; i < f->f_sub_initial.bv_len; i++ ) {
482 bv.bv_val[ s + 2 * i - 1 ] = '%';
483 bv.bv_val[ s + 2 * i ] = f->f_sub_initial.bv_val[ i ];
485 bv.bv_val[ s + 2 * i - 1 ] = '%';
489 if ( f->f_sub_any != NULL ) {
490 for ( a = 0; !BER_BVISNULL( &f->f_sub_any[ a ] ); a++ ) {
491 bv.bv_val[ s ] = f->f_sub_any[ a ].bv_val[ 0 ];
492 for ( i = 1; i < f->f_sub_any[ a ].bv_len; i++ ) {
493 bv.bv_val[ s + 2 * i - 1 ] = '%';
494 bv.bv_val[ s + 2 * i ] = f->f_sub_any[ a ].bv_val[ i ];
496 bv.bv_val[ s + 2 * i - 1 ] = '%';
501 if ( !BER_BVISNULL( &f->f_sub_final ) ) {
502 bv.bv_val[ s ] = f->f_sub_final.bv_val[ 0 ];
503 for ( i = 1; i < f->f_sub_final.bv_len; i++ ) {
504 bv.bv_val[ s + 2 * i - 1 ] = '%';
505 bv.bv_val[ s + 2 * i ] = f->f_sub_final.bv_val[ i ];
507 bv.bv_val[ s + 2 * i - 1 ] = '%';
511 bv.bv_val[ s - 1 ] = '\0';
513 (void)backsql_process_filter_like( bsi, at, casefold, &bv );
514 ch_free( bv.bv_val );
520 * When dealing with case-sensitive strings
521 * we may omit normalization; however, normalized
522 * SQL filters are more liberal.
525 backsql_strfcat_x( &bsi->bsi_flt_where,
526 bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */ );
529 Debug( LDAP_DEBUG_TRACE, "backsql_process_sub_filter(%s):\n",
530 at->bam_ad->ad_cname.bv_val, 0, 0 );
531 Debug(LDAP_DEBUG_TRACE, " expr: '%s%s%s'\n", at->bam_sel_expr.bv_val,
532 at->bam_sel_expr_u.bv_val ? "' '" : "",
533 at->bam_sel_expr_u.bv_val ? at->bam_sel_expr_u.bv_val : "" );
534 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
536 * If a pre-upper-cased version of the column
537 * or a precompiled upper function exists, use it
539 backsql_strfcat_x( &bsi->bsi_flt_where,
540 bsi->bsi_op->o_tmpmemctx,
543 (ber_len_t)STRLENOF( " LIKE '" ),
547 backsql_strfcat_x( &bsi->bsi_flt_where,
548 bsi->bsi_op->o_tmpmemctx,
551 (ber_len_t)STRLENOF( " LIKE '" ), " LIKE '" );
554 if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
558 Debug( LDAP_DEBUG_TRACE,
559 "==>backsql_process_sub_filter(%s): "
560 "sub_initial=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
561 f->f_sub_initial.bv_val, 0 );
562 #endif /* BACKSQL_TRACE */
564 start = bsi->bsi_flt_where.bb_val.bv_len;
565 backsql_strfcat_x( &bsi->bsi_flt_where,
566 bsi->bsi_op->o_tmpmemctx,
569 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
570 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
574 backsql_strfcat_x( &bsi->bsi_flt_where,
575 bsi->bsi_op->o_tmpmemctx,
578 if ( f->f_sub_any != NULL ) {
579 for ( i = 0; !BER_BVISNULL( &f->f_sub_any[ i ] ); i++ ) {
583 Debug( LDAP_DEBUG_TRACE,
584 "==>backsql_process_sub_filter(%s): "
585 "sub_any[%d]=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
586 i, f->f_sub_any[ i ].bv_val );
587 #endif /* BACKSQL_TRACE */
589 start = bsi->bsi_flt_where.bb_val.bv_len;
590 backsql_strfcat_x( &bsi->bsi_flt_where,
591 bsi->bsi_op->o_tmpmemctx,
595 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
597 * Note: toupper('%') = '%'
599 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
604 if ( !BER_BVISNULL( &f->f_sub_final ) ) {
608 Debug( LDAP_DEBUG_TRACE,
609 "==>backsql_process_sub_filter(%s): "
610 "sub_final=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
611 f->f_sub_final.bv_val, 0 );
612 #endif /* BACKSQL_TRACE */
614 start = bsi->bsi_flt_where.bb_val.bv_len;
615 backsql_strfcat_x( &bsi->bsi_flt_where,
616 bsi->bsi_op->o_tmpmemctx,
619 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
620 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
624 backsql_strfcat_x( &bsi->bsi_flt_where,
625 bsi->bsi_op->o_tmpmemctx,
627 (ber_len_t)STRLENOF( /* (' */ "')" ), /* (' */ "')" );
633 backsql_merge_from_tbls( backsql_srch_info *bsi, struct berval *from_tbls )
635 if ( BER_BVISNULL( from_tbls ) ) {
639 if ( !BER_BVISNULL( &bsi->bsi_from.bb_val ) ) {
643 ber_dupbv_x( &tmp, from_tbls, bsi->bsi_op->o_tmpmemctx );
645 for ( start = tmp.bv_val, end = strchr( start, ',' ); start; ) {
650 if ( strstr( bsi->bsi_from.bb_val.bv_val, start) == NULL )
652 backsql_strfcat_x( &bsi->bsi_from,
653 bsi->bsi_op->o_tmpmemctx,
658 /* in case there are spaces after the comma... */
659 for ( start = &end[1]; isspace( start[0] ); start++ );
661 end = strchr( start, ',' );
670 bsi->bsi_op->o_tmpfree( tmp.bv_val, bsi->bsi_op->o_tmpmemctx );
673 backsql_strfcat_x( &bsi->bsi_from,
674 bsi->bsi_op->o_tmpmemctx,
682 backsql_process_filter( backsql_srch_info *bsi, Filter *f )
684 backsql_at_map_rec **vat = NULL;
685 AttributeDescription *ad = NULL;
690 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter()\n", 0, 0, 0 );
691 if ( f->f_choice == SLAPD_FILTER_COMPUTED ) {
695 switch ( f->f_result ) {
696 case LDAP_COMPARE_TRUE:
697 BER_BVSTR( &flt, "10=10" );
701 case LDAP_COMPARE_FALSE:
702 BER_BVSTR( &flt, "11=0" );
706 case SLAPD_COMPARE_UNDEFINED:
707 BER_BVSTR( &flt, "12=0" );
716 Debug( LDAP_DEBUG_TRACE, "backsql_process_filter(): "
717 "filter computed (%s)\n", msg, 0, 0 );
718 backsql_strfcat_x( &bsi->bsi_flt_where,
719 bsi->bsi_op->o_tmpmemctx, "b", &flt );
724 switch( f->f_choice ) {
726 rc = backsql_process_filter_list( bsi, f->f_or,
731 case LDAP_FILTER_AND:
732 rc = backsql_process_filter_list( bsi, f->f_and,
737 case LDAP_FILTER_NOT:
738 backsql_strfcat_x( &bsi->bsi_flt_where,
739 bsi->bsi_op->o_tmpmemctx,
741 (ber_len_t)STRLENOF( "NOT (" /* ) */ ),
743 rc = backsql_process_filter( bsi, f->f_not );
744 backsql_strfcat_x( &bsi->bsi_flt_where,
745 bsi->bsi_op->o_tmpmemctx,
750 case LDAP_FILTER_PRESENT:
754 case LDAP_FILTER_EXT:
755 ad = f->f_mra->ma_desc;
756 if ( f->f_mr_dnattrs ) {
758 * if dn attrs filtering is requested, better return
759 * success and let test_filter() deal with candidate
760 * selection; otherwise we'd need to set conditions
761 * on the contents of the DN, e.g. "SELECT ... FROM
762 * ldap_entries AS attributeName WHERE attributeName.dn
763 * like '%attributeName=value%'"
765 backsql_strfcat_x( &bsi->bsi_flt_where,
766 bsi->bsi_op->o_tmpmemctx,
768 (ber_len_t)STRLENOF( "1=1" ), "1=1" );
769 bsi->bsi_status = LDAP_SUCCESS;
790 * Turn structuralObjectClass into objectClass
792 if ( ad == slap_schema.si_ad_objectClass
793 || ad == slap_schema.si_ad_structuralObjectClass )
796 * If the filter is LDAP_FILTER_PRESENT, then it's done;
797 * otherwise, let's see if we are lucky: filtering
798 * for "structural" objectclass or ancestor...
800 switch ( f->f_choice ) {
801 case LDAP_FILTER_EQUALITY:
803 ObjectClass *oc = oc_bvfind( &f->f_av_value );
806 Debug( LDAP_DEBUG_TRACE,
807 "backsql_process_filter(): "
808 "unknown objectClass \"%s\" "
810 f->f_av_value.bv_val, 0, 0 );
811 bsi->bsi_status = LDAP_OTHER;
817 * "structural" objectClass inheritance:
818 * - a search for "person" will also return
820 * - a search for "top" will return everything
822 if ( is_object_subclass( oc, bsi->bsi_oc->bom_oc ) ) {
823 static struct berval ldap_entry_objclasses = BER_BVC( "ldap_entry_objclasses" );
825 backsql_merge_from_tbls( bsi, &ldap_entry_objclasses );
827 backsql_strfcat_x( &bsi->bsi_flt_where,
828 bsi->bsi_op->o_tmpmemctx,
830 (ber_len_t)STRLENOF( "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */ ),
831 "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */,
832 &bsi->bsi_oc->bom_oc->soc_cname,
833 (ber_len_t)STRLENOF( /* ((' */ "'))" ),
835 bsi->bsi_status = LDAP_SUCCESS;
843 case LDAP_FILTER_PRESENT:
844 backsql_strfcat_x( &bsi->bsi_flt_where,
845 bsi->bsi_op->o_tmpmemctx,
847 (ber_len_t)STRLENOF( "3=3" ), "3=3" );
848 bsi->bsi_status = LDAP_SUCCESS;
852 /* FIXME: LDAP_FILTER_EXT? */
855 Debug( LDAP_DEBUG_TRACE,
856 "backsql_process_filter(): "
857 "illegal/unhandled filter "
858 "on objectClass attribute",
860 bsi->bsi_status = LDAP_OTHER;
865 } else if ( ad == slap_schema.si_ad_entryUUID ) {
867 #ifdef BACKSQL_ARBITRARY_KEY
868 struct berval keyval;
869 #else /* ! BACKSQL_ARBITRARY_KEY */
870 unsigned long keyval;
871 char keyvalbuf[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
872 #endif /* ! BACKSQL_ARBITRARY_KEY */
874 switch ( f->f_choice ) {
875 case LDAP_FILTER_EQUALITY:
876 backsql_entryUUID_decode( &f->f_av_value, &oc_id, &keyval );
878 if ( oc_id != bsi->bsi_oc->bom_id ) {
879 bsi->bsi_status = LDAP_SUCCESS;
884 #ifdef BACKSQL_ARBITRARY_KEY
885 backsql_strfcat_x( &bsi->bsi_flt_where,
886 bsi->bsi_op->o_tmpmemctx,
888 &bsi->bsi_oc->bom_keytbl, '.',
889 &bsi->bsi_oc->bom_keycol,
890 STRLENOF( " LIKE '" ), " LIKE '",
892 #else /* ! BACKSQL_ARBITRARY_KEY */
893 snprintf( keyvalbuf, sizeof( keyvalbuf ), "%lu", keyval );
894 backsql_strfcat_x( &bsi->bsi_flt_where,
895 bsi->bsi_op->o_tmpmemctx,
897 &bsi->bsi_oc->bom_keytbl, '.',
898 &bsi->bsi_oc->bom_keycol, '=', keyvalbuf );
899 #endif /* ! BACKSQL_ARBITRARY_KEY */
902 case LDAP_FILTER_PRESENT:
903 backsql_strfcat_x( &bsi->bsi_flt_where,
904 bsi->bsi_op->o_tmpmemctx,
906 (ber_len_t)STRLENOF( "4=4" ), "4=4" );
914 bsi->bsi_flags |= BSQL_SF_FILTER_ENTRYUUID;
918 #ifdef BACKSQL_SYNCPROV
919 } else if ( ad == slap_schema.si_ad_entryCSN ) {
921 * support for syncrepl as producer...
924 if ( !bsi->bsi_op->o_sync ) {
925 /* unsupported at present... */
926 bsi->bsi_status = LDAP_OTHER;
932 bsi->bsi_flags |= ( BSQL_SF_FILTER_ENTRYCSN | BSQL_SF_RETURN_ENTRYUUID);
934 /* if doing a syncrepl, try to return as much as possible,
935 * and always match the filter */
936 backsql_strfcat_x( &bsi->bsi_flt_where,
937 bsi->bsi_op->o_tmpmemctx,
939 (ber_len_t)STRLENOF( "5=5" ), "5=5" );
941 /* save for later use in operational attributes */
942 /* FIXME: saves only the first occurrence, because
943 * the filter during updates is written as
944 * "(&(entryCSN<={contextCSN})(entryCSN>={oldContextCSN})({filter}))"
945 * so we want our fake entryCSN to match the greatest
948 if ( bsi->bsi_op->o_private == NULL ) {
949 bsi->bsi_op->o_private = &f->f_av_value;
951 bsi->bsi_status = LDAP_SUCCESS;
955 #endif /* BACKSQL_SYNCPROV */
957 } else if ( ad == slap_schema.si_ad_hasSubordinates || ad == NULL ) {
959 * FIXME: this is not robust; e.g. a filter
960 * '(!(hasSubordinates=TRUE))' fails because
961 * in SQL it would read 'NOT (1=1)' instead
963 * Note however that hasSubordinates is boolean,
964 * so a more appropriate filter would be
965 * '(hasSubordinates=FALSE)'
967 * A more robust search for hasSubordinates
968 * would * require joining the ldap_entries table
969 * selecting if there are descendants of the
972 backsql_strfcat_x( &bsi->bsi_flt_where,
973 bsi->bsi_op->o_tmpmemctx,
975 (ber_len_t)STRLENOF( "6=6" ), "6=6" );
976 if ( ad == slap_schema.si_ad_hasSubordinates ) {
978 * instruct candidate selection algorithm
979 * and attribute list to try to detect
980 * if an entry has subordinates
982 bsi->bsi_flags |= BSQL_SF_FILTER_HASSUBORDINATE;
986 * clear attributes to fetch, to require ALL
987 * and try extended match on all attributes
989 backsql_attrlist_add( bsi, NULL );
996 * attribute inheritance:
998 if ( backsql_supad2at( bsi->bsi_oc, ad, &vat ) ) {
999 bsi->bsi_status = LDAP_OTHER;
1004 if ( vat == NULL ) {
1005 /* search anyway; other parts of the filter
1007 backsql_strfcat_x( &bsi->bsi_flt_where,
1008 bsi->bsi_op->o_tmpmemctx,
1010 (ber_len_t)STRLENOF( "7=7" ), "7=7" );
1011 bsi->bsi_status = LDAP_SUCCESS;
1016 /* if required, open extra level of parens */
1018 if ( vat[0]->bam_next || vat[1] ) {
1019 backsql_strfcat_x( &bsi->bsi_flt_where,
1020 bsi->bsi_op->o_tmpmemctx,
1028 if ( backsql_process_filter_attr( bsi, f, vat[i] ) == -1 ) {
1032 /* if more definitions of the same attr, apply */
1033 if ( vat[i]->bam_next ) {
1034 backsql_strfcat_x( &bsi->bsi_flt_where,
1035 bsi->bsi_op->o_tmpmemctx,
1037 STRLENOF( " OR " ), " OR " );
1038 vat[i] = vat[i]->bam_next;
1042 /* if more descendants of the same attr, apply */
1045 backsql_strfcat_x( &bsi->bsi_flt_where,
1046 bsi->bsi_op->o_tmpmemctx,
1048 STRLENOF( " OR " ), " OR " );
1052 /* if needed, close extra level of parens */
1054 backsql_strfcat_x( &bsi->bsi_flt_where,
1055 bsi->bsi_op->o_tmpmemctx,
1066 Debug( LDAP_DEBUG_TRACE,
1067 "<==backsql_process_filter() %s\n",
1068 rc == 1 ? "succeeded" : "failed", 0, 0);
1074 backsql_process_filter_eq( backsql_srch_info *bsi, backsql_at_map_rec *at,
1075 int casefold, struct berval *filter_value )
1078 * maybe we should check type of at->sel_expr here somehow,
1079 * to know whether upper_func is applicable, but for now
1080 * upper_func stuff is made for Oracle, where UPPER is
1081 * safely applicable to NUMBER etc.
1083 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1086 backsql_strfcat_x( &bsi->bsi_flt_where,
1087 bsi->bsi_op->o_tmpmemctx,
1090 &at->bam_sel_expr_u,
1091 (ber_len_t)STRLENOF( "='" ),
1094 start = bsi->bsi_flt_where.bb_val.bv_len;
1096 backsql_strfcat_x( &bsi->bsi_flt_where,
1097 bsi->bsi_op->o_tmpmemctx,
1100 (ber_len_t)STRLENOF( /* (' */ "')" ),
1103 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1106 backsql_strfcat_x( &bsi->bsi_flt_where,
1107 bsi->bsi_op->o_tmpmemctx,
1111 (ber_len_t)STRLENOF( "='" ), "='",
1113 (ber_len_t)STRLENOF( /* (' */ "')" ),
1121 backsql_process_filter_like( backsql_srch_info *bsi, backsql_at_map_rec *at,
1122 int casefold, struct berval *filter_value )
1125 * maybe we should check type of at->sel_expr here somehow,
1126 * to know whether upper_func is applicable, but for now
1127 * upper_func stuff is made for Oracle, where UPPER is
1128 * safely applicable to NUMBER etc.
1130 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1133 backsql_strfcat_x( &bsi->bsi_flt_where,
1134 bsi->bsi_op->o_tmpmemctx,
1137 &at->bam_sel_expr_u,
1138 (ber_len_t)STRLENOF( " LIKE '%" ),
1141 start = bsi->bsi_flt_where.bb_val.bv_len;
1143 backsql_strfcat_x( &bsi->bsi_flt_where,
1144 bsi->bsi_op->o_tmpmemctx,
1147 (ber_len_t)STRLENOF( /* (' */ "%')" ),
1150 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1153 backsql_strfcat_x( &bsi->bsi_flt_where,
1154 bsi->bsi_op->o_tmpmemctx,
1158 (ber_len_t)STRLENOF( " LIKE '%" ),
1161 (ber_len_t)STRLENOF( /* (' */ "%')" ),
1169 backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f, backsql_at_map_rec *at )
1171 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1173 struct berval *filter_value = NULL;
1174 MatchingRule *matching_rule = NULL;
1175 struct berval ordering = BER_BVC("<=");
1177 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter_attr(%s)\n",
1178 at->bam_ad->ad_cname.bv_val, 0, 0 );
1181 * need to add this attribute to list of attrs to load,
1182 * so that we can do test_filter() later
1184 backsql_attrlist_add( bsi, at->bam_ad );
1186 backsql_merge_from_tbls( bsi, &at->bam_from_tbls );
1188 if ( !BER_BVISNULL( &at->bam_join_where )
1189 && strstr( bsi->bsi_join_where.bb_val.bv_val,
1190 at->bam_join_where.bv_val ) == NULL )
1192 backsql_strfcat_x( &bsi->bsi_join_where,
1193 bsi->bsi_op->o_tmpmemctx,
1195 (ber_len_t)STRLENOF( " AND " ), " AND ",
1196 &at->bam_join_where );
1199 switch ( f->f_choice ) {
1200 case LDAP_FILTER_EQUALITY:
1201 filter_value = &f->f_av_value;
1202 matching_rule = at->bam_ad->ad_type->sat_equality;
1204 goto equality_match;
1206 /* fail over into next case */
1208 case LDAP_FILTER_EXT:
1209 filter_value = &f->f_mra->ma_value;
1210 matching_rule = f->f_mr_rule;
1213 /* always uppercase strings by now */
1214 #ifdef BACKSQL_UPPERCASE_FILTER
1215 if ( SLAP_MR_ASSOCIATED( matching_rule,
1216 bi->sql_caseIgnoreMatch ) )
1217 #endif /* BACKSQL_UPPERCASE_FILTER */
1222 /* FIXME: directoryString filtering should use a similar
1223 * approach to deal with non-prettified values like
1224 * " A non prettified value ", by using a LIKE
1225 * filter with all whitespaces collapsed to a single '%' */
1226 if ( SLAP_MR_ASSOCIATED( matching_rule,
1227 bi->sql_telephoneNumberMatch ) )
1233 * to check for matching telephone numbers
1234 * with intermized chars, e.g. val='1234'
1237 * val LIKE '%1%2%3%4%'
1240 bv.bv_len = 2 * filter_value->bv_len - 1;
1241 bv.bv_val = ch_malloc( bv.bv_len + 1 );
1243 bv.bv_val[ 0 ] = filter_value->bv_val[ 0 ];
1244 for ( i = 1; i < filter_value->bv_len; i++ ) {
1245 bv.bv_val[ 2 * i - 1 ] = '%';
1246 bv.bv_val[ 2 * i ] = filter_value->bv_val[ i ];
1248 bv.bv_val[ 2 * i - 1 ] = '\0';
1250 (void)backsql_process_filter_like( bsi, at, casefold, &bv );
1251 ch_free( bv.bv_val );
1256 /* NOTE: this is required by objectClass inheritance
1257 * and auxiliary objectClass use in filters for slightly
1258 * more efficient candidate selection. */
1259 /* FIXME: a bit too many specializations to deal with
1260 * very specific cases... */
1261 if ( at->bam_ad == slap_schema.si_ad_objectClass
1262 || at->bam_ad == slap_schema.si_ad_structuralObjectClass )
1264 backsql_strfcat_x( &bsi->bsi_flt_where,
1265 bsi->bsi_op->o_tmpmemctx,
1267 (ber_len_t)STRLENOF( "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */ ),
1268 "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */,
1270 (ber_len_t)STRLENOF( /* (' */ "')" ),
1276 * maybe we should check type of at->sel_expr here somehow,
1277 * to know whether upper_func is applicable, but for now
1278 * upper_func stuff is made for Oracle, where UPPER is
1279 * safely applicable to NUMBER etc.
1281 (void)backsql_process_filter_eq( bsi, at, casefold, filter_value );
1284 case LDAP_FILTER_GE:
1285 ordering.bv_val = ">=";
1287 /* fall thru to next case */
1289 case LDAP_FILTER_LE:
1290 filter_value = &f->f_av_value;
1292 /* always uppercase strings by now */
1293 #ifdef BACKSQL_UPPERCASE_FILTER
1294 if ( at->bam_ad->ad_type->sat_ordering &&
1295 SLAP_MR_ASSOCIATED( at->bam_ad->ad_type->sat_ordering,
1296 bi->sql_caseIgnoreMatch ) )
1297 #endif /* BACKSQL_UPPERCASE_FILTER */
1303 * FIXME: should we uppercase the operands?
1305 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1308 backsql_strfcat_x( &bsi->bsi_flt_where,
1309 bsi->bsi_op->o_tmpmemctx,
1312 &at->bam_sel_expr_u,
1316 start = bsi->bsi_flt_where.bb_val.bv_len;
1318 backsql_strfcat_x( &bsi->bsi_flt_where,
1319 bsi->bsi_op->o_tmpmemctx,
1322 (ber_len_t)STRLENOF( /* (' */ "')" ),
1325 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1328 backsql_strfcat_x( &bsi->bsi_flt_where,
1329 bsi->bsi_op->o_tmpmemctx,
1336 (ber_len_t)STRLENOF( /* (' */ "')" ),
1341 case LDAP_FILTER_PRESENT:
1342 backsql_strfcat_x( &bsi->bsi_flt_where,
1343 bsi->bsi_op->o_tmpmemctx,
1345 (ber_len_t)STRLENOF( "NOT (" /* ) */),
1348 (ber_len_t)STRLENOF( /* ( */ " IS NULL)" ),
1349 /* ( */ " IS NULL)" );
1352 case LDAP_FILTER_SUBSTRINGS:
1353 backsql_process_sub_filter( bsi, f, at );
1356 case LDAP_FILTER_APPROX:
1357 /* we do our best */
1360 * maybe we should check type of at->sel_expr here somehow,
1361 * to know whether upper_func is applicable, but for now
1362 * upper_func stuff is made for Oracle, where UPPER is
1363 * safely applicable to NUMBER etc.
1365 (void)backsql_process_filter_like( bsi, at, 1, &f->f_av_value );
1369 /* unhandled filter type; should not happen */
1371 backsql_strfcat_x( &bsi->bsi_flt_where,
1372 bsi->bsi_op->o_tmpmemctx,
1374 (ber_len_t)STRLENOF( "8=8" ), "8=8" );
1379 Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter_attr(%s)\n",
1380 at->bam_ad->ad_cname.bv_val, 0, 0 );
1386 backsql_srch_query( backsql_srch_info *bsi, struct berval *query )
1388 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1391 assert( query != NULL );
1392 BER_BVZERO( query );
1394 bsi->bsi_use_subtree_shortcut = 0;
1396 Debug( LDAP_DEBUG_TRACE, "==>backsql_srch_query()\n", 0, 0, 0 );
1397 BER_BVZERO( &bsi->bsi_sel.bb_val );
1398 BER_BVZERO( &bsi->bsi_sel.bb_val );
1399 bsi->bsi_sel.bb_len = 0;
1400 BER_BVZERO( &bsi->bsi_from.bb_val );
1401 bsi->bsi_from.bb_len = 0;
1402 BER_BVZERO( &bsi->bsi_join_where.bb_val );
1403 bsi->bsi_join_where.bb_len = 0;
1404 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1405 bsi->bsi_flt_where.bb_len = 0;
1407 backsql_strfcat_x( &bsi->bsi_sel,
1408 bsi->bsi_op->o_tmpmemctx,
1410 (ber_len_t)STRLENOF( "SELECT DISTINCT ldap_entries.id," ),
1411 "SELECT DISTINCT ldap_entries.id,",
1412 &bsi->bsi_oc->bom_keytbl,
1414 &bsi->bsi_oc->bom_keycol,
1417 if ( !BER_BVISNULL( &bi->sql_strcast_func ) ) {
1418 backsql_strfcat_x( &bsi->bsi_sel,
1419 bsi->bsi_op->o_tmpmemctx,
1421 &bi->sql_strcast_func,
1422 (ber_len_t)STRLENOF( "('" /* ') */ ),
1424 &bsi->bsi_oc->bom_oc->soc_cname,
1425 (ber_len_t)STRLENOF( /* (' */ "')" ),
1428 backsql_strfcat_x( &bsi->bsi_sel,
1429 bsi->bsi_op->o_tmpmemctx,
1432 &bsi->bsi_oc->bom_oc->soc_cname,
1436 backsql_strfcat_x( &bsi->bsi_sel,
1437 bsi->bsi_op->o_tmpmemctx,
1439 &bi->sql_dn_oc_aliasing );
1440 backsql_strfcat_x( &bsi->bsi_from,
1441 bsi->bsi_op->o_tmpmemctx,
1443 (ber_len_t)STRLENOF( " FROM ldap_entries," ),
1444 " FROM ldap_entries,",
1445 &bsi->bsi_oc->bom_keytbl );
1447 backsql_strfcat_x( &bsi->bsi_join_where,
1448 bsi->bsi_op->o_tmpmemctx,
1450 (ber_len_t)STRLENOF( " WHERE " ), " WHERE ",
1451 &bsi->bsi_oc->bom_keytbl,
1453 &bsi->bsi_oc->bom_keycol,
1454 (ber_len_t)STRLENOF( "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ),
1455 "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " );
1457 switch ( bsi->bsi_scope ) {
1458 case LDAP_SCOPE_BASE:
1459 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1460 backsql_strfcat_x( &bsi->bsi_join_where,
1461 bsi->bsi_op->o_tmpmemctx,
1463 &bi->sql_upper_func,
1464 (ber_len_t)STRLENOF( "(ldap_entries.dn)=?" ),
1465 "(ldap_entries.dn)=?" );
1467 backsql_strfcat_x( &bsi->bsi_join_where,
1468 bsi->bsi_op->o_tmpmemctx,
1470 (ber_len_t)STRLENOF( "ldap_entries.dn=?" ),
1471 "ldap_entries.dn=?" );
1475 case BACKSQL_SCOPE_BASE_LIKE:
1476 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1477 backsql_strfcat_x( &bsi->bsi_join_where,
1478 bsi->bsi_op->o_tmpmemctx,
1480 &bi->sql_upper_func,
1481 (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1482 "(ldap_entries.dn) LIKE ?" );
1484 backsql_strfcat_x( &bsi->bsi_join_where,
1485 bsi->bsi_op->o_tmpmemctx,
1487 (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1488 "ldap_entries.dn LIKE ?" );
1492 case LDAP_SCOPE_ONELEVEL:
1493 backsql_strfcat_x( &bsi->bsi_join_where,
1494 bsi->bsi_op->o_tmpmemctx,
1496 (ber_len_t)STRLENOF( "ldap_entries.parent=?" ),
1497 "ldap_entries.parent=?" );
1500 case LDAP_SCOPE_SUBORDINATE:
1501 case LDAP_SCOPE_SUBTREE:
1502 if ( BACKSQL_USE_SUBTREE_SHORTCUT( bi ) ) {
1504 BackendDB *bd = bsi->bsi_op->o_bd;
1506 assert( bd->be_nsuffix != NULL );
1508 for ( i = 0; !BER_BVISNULL( &bd->be_nsuffix[ i ] ); i++ )
1510 if ( dn_match( &bd->be_nsuffix[ i ],
1511 bsi->bsi_base_ndn ) )
1513 /* pass this to the candidate selection
1514 * routine so that the DN is not bound
1515 * to the select statement */
1516 bsi->bsi_use_subtree_shortcut = 1;
1522 if ( bsi->bsi_use_subtree_shortcut ) {
1523 /* Skip the base DN filter, as every entry will match it */
1524 backsql_strfcat_x( &bsi->bsi_join_where,
1525 bsi->bsi_op->o_tmpmemctx,
1527 (ber_len_t)STRLENOF( "9=9"), "9=9");
1529 } else if ( !BER_BVISNULL( &bi->sql_subtree_cond ) ) {
1530 /* This should always be true... */
1531 backsql_strfcat_x( &bsi->bsi_join_where,
1532 bsi->bsi_op->o_tmpmemctx,
1534 &bi->sql_subtree_cond );
1536 } else if ( BACKSQL_CANUPPERCASE( bi ) ) {
1537 backsql_strfcat_x( &bsi->bsi_join_where,
1538 bsi->bsi_op->o_tmpmemctx,
1540 &bi->sql_upper_func,
1541 (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1542 "(ldap_entries.dn) LIKE ?" );
1545 backsql_strfcat_x( &bsi->bsi_join_where,
1546 bsi->bsi_op->o_tmpmemctx,
1548 (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1549 "ldap_entries.dn LIKE ?" );
1558 #ifndef BACKSQL_ARBITRARY_KEY
1559 /* If paged results are in effect, ignore low ldap_entries.id numbers */
1560 if ( get_pagedresults(bsi->bsi_op) > SLAP_CONTROL_IGNORED ) {
1561 unsigned long lowid = 0;
1563 /* Pick up the previous ldap_entries.id if the previous page ended in this objectClass */
1564 if ( bsi->bsi_oc->bom_id == PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie ) )
1566 lowid = PAGECOOKIE_TO_SQL_ID( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie );
1570 char lowidstring[48];
1573 lowidlen = snprintf( lowidstring, sizeof( lowidstring ),
1574 " AND ldap_entries.id>%lu", lowid );
1575 backsql_strfcat_x( &bsi->bsi_join_where,
1576 bsi->bsi_op->o_tmpmemctx,
1578 (ber_len_t)lowidlen,
1582 #endif /* ! BACKSQL_ARBITRARY_KEY */
1584 rc = backsql_process_filter( bsi, bsi->bsi_filter );
1586 struct berbuf bb = BB_NULL;
1588 backsql_strfcat_x( &bb,
1589 bsi->bsi_op->o_tmpmemctx,
1591 &bsi->bsi_sel.bb_val,
1592 &bsi->bsi_from.bb_val,
1593 &bsi->bsi_join_where.bb_val,
1594 (ber_len_t)STRLENOF( " AND " ), " AND ",
1595 &bsi->bsi_flt_where.bb_val );
1599 } else if ( rc < 0 ) {
1601 * Indicates that there's no possible way the filter matches
1602 * anything. No need to issue the query
1604 free( query->bv_val );
1605 BER_BVZERO( query );
1608 bsi->bsi_op->o_tmpfree( bsi->bsi_sel.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1609 BER_BVZERO( &bsi->bsi_sel.bb_val );
1610 bsi->bsi_sel.bb_len = 0;
1611 bsi->bsi_op->o_tmpfree( bsi->bsi_from.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1612 BER_BVZERO( &bsi->bsi_from.bb_val );
1613 bsi->bsi_from.bb_len = 0;
1614 bsi->bsi_op->o_tmpfree( bsi->bsi_join_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1615 BER_BVZERO( &bsi->bsi_join_where.bb_val );
1616 bsi->bsi_join_where.bb_len = 0;
1617 bsi->bsi_op->o_tmpfree( bsi->bsi_flt_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1618 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1619 bsi->bsi_flt_where.bb_len = 0;
1621 Debug( LDAP_DEBUG_TRACE, "<==backsql_srch_query() returns %s\n",
1622 query->bv_val ? query->bv_val : "NULL", 0, 0 );
1624 return ( rc <= 0 ? 1 : 0 );
1628 backsql_oc_get_candidates( void *v_oc, void *v_bsi )
1630 backsql_oc_map_rec *oc = v_oc;
1631 backsql_srch_info *bsi = v_bsi;
1632 Operation *op = bsi->bsi_op;
1633 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1634 struct berval query;
1635 SQLHSTMT sth = SQL_NULL_HSTMT;
1638 BACKSQL_ROW_NTS row;
1641 int n_candidates = bsi->bsi_n_candidates;
1644 * + 1 because we need room for '%';
1645 * + 1 because we need room for ',' for LDAP_SCOPE_SUBORDINATE;
1646 * this makes a subtree
1647 * search for a DN BACKSQL_MAX_DN_LEN long legal
1648 * if it returns that DN only
1650 char tmp_base_ndn[ BACKSQL_MAX_DN_LEN + 1 + 1 ];
1652 bsi->bsi_status = LDAP_SUCCESS;
1654 Debug( LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc=\"%s\"\n",
1655 BACKSQL_OC_NAME( oc ), 0, 0 );
1657 /* check for abandon */
1658 if ( op->o_abandon ) {
1659 bsi->bsi_status = SLAPD_ABANDON;
1660 return BACKSQL_AVL_STOP;
1663 #ifndef BACKSQL_ARBITRARY_KEY
1664 /* If paged results have already completed this objectClass, skip it */
1665 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
1666 if ( oc->bom_id < PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)op->o_pagedresults_state)->ps_cookie ) )
1668 return BACKSQL_AVL_CONTINUE;
1671 #endif /* ! BACKSQL_ARBITRARY_KEY */
1673 if ( bsi->bsi_n_candidates == -1 ) {
1674 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1675 "unchecked limit has been overcome\n", 0, 0, 0 );
1676 /* should never get here */
1678 bsi->bsi_status = LDAP_ADMINLIMIT_EXCEEDED;
1679 return BACKSQL_AVL_STOP;
1683 res = backsql_srch_query( bsi, &query );
1685 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1686 "error while constructing query for objectclass \"%s\"\n",
1687 oc->bom_oc->soc_cname.bv_val, 0, 0 );
1689 * FIXME: need to separate errors from legally
1690 * impossible filters
1692 switch ( bsi->bsi_status ) {
1694 case LDAP_UNDEFINED_TYPE:
1695 case LDAP_NO_SUCH_OBJECT:
1696 /* we are conservative... */
1698 bsi->bsi_status = LDAP_SUCCESS;
1700 return BACKSQL_AVL_CONTINUE;
1702 case LDAP_ADMINLIMIT_EXCEEDED:
1704 /* don't try any more */
1705 return BACKSQL_AVL_STOP;
1709 if ( BER_BVISNULL( &query ) ) {
1710 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1711 "could not construct query for objectclass \"%s\"\n",
1712 oc->bom_oc->soc_cname.bv_val, 0, 0 );
1713 bsi->bsi_status = LDAP_SUCCESS;
1714 return BACKSQL_AVL_CONTINUE;
1717 Debug( LDAP_DEBUG_TRACE, "Constructed query: %s\n",
1718 query.bv_val, 0, 0 );
1720 rc = backsql_Prepare( bsi->bsi_dbh, &sth, query.bv_val, 0 );
1721 bsi->bsi_op->o_tmpfree( query.bv_val, bsi->bsi_op->o_tmpmemctx );
1722 BER_BVZERO( &query );
1723 if ( rc != SQL_SUCCESS ) {
1724 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1725 "error preparing query\n", 0, 0, 0 );
1726 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1727 bsi->bsi_status = LDAP_OTHER;
1728 return BACKSQL_AVL_CONTINUE;
1731 Debug( LDAP_DEBUG_TRACE, "id: '%ld'\n", bsi->bsi_oc->bom_id, 0, 0 );
1733 rc = backsql_BindParamInt( sth, 1, SQL_PARAM_INPUT,
1734 &bsi->bsi_oc->bom_id );
1735 if ( rc != SQL_SUCCESS ) {
1736 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1737 "error binding objectclass id parameter\n", 0, 0, 0 );
1738 bsi->bsi_status = LDAP_OTHER;
1739 return BACKSQL_AVL_CONTINUE;
1742 switch ( bsi->bsi_scope ) {
1743 case LDAP_SCOPE_BASE:
1744 case BACKSQL_SCOPE_BASE_LIKE:
1746 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1747 * however this should be handled earlier
1749 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1750 bsi->bsi_status = LDAP_OTHER;
1751 return BACKSQL_AVL_CONTINUE;
1754 AC_MEMCPY( tmp_base_ndn, bsi->bsi_base_ndn->bv_val,
1755 bsi->bsi_base_ndn->bv_len + 1 );
1757 /* uppercase DN only if the stored DN can be uppercased
1759 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1760 ldap_pvt_str2upper( tmp_base_ndn );
1763 Debug( LDAP_DEBUG_TRACE, "(base)dn: \"%s\"\n",
1764 tmp_base_ndn, 0, 0 );
1766 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1767 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1768 if ( rc != SQL_SUCCESS ) {
1769 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1770 "error binding base_ndn parameter\n", 0, 0, 0 );
1771 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh,
1773 bsi->bsi_status = LDAP_OTHER;
1774 return BACKSQL_AVL_CONTINUE;
1778 case LDAP_SCOPE_SUBORDINATE:
1779 case LDAP_SCOPE_SUBTREE:
1781 /* if short-cutting the search base,
1782 * don't bind any parameter */
1783 if ( bsi->bsi_use_subtree_shortcut ) {
1788 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1789 * however this should be handled earlier
1791 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1792 bsi->bsi_status = LDAP_OTHER;
1793 return BACKSQL_AVL_CONTINUE;
1797 * Sets the parameters for the SQL built earlier
1798 * NOTE that all the databases could actually use
1799 * the TimesTen version, which would be cleaner
1800 * and would also eliminate the need for the
1801 * subtree_cond line in the configuration file.
1802 * For now, I'm leaving it the way it is,
1803 * so non-TimesTen databases use the original code.
1804 * But at some point this should get cleaned up.
1806 * If "dn" is being used, do a suffix search.
1807 * If "dn_ru" is being used, do a prefix search.
1809 if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
1810 tmp_base_ndn[ 0 ] = '\0';
1812 for ( i = 0, j = bsi->bsi_base_ndn->bv_len - 1;
1814 tmp_base_ndn[ i ] = bsi->bsi_base_ndn->bv_val[ j ];
1817 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1818 tmp_base_ndn[ i++ ] = ',';
1821 tmp_base_ndn[ i ] = '%';
1822 tmp_base_ndn[ i + 1 ] = '\0';
1827 tmp_base_ndn[ i++ ] = '%';
1829 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1830 tmp_base_ndn[ i++ ] = ',';
1833 AC_MEMCPY( &tmp_base_ndn[ i ], bsi->bsi_base_ndn->bv_val,
1834 bsi->bsi_base_ndn->bv_len + 1 );
1837 /* uppercase DN only if the stored DN can be uppercased
1839 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1840 ldap_pvt_str2upper( tmp_base_ndn );
1843 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1844 Debug( LDAP_DEBUG_TRACE, "(children)dn: \"%s\"\n",
1845 tmp_base_ndn, 0, 0 );
1847 Debug( LDAP_DEBUG_TRACE, "(sub)dn: \"%s\"\n",
1848 tmp_base_ndn, 0, 0 );
1851 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1852 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1853 if ( rc != SQL_SUCCESS ) {
1854 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1855 "error binding base_ndn parameter (2)\n",
1857 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh,
1859 bsi->bsi_status = LDAP_OTHER;
1860 return BACKSQL_AVL_CONTINUE;
1865 case LDAP_SCOPE_ONELEVEL:
1866 assert( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) );
1868 #ifdef BACKSQL_ARBITRARY_KEY
1869 Debug( LDAP_DEBUG_TRACE, "(one)id: \"%s\"\n",
1870 bsi->bsi_base_id.eid_id.bv_val, 0, 0 );
1871 #else /* ! BACKSQL_ARBITRARY_KEY */
1872 Debug( LDAP_DEBUG_TRACE, "(one)id: '%lu'\n",
1873 bsi->bsi_base_id.eid_id, 0, 0 );
1874 #endif /* ! BACKSQL_ARBITRARY_KEY */
1875 rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT,
1876 &bsi->bsi_base_id.eid_id );
1877 if ( rc != SQL_SUCCESS ) {
1878 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1879 "error binding base id parameter\n", 0, 0, 0 );
1880 bsi->bsi_status = LDAP_OTHER;
1881 return BACKSQL_AVL_CONTINUE;
1886 rc = SQLExecute( sth );
1887 if ( !BACKSQL_SUCCESS( rc ) ) {
1888 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1889 "error executing query\n", 0, 0, 0 );
1890 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1891 SQLFreeStmt( sth, SQL_DROP );
1892 bsi->bsi_status = LDAP_OTHER;
1893 return BACKSQL_AVL_CONTINUE;
1896 backsql_BindRowAsStrings_x( sth, &row, bsi->bsi_op->o_tmpmemctx );
1897 rc = SQLFetch( sth );
1898 for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
1899 struct berval dn, pdn, ndn;
1900 backsql_entryID *c_id = NULL;
1903 ber_str2bv( row.cols[ 3 ], 0, 0, &dn );
1905 if ( backsql_api_odbc2dn( bsi->bsi_op, bsi->bsi_rs, &dn ) ) {
1909 ret = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
1910 if ( dn.bv_val != row.cols[ 3 ] ) {
1914 if ( ret != LDAP_SUCCESS ) {
1918 if ( bi->sql_baseObject && dn_match( &ndn, &bi->sql_baseObject->e_nname ) ) {
1922 c_id = (backsql_entryID *)op->o_tmpcalloc( 1,
1923 sizeof( backsql_entryID ), op->o_tmpmemctx );
1924 #ifdef BACKSQL_ARBITRARY_KEY
1925 ber_str2bv_x( row.cols[ 0 ], 0, 1, &c_id->eid_id,
1927 ber_str2bv_x( row.cols[ 1 ], 0, 1, &c_id->eid_keyval,
1929 #else /* ! BACKSQL_ARBITRARY_KEY */
1930 if ( lutil_atoulx( &c_id->eid_id, row.cols[ 0 ], 0 ) != 0 ) {
1933 if ( lutil_atoulx( &c_id->eid_keyval, row.cols[ 1 ], 0 ) != 0 ) {
1936 #endif /* ! BACKSQL_ARBITRARY_KEY */
1937 c_id->eid_oc = bsi->bsi_oc;
1938 c_id->eid_oc_id = bsi->bsi_oc->bom_id;
1941 c_id->eid_ndn = ndn;
1943 /* append at end of list ... */
1944 c_id->eid_next = NULL;
1945 *bsi->bsi_id_listtail = c_id;
1946 bsi->bsi_id_listtail = &c_id->eid_next;
1948 #ifdef BACKSQL_ARBITRARY_KEY
1949 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1950 "added entry id=%s, keyval=%s dn=\"%s\"\n",
1951 c_id->eid_id.bv_val, c_id->eid_keyval.bv_val,
1953 #else /* ! BACKSQL_ARBITRARY_KEY */
1954 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1955 "added entry id=%ld, keyval=%ld dn=\"%s\"\n",
1956 c_id->eid_id, c_id->eid_keyval, row.cols[ 3 ] );
1957 #endif /* ! BACKSQL_ARBITRARY_KEY */
1959 /* count candidates, for unchecked limit */
1960 bsi->bsi_n_candidates--;
1961 if ( bsi->bsi_n_candidates == -1 ) {
1967 if ( !BER_BVISNULL( &pdn ) ) {
1968 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
1970 if ( !BER_BVISNULL( &ndn ) ) {
1971 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
1973 if ( c_id != NULL ) {
1977 backsql_FreeRow_x( &row, bsi->bsi_op->o_tmpmemctx );
1978 SQLFreeStmt( sth, SQL_DROP );
1980 Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates(): %d\n",
1981 n_candidates - bsi->bsi_n_candidates, 0, 0 );
1983 return ( bsi->bsi_n_candidates == -1 ? BACKSQL_AVL_STOP : BACKSQL_AVL_CONTINUE );
1987 backsql_search( Operation *op, SlapReply *rs )
1989 backsql_info *bi = (backsql_info *)op->o_bd->be_private;
1990 SQLHDBC dbh = SQL_NULL_HDBC;
1992 Entry user_entry = { 0 },
1994 int manageDSAit = get_manageDSAit( op );
1995 time_t stoptime = 0;
1996 backsql_srch_info bsi = { 0 };
1997 backsql_entryID *eid = NULL;
1998 struct berval nbase = BER_BVNULL;
1999 #ifndef BACKSQL_ARBITRARY_KEY
2001 #endif /* ! BACKSQL_ARBITRARY_KEY */
2003 Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): "
2004 "base=\"%s\", filter=\"%s\", scope=%d,",
2005 op->o_req_ndn.bv_val,
2006 op->ors_filterstr.bv_val,
2008 Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, "
2009 "attributes to load: %s\n",
2012 op->ors_attrs == NULL ? "all" : "custom list" );
2014 if ( op->o_req_ndn.bv_len > BACKSQL_MAX_DN_LEN ) {
2015 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2016 "search base length (%ld) exceeds max length (%d)\n",
2017 op->o_req_ndn.bv_len, BACKSQL_MAX_DN_LEN, 0 );
2019 * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate
2020 * since it is impossible that such a long DN exists
2023 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
2024 send_ldap_result( op, rs );
2028 sres = backsql_get_db_conn( op, &dbh );
2029 if ( sres != LDAP_SUCCESS ) {
2030 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2031 "could not get connection handle - exiting\n",
2034 rs->sr_text = sres == LDAP_OTHER ? "SQL-backend error" : NULL;
2035 send_ldap_result( op, rs );
2039 /* compute it anyway; root does not use it */
2040 stoptime = op->o_time + op->ors_tlimit;
2043 bsi.bsi_e = &base_entry;
2044 rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn,
2046 stoptime, op->ors_filter,
2047 dbh, op, rs, op->ors_attrs,
2048 ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) );
2049 switch ( rs->sr_err ) {
2054 if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
2055 dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) )
2057 rs->sr_err = LDAP_SUCCESS;
2059 rs->sr_matched = NULL;
2061 ber_bvarray_free( rs->sr_ref );
2067 /* an entry was created; free it */
2068 entry_clean( bsi.bsi_e );
2073 if ( !BER_BVISNULL( &base_entry.e_nname )
2074 && !access_allowed( op, &base_entry,
2075 slap_schema.si_ad_entry, NULL,
2076 ACL_DISCLOSE, NULL ) )
2078 rs->sr_err = LDAP_NO_SUCH_OBJECT;
2080 ber_bvarray_free( rs->sr_ref );
2083 rs->sr_matched = NULL;
2087 send_ldap_result( op, rs );
2090 ber_bvarray_free( rs->sr_ref );
2094 if ( !BER_BVISNULL( &base_entry.e_nname ) ) {
2095 entry_clean( &base_entry );
2100 /* NOTE: __NEW__ "search" access is required
2101 * on searchBase object */
2105 if ( get_assert( op ) &&
2106 ( test_filter( op, &base_entry, get_assertion( op ) )
2107 != LDAP_COMPARE_TRUE ) )
2109 rs->sr_err = LDAP_ASSERTION_FAILED;
2112 if ( ! access_allowed_mask( op, &base_entry,
2113 slap_schema.si_ad_entry,
2114 NULL, ACL_SEARCH, NULL, &mask ) )
2116 if ( rs->sr_err == LDAP_SUCCESS ) {
2117 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
2121 if ( rs->sr_err != LDAP_SUCCESS ) {
2122 if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
2123 rs->sr_err = LDAP_NO_SUCH_OBJECT;
2126 send_ldap_result( op, rs );
2133 bsi.bsi_n_candidates =
2134 ( op->ors_limit == NULL /* isroot == TRUE */ ? -2 :
2135 ( op->ors_limit->lms_s_unchecked == -1 ? -2 :
2136 ( op->ors_limit->lms_s_unchecked ) ) );
2138 #ifndef BACKSQL_ARBITRARY_KEY
2139 /* If paged results are in effect, check the paging cookie */
2140 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
2141 rs->sr_err = parse_paged_cookie( op, rs );
2142 if ( rs->sr_err != LDAP_SUCCESS ) {
2143 send_ldap_result( op, rs );
2147 #endif /* ! BACKSQL_ARBITRARY_KEY */
2149 switch ( bsi.bsi_scope ) {
2150 case LDAP_SCOPE_BASE:
2151 case BACKSQL_SCOPE_BASE_LIKE:
2153 * probably already found...
2155 bsi.bsi_id_list = &bsi.bsi_base_id;
2156 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
2159 case LDAP_SCOPE_SUBTREE:
2161 * if baseObject is defined, and if it is the root
2162 * of the search, add it to the candidate list
2164 if ( bi->sql_baseObject && BACKSQL_IS_BASEOBJECT_ID( &bsi.bsi_base_id.eid_id ) )
2166 bsi.bsi_id_list = &bsi.bsi_base_id;
2167 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
2174 * for each objectclass we try to construct query which gets IDs
2175 * of entries matching LDAP query filter and scope (or at least
2176 * candidates), and get the IDs. Do this in ID order for paging.
2178 avl_apply( bi->sql_oc_by_id, backsql_oc_get_candidates,
2179 &bsi, BACKSQL_AVL_STOP, AVL_INORDER );
2181 /* check for abandon */
2182 if ( op->o_abandon ) {
2183 eid = bsi.bsi_id_list;
2184 rs->sr_err = SLAPD_ABANDON;
2189 if ( op->ors_limit != NULL /* isroot == FALSE */
2190 && op->ors_limit->lms_s_unchecked != -1
2191 && bsi.bsi_n_candidates == -1 )
2193 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
2194 send_ldap_result( op, rs );
2199 * now we load candidate entries (only those attributes
2200 * mentioned in attrs and filter), test it against full filter
2201 * and then send to client; don't free entry_id if baseObject...
2203 for ( eid = bsi.bsi_id_list;
2205 eid = backsql_free_entryID(
2206 eid, eid == &bsi.bsi_base_id ? 0 : 1, op->o_tmpmemctx ) )
2209 Attribute *a_hasSubordinate = NULL,
2210 *a_entryUUID = NULL,
2215 /* check for abandon */
2216 if ( op->o_abandon ) {
2217 rs->sr_err = SLAPD_ABANDON;
2221 /* check time limit */
2222 if ( op->ors_tlimit != SLAP_NO_LIMIT
2223 && slap_get_time() > stoptime )
2225 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
2226 rs->sr_ctrls = NULL;
2227 rs->sr_ref = rs->sr_v2ref;
2231 #ifdef BACKSQL_ARBITRARY_KEY
2232 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
2233 "for entry id=%s, oc_id=%ld, keyval=%s\n",
2234 eid->eid_id.bv_val, eid->eid_oc_id,
2235 eid->eid_keyval.bv_val );
2236 #else /* ! BACKSQL_ARBITRARY_KEY */
2237 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
2238 "for entry id=%ld, oc_id=%ld, keyval=%ld\n",
2239 eid->eid_id, eid->eid_oc_id, eid->eid_keyval );
2240 #endif /* ! BACKSQL_ARBITRARY_KEY */
2243 switch ( op->ors_scope ) {
2244 case LDAP_SCOPE_BASE:
2245 case BACKSQL_SCOPE_BASE_LIKE:
2246 if ( !dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
2251 case LDAP_SCOPE_ONE:
2253 struct berval rdn = eid->eid_ndn;
2255 rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
2256 if ( !dnIsOneLevelRDN( &rdn ) ) {
2262 case LDAP_SCOPE_SUBORDINATE:
2263 /* discard the baseObject entry */
2264 if ( dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
2268 case LDAP_SCOPE_SUBTREE:
2269 /* FIXME: this should never fail... */
2270 if ( !dnIsSuffix( &eid->eid_ndn, &op->o_req_ndn ) ) {
2276 if ( BACKSQL_IS_BASEOBJECT_ID( &eid->eid_id ) ) {
2277 /* don't recollect baseObject... */
2278 e = bi->sql_baseObject;
2280 } else if ( eid == &bsi.bsi_base_id ) {
2281 /* don't recollect searchBase object... */
2285 bsi.bsi_e = &user_entry;
2286 rc = backsql_id2entry( &bsi, eid );
2287 if ( rc != LDAP_SUCCESS ) {
2288 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2289 "error %d in backsql_id2entry() "
2290 "- skipping\n", rc, 0, 0 );
2296 if ( !manageDSAit &&
2297 op->ors_scope != LDAP_SCOPE_BASE &&
2298 op->ors_scope != BACKSQL_SCOPE_BASE_LIKE &&
2299 is_entry_referral( e ) )
2303 refs = get_entry_referrals( op, e );
2305 backsql_srch_info bsi2 = { 0 };
2306 Entry user_entry2 = { 0 };
2308 /* retry with the full entry... */
2309 bsi2.bsi_e = &user_entry2;
2310 rc = backsql_init_search( &bsi2,
2315 BACKSQL_ISF_GET_ENTRY );
2316 if ( rc == LDAP_SUCCESS ) {
2317 if ( is_entry_referral( &user_entry2 ) )
2319 refs = get_entry_referrals( op,
2322 rs->sr_err = LDAP_OTHER;
2324 backsql_entry_clean( op, &user_entry2 );
2326 if ( bsi2.bsi_attrs != NULL ) {
2327 op->o_tmpfree( bsi2.bsi_attrs,
2333 rs->sr_ref = referral_rewrite( refs,
2337 ber_bvarray_free( refs );
2341 rs->sr_err = LDAP_REFERRAL;
2344 rs->sr_text = "bad referral object";
2348 rs->sr_matched = user_entry.e_name.bv_val;
2349 send_search_reference( op, rs );
2351 ber_bvarray_free( rs->sr_ref );
2353 rs->sr_matched = NULL;
2354 rs->sr_entry = NULL;
2355 if ( rs->sr_err == LDAP_REFERRAL ) {
2356 rs->sr_err = LDAP_SUCCESS;
2363 * We use this flag since we need to parse the filter
2364 * anyway; we should have used the frontend API function
2365 * filter_has_subordinates()
2367 if ( bsi.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) {
2368 rc = backsql_has_children( op, dbh, &e->e_nname );
2371 case LDAP_COMPARE_TRUE:
2372 case LDAP_COMPARE_FALSE:
2373 a_hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE );
2374 if ( a_hasSubordinate != NULL ) {
2375 for ( ap = &user_entry.e_attrs;
2377 ap = &(*ap)->a_next );
2379 *ap = a_hasSubordinate;
2385 Debug(LDAP_DEBUG_TRACE,
2386 "backsql_search(): "
2387 "has_children failed( %d)\n",
2394 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYUUID ) {
2395 a_entryUUID = backsql_operational_entryUUID( bi, eid );
2396 if ( a_entryUUID != NULL ) {
2398 ap = &user_entry.e_attrs;
2401 for ( ; *ap; ap = &(*ap)->a_next );
2407 #ifdef BACKSQL_SYNCPROV
2408 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYCSN ) {
2409 a_entryCSN = backsql_operational_entryCSN( op );
2410 if ( a_entryCSN != NULL ) {
2412 ap = &user_entry.e_attrs;
2415 for ( ; *ap; ap = &(*ap)->a_next );
2420 #endif /* BACKSQL_SYNCPROV */
2422 if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE )
2424 #ifndef BACKSQL_ARBITRARY_KEY
2425 /* If paged results are in effect, see if the page limit was exceeded */
2426 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
2427 if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size )
2430 send_paged_response( op, rs, &lastid );
2433 lastid = SQL_TO_PAGECOOKIE( eid->eid_id, eid->eid_oc_id );
2435 #endif /* ! BACKSQL_ARBITRARY_KEY */
2436 rs->sr_attrs = op->ors_attrs;
2437 rs->sr_operational_attrs = NULL;
2439 e->e_private = (void *)eid;
2440 rs->sr_flags = ( e == &user_entry ) ? REP_ENTRY_MODIFIABLE : 0;
2441 /* FIXME: need the whole entry (ITS#3480) */
2442 rs->sr_err = send_search_entry( op, rs );
2443 e->e_private = NULL;
2444 rs->sr_entry = NULL;
2445 rs->sr_attrs = NULL;
2446 rs->sr_operational_attrs = NULL;
2448 switch ( rs->sr_err ) {
2449 case LDAP_UNAVAILABLE:
2451 * FIXME: send_search_entry failed;
2454 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2455 "connection lost\n", 0, 0, 0 );
2458 case LDAP_SIZELIMIT_EXCEEDED:
2464 if ( e == &user_entry ) {
2465 backsql_entry_clean( op, &user_entry );
2472 if ( rs->sr_nentries > 0 ) {
2473 rs->sr_ref = rs->sr_v2ref;
2474 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
2478 rs->sr_err = bsi.bsi_status;
2482 if ( rs->sr_err != SLAPD_ABANDON ) {
2483 #ifndef BACKSQL_ARBITRARY_KEY
2484 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
2485 send_paged_response( op, rs, NULL );
2487 #endif /* ! BACKSQL_ARBITRARY_KEY */
2489 send_ldap_result( op, rs );
2493 /* cleanup in case of abandon */
2494 for ( ; eid != NULL;
2495 eid = backsql_free_entryID(
2496 eid, eid == &bsi.bsi_base_id ? 0 : 1, op->o_tmpmemctx ) )
2499 backsql_entry_clean( op, &base_entry );
2501 /* in case we got here accidentally */
2502 backsql_entry_clean( op, &user_entry );
2504 if ( rs->sr_v2ref ) {
2505 ber_bvarray_free( rs->sr_v2ref );
2506 rs->sr_v2ref = NULL;
2509 #ifdef BACKSQL_SYNCPROV
2511 Operation op2 = *op;
2512 SlapReply rs2 = { 0 };
2513 Entry *e = entry_alloc();
2514 slap_callback cb = { 0 };
2516 op2.o_tag = LDAP_REQ_ADD;
2517 op2.o_bd = select_backend( &op->o_bd->be_nsuffix[0], 0 );
2519 op2.o_callback = &cb;
2521 ber_dupbv( &e->e_name, op->o_bd->be_suffix );
2522 ber_dupbv( &e->e_nname, op->o_bd->be_nsuffix );
2524 cb.sc_response = slap_null_cb;
2526 op2.o_bd->be_add( &op2, &rs2 );
2528 if ( op2.ora_e == e )
2531 #endif /* BACKSQL_SYNCPROV */
2534 (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
2536 if ( bsi.bsi_attrs != NULL ) {
2537 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
2540 if ( !BER_BVISNULL( &nbase )
2541 && nbase.bv_val != op->o_req_ndn.bv_val )
2543 ch_free( nbase.bv_val );
2546 /* restore scope ... FIXME: this should be done before ANY
2547 * frontend call that uses op */
2548 if ( op->ors_scope == BACKSQL_SCOPE_BASE_LIKE ) {
2549 op->ors_scope = LDAP_SCOPE_BASE;
2552 Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n", 0, 0, 0 );
2557 /* return LDAP_SUCCESS IFF we can retrieve the specified entry.
2564 AttributeDescription *at,
2568 backsql_srch_info bsi = { 0 };
2569 SQLHDBC dbh = SQL_NULL_HDBC;
2571 SlapReply rs = { 0 };
2572 AttributeName anlist[ 2 ];
2576 rc = backsql_get_db_conn( op, &dbh );
2577 if ( rc != LDAP_SUCCESS ) {
2582 anlist[ 0 ].an_name = at->ad_cname;
2583 anlist[ 0 ].an_desc = at;
2584 BER_BVZERO( &anlist[ 1 ].an_name );
2587 bsi.bsi_e = entry_alloc();
2588 rc = backsql_init_search( &bsi,
2592 dbh, op, &rs, at ? anlist : NULL,
2593 BACKSQL_ISF_GET_ENTRY );
2595 if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
2596 (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
2599 if ( rc == LDAP_SUCCESS ) {
2601 #if 0 /* not supported at present */
2602 /* find attribute values */
2603 if ( is_entry_alias( bsi.bsi_e ) ) {
2604 Debug( LDAP_DEBUG_ACL,
2605 "<= backsql_entry_get: entry is an alias\n",
2607 rc = LDAP_ALIAS_PROBLEM;
2608 goto return_results;
2612 if ( is_entry_referral( bsi.bsi_e ) ) {
2613 Debug( LDAP_DEBUG_ACL,
2614 "<= backsql_entry_get: entry is a referral\n",
2617 goto return_results;
2620 if ( oc && !is_entry_objectclass( bsi.bsi_e, oc, 0 ) ) {
2621 Debug( LDAP_DEBUG_ACL,
2622 "<= backsql_entry_get: "
2623 "failed to find objectClass\n",
2625 rc = LDAP_NO_SUCH_ATTRIBUTE;
2626 goto return_results;
2633 if ( bsi.bsi_attrs != NULL ) {
2634 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
2637 if ( rc != LDAP_SUCCESS ) {
2639 entry_free( bsi.bsi_e );
2647 backsql_entry_clean(
2653 ctx = ldap_pvt_thread_pool_context();
2655 if ( ctx == NULL || ctx != op->o_tmpmemctx ) {
2656 if ( !BER_BVISNULL( &e->e_name ) ) {
2657 op->o_tmpfree( e->e_name.bv_val, op->o_tmpmemctx );
2658 BER_BVZERO( &e->e_name );
2661 if ( !BER_BVISNULL( &e->e_nname ) ) {
2662 op->o_tmpfree( e->e_nname.bv_val, op->o_tmpmemctx );
2663 BER_BVZERO( &e->e_nname );
2671 backsql_entry_release(
2676 backsql_entry_clean( op, e );
2683 #ifndef BACKSQL_ARBITRARY_KEY
2684 /* This function is copied verbatim from back-bdb/search.c */
2686 parse_paged_cookie( Operation *op, SlapReply *rs )
2688 int rc = LDAP_SUCCESS;
2689 PagedResultsState *ps = op->o_pagedresults_state;
2691 /* this function must be invoked only if the pagedResults
2692 * control has been detected, parsed and partially checked
2693 * by the frontend */
2694 assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
2696 /* cookie decoding/checks deferred to backend... */
2697 if ( ps->ps_cookieval.bv_len ) {
2698 PagedResultsCookie reqcookie;
2699 if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) {
2701 rs->sr_text = "paged results cookie is invalid";
2702 rc = LDAP_PROTOCOL_ERROR;
2706 AC_MEMCPY( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie ));
2708 if ( reqcookie > ps->ps_cookie ) {
2710 rs->sr_text = "paged results cookie is invalid";
2711 rc = LDAP_PROTOCOL_ERROR;
2714 } else if ( reqcookie < ps->ps_cookie ) {
2715 rs->sr_text = "paged results cookie is invalid or old";
2716 rc = LDAP_UNWILLING_TO_PERFORM;
2721 /* Initial request. Initialize state. */
2731 /* This function is copied nearly verbatim from back-bdb/search.c */
2733 send_paged_response(
2738 LDAPControl ctrl, *ctrls[2];
2739 BerElementBuffer berbuf;
2740 BerElement *ber = (BerElement *)&berbuf;
2741 PagedResultsCookie respcookie;
2742 struct berval cookie;
2744 Debug(LDAP_DEBUG_ARGS,
2745 "send_paged_response: lastid=0x%08lx nentries=%d\n",
2746 lastid ? *lastid : 0, rs->sr_nentries, NULL );
2748 BER_BVZERO( &ctrl.ldctl_value );
2752 ber_init2( ber, NULL, LBER_USE_DER );
2755 respcookie = ( PagedResultsCookie )(*lastid);
2756 cookie.bv_len = sizeof( respcookie );
2757 cookie.bv_val = (char *)&respcookie;
2760 respcookie = ( PagedResultsCookie )0;
2761 BER_BVSTR( &cookie, "" );
2764 op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
2765 op->o_conn->c_pagedresults_state.ps_count =
2766 ((PagedResultsState *)op->o_pagedresults_state)->ps_count +
2769 /* return size of 0 -- no estimate */
2770 ber_printf( ber, "{iO}", 0, &cookie );
2772 if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
2776 ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
2777 ctrls[0]->ldctl_iscritical = 0;
2779 rs->sr_ctrls = ctrls;
2780 rs->sr_err = LDAP_SUCCESS;
2781 send_ldap_result( op, rs );
2782 rs->sr_ctrls = NULL;
2785 (void) ber_free_buf( ber );
2787 #endif /* ! BACKSQL_ARBITRARY_KEY */