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. */
51 #define SQL_TO_PAGECOOKIE(id, oc) (((id) << 6 ) | ((oc) & 0x3F))
52 #define PAGECOOKIE_TO_SQL_ID(pc) ((pc) >> 6)
53 #define PAGECOOKIE_TO_SQL_OC(pc) ((pc) & 0x3F)
55 static int parse_paged_cookie( Operation *op, SlapReply *rs );
57 static void send_paged_response(
63 backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad )
66 AttributeName *an = NULL;
68 if ( bsi->bsi_attrs == NULL ) {
73 * clear the list (retrieve all attrs)
76 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs, bsi->bsi_op->o_tmpmemctx );
77 bsi->bsi_attrs = NULL;
78 bsi->bsi_flags |= BSQL_SF_ALL_ATTRS;
83 if ( slap_ad_is_binary( ad ) ) {
84 ad = ad->ad_type->sat_ad;
87 for ( ; !BER_BVISNULL( &bsi->bsi_attrs[ n_attrs ].an_name ); n_attrs++ ) {
88 an = &bsi->bsi_attrs[ n_attrs ];
90 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
91 "attribute \"%s\" is in list\n",
92 an->an_name.bv_val, 0, 0 );
94 * We can live with strcmp because the attribute
95 * list has been normalized before calling be_search
97 if ( !BACKSQL_NCMP( &an->an_name, &ad->ad_cname ) ) {
102 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
103 "adding \"%s\" to list\n", ad->ad_cname.bv_val, 0, 0 );
105 an = (AttributeName *)bsi->bsi_op->o_tmprealloc( bsi->bsi_attrs,
106 sizeof( AttributeName ) * ( n_attrs + 2 ),
107 bsi->bsi_op->o_tmpmemctx );
112 an[ n_attrs ].an_name = ad->ad_cname;
113 an[ n_attrs ].an_desc = ad;
114 BER_BVZERO( &an[ n_attrs + 1 ].an_name );
122 * Initializes the search structure.
124 * If get_base_id != 0, the field bsi_base_id is filled
125 * with the entryID of bsi_base_ndn; it must be freed
126 * by backsql_free_entryID() when no longer required.
128 * NOTE: base must be normalized
132 backsql_srch_info *bsi,
133 struct berval *nbase,
140 AttributeName *attrs,
143 backsql_info *bi = (backsql_info *)op->o_bd->be_private;
144 int rc = LDAP_SUCCESS;
146 bsi->bsi_base_ndn = nbase;
147 bsi->bsi_use_subtree_shortcut = 0;
148 BER_BVZERO( &bsi->bsi_base_id.eid_dn );
149 BER_BVZERO( &bsi->bsi_base_id.eid_ndn );
150 bsi->bsi_scope = scope;
151 bsi->bsi_filter = filter;
155 bsi->bsi_flags = BSQL_SF_NONE;
157 bsi->bsi_attrs = NULL;
159 if ( BACKSQL_FETCH_ALL_ATTRS( bi ) ) {
161 * if requested, simply try to fetch all attributes
163 bsi->bsi_flags |= BSQL_SF_ALL_ATTRS;
166 if ( BACKSQL_FETCH_ALL_USERATTRS( bi ) ) {
167 bsi->bsi_flags |= BSQL_SF_ALL_USER;
169 } else if ( BACKSQL_FETCH_ALL_OPATTRS( bi ) ) {
170 bsi->bsi_flags |= BSQL_SF_ALL_OPER;
173 if ( attrs == NULL ) {
174 /* NULL means all user attributes */
175 bsi->bsi_flags |= BSQL_SF_ALL_USER;
181 bsi->bsi_attrs = (AttributeName *)bsi->bsi_op->o_tmpalloc(
182 sizeof( AttributeName ),
183 bsi->bsi_op->o_tmpmemctx );
184 BER_BVZERO( &bsi->bsi_attrs[ 0 ].an_name );
186 for ( p = attrs; !BER_BVISNULL( &p->an_name ); p++ ) {
187 if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_user_attrs ) == 0 ) {
189 bsi->bsi_flags |= BSQL_SF_ALL_USER;
191 /* if all attrs are requested, there's
192 * no need to continue */
193 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
194 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
195 bsi->bsi_op->o_tmpmemctx );
196 bsi->bsi_attrs = NULL;
201 } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_operational_attrs ) == 0 ) {
203 bsi->bsi_flags |= BSQL_SF_ALL_OPER;
205 /* if all attrs are requested, there's
206 * no need to continue */
207 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
208 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
209 bsi->bsi_op->o_tmpmemctx );
210 bsi->bsi_attrs = NULL;
215 } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_no_attrs ) == 0 ) {
219 } else if ( p->an_desc == slap_schema.si_ad_objectClass ) {
223 backsql_attrlist_add( bsi, p->an_desc );
226 if ( got_oc == 0 && !( bsi->bsi_flags & BSQL_SF_ALL_USER ) ) {
227 /* add objectClass if not present,
228 * because it is required to understand
229 * if an entry is a referral, an alias
231 backsql_attrlist_add( bsi, slap_schema.si_ad_objectClass );
235 if ( !BSQL_ISF_ALL_ATTRS( bsi ) && bi->sql_anlist ) {
238 /* use hints if available */
239 for ( p = bi->sql_anlist; !BER_BVISNULL( &p->an_name ); p++ ) {
240 if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_user_attrs ) == 0 ) {
242 bsi->bsi_flags |= BSQL_SF_ALL_USER;
244 /* if all attrs are requested, there's
245 * no need to continue */
246 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
247 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
248 bsi->bsi_op->o_tmpmemctx );
249 bsi->bsi_attrs = NULL;
254 } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_operational_attrs ) == 0 ) {
256 bsi->bsi_flags |= BSQL_SF_ALL_OPER;
258 /* if all attrs are requested, there's
259 * no need to continue */
260 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
261 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
262 bsi->bsi_op->o_tmpmemctx );
263 bsi->bsi_attrs = NULL;
269 backsql_attrlist_add( bsi, p->an_desc );
275 bsi->bsi_id_list = NULL;
276 bsi->bsi_id_listtail = &bsi->bsi_id_list;
277 bsi->bsi_n_candidates = 0;
278 bsi->bsi_stoptime = stoptime;
279 BER_BVZERO( &bsi->bsi_sel.bb_val );
280 bsi->bsi_sel.bb_len = 0;
281 BER_BVZERO( &bsi->bsi_from.bb_val );
282 bsi->bsi_from.bb_len = 0;
283 BER_BVZERO( &bsi->bsi_join_where.bb_val );
284 bsi->bsi_join_where.bb_len = 0;
285 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
286 bsi->bsi_flt_where.bb_len = 0;
287 bsi->bsi_filter_oc = NULL;
289 if ( BACKSQL_IS_GET_ID( flags ) ) {
290 int matched = BACKSQL_IS_MATCHED( flags );
291 int getentry = BACKSQL_IS_GET_ENTRY( flags );
294 assert( op->o_bd->be_private != NULL );
296 rc = backsql_dn2id( op, rs, dbh, nbase, &bsi->bsi_base_id,
299 /* the entry is collected either if requested for by getentry
300 * or if get noSuchObject and requested to climb the tree,
301 * so that a matchedDN or a referral can be returned */
302 if ( ( rc == LDAP_NO_SUCH_OBJECT && matched ) || getentry ) {
303 if ( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) ) {
304 assert( bsi->bsi_e != NULL );
306 if ( dn_match( nbase, &bsi->bsi_base_id.eid_ndn ) )
312 * let's see if it is a referral and, in case, get it
314 backsql_attrlist_add( bsi, slap_schema.si_ad_ref );
315 rc = backsql_id2entry( bsi, &bsi->bsi_base_id );
316 if ( rc == LDAP_SUCCESS ) {
317 if ( is_entry_referral( bsi->bsi_e ) )
319 BerVarray erefs = get_entry_referrals( op, bsi->bsi_e );
321 rc = rs->sr_err = LDAP_REFERRAL;
322 rs->sr_ref = referral_rewrite( erefs,
323 &bsi->bsi_e->e_nname,
326 ber_bvarray_free( erefs );
329 rc = rs->sr_err = LDAP_OTHER;
330 rs->sr_text = "bad referral object";
333 } else if ( !gotit ) {
334 rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
343 if ( gotit && BACKSQL_IS_GET_OC( flags ) ) {
344 bsi->bsi_base_id.eid_oc = backsql_id2oc( bi,
345 bsi->bsi_base_id.eid_oc_id );
346 if ( bsi->bsi_base_id.eid_oc == NULL ) {
348 backsql_free_entryID( &bsi->bsi_base_id, 1,
350 rc = rs->sr_err = LDAP_OTHER;
355 bsi->bsi_status = rc;
363 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
364 bsi->bsi_op->o_tmpmemctx );
372 backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op )
380 backsql_strfcat_x( &bsi->bsi_flt_where,
381 bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */ );
384 res = backsql_process_filter( bsi, f );
387 * TimesTen : If the query has no answers,
388 * don't bother to run the query.
399 case LDAP_FILTER_AND:
400 backsql_strfcat_x( &bsi->bsi_flt_where,
401 bsi->bsi_op->o_tmpmemctx, "l",
402 (ber_len_t)STRLENOF( " AND " ),
407 backsql_strfcat_x( &bsi->bsi_flt_where,
408 bsi->bsi_op->o_tmpmemctx, "l",
409 (ber_len_t)STRLENOF( " OR " ),
415 backsql_strfcat_x( &bsi->bsi_flt_where,
416 bsi->bsi_op->o_tmpmemctx, "c", /* ( */ ')' );
422 backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f,
423 backsql_at_map_rec *at )
425 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
433 /* always uppercase strings by now */
434 #ifdef BACKSQL_UPPERCASE_FILTER
435 if ( f->f_sub_desc->ad_type->sat_substr &&
436 SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
437 bi->sql_caseIgnoreMatch ) )
438 #endif /* BACKSQL_UPPERCASE_FILTER */
443 if ( f->f_sub_desc->ad_type->sat_substr &&
444 SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
445 bi->sql_telephoneNumberMatch ) )
452 * to check for matching telephone numbers
453 * with intermixed chars, e.g. val='1234'
456 * val LIKE '%1%2%3%4%'
460 if ( f->f_sub_initial.bv_val ) {
461 bv.bv_len += f->f_sub_initial.bv_len;
463 if ( f->f_sub_any != NULL ) {
464 for ( a = 0; f->f_sub_any[ a ].bv_val != NULL; a++ ) {
465 bv.bv_len += f->f_sub_any[ a ].bv_len;
468 if ( f->f_sub_final.bv_val ) {
469 bv.bv_len += f->f_sub_final.bv_len;
471 bv.bv_len = 2 * bv.bv_len - 1;
472 bv.bv_val = ch_malloc( bv.bv_len + 1 );
475 if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
476 bv.bv_val[ s ] = f->f_sub_initial.bv_val[ 0 ];
477 for ( i = 1; i < f->f_sub_initial.bv_len; i++ ) {
478 bv.bv_val[ s + 2 * i - 1 ] = '%';
479 bv.bv_val[ s + 2 * i ] = f->f_sub_initial.bv_val[ i ];
481 bv.bv_val[ s + 2 * i - 1 ] = '%';
485 if ( f->f_sub_any != NULL ) {
486 for ( a = 0; !BER_BVISNULL( &f->f_sub_any[ a ] ); a++ ) {
487 bv.bv_val[ s ] = f->f_sub_any[ a ].bv_val[ 0 ];
488 for ( i = 1; i < f->f_sub_any[ a ].bv_len; i++ ) {
489 bv.bv_val[ s + 2 * i - 1 ] = '%';
490 bv.bv_val[ s + 2 * i ] = f->f_sub_any[ a ].bv_val[ i ];
492 bv.bv_val[ s + 2 * i - 1 ] = '%';
497 if ( !BER_BVISNULL( &f->f_sub_final ) ) {
498 bv.bv_val[ s ] = f->f_sub_final.bv_val[ 0 ];
499 for ( i = 1; i < f->f_sub_final.bv_len; i++ ) {
500 bv.bv_val[ s + 2 * i - 1 ] = '%';
501 bv.bv_val[ s + 2 * i ] = f->f_sub_final.bv_val[ i ];
503 bv.bv_val[ s + 2 * i - 1 ] = '%';
507 bv.bv_val[ s - 1 ] = '\0';
509 (void)backsql_process_filter_like( bsi, at, casefold, &bv );
510 ch_free( bv.bv_val );
516 * When dealing with case-sensitive strings
517 * we may omit normalization; however, normalized
518 * SQL filters are more liberal.
521 backsql_strfcat_x( &bsi->bsi_flt_where,
522 bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */ );
525 Debug( LDAP_DEBUG_TRACE, "backsql_process_sub_filter(%s):\n",
526 at->bam_ad->ad_cname.bv_val, 0, 0 );
527 Debug(LDAP_DEBUG_TRACE, " expr: '%s%s%s'\n", at->bam_sel_expr.bv_val,
528 at->bam_sel_expr_u.bv_val ? "' '" : "",
529 at->bam_sel_expr_u.bv_val ? at->bam_sel_expr_u.bv_val : "" );
530 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
532 * If a pre-upper-cased version of the column
533 * or a precompiled upper function exists, use it
535 backsql_strfcat_x( &bsi->bsi_flt_where,
536 bsi->bsi_op->o_tmpmemctx,
539 (ber_len_t)STRLENOF( " LIKE '" ),
543 backsql_strfcat_x( &bsi->bsi_flt_where,
544 bsi->bsi_op->o_tmpmemctx,
547 (ber_len_t)STRLENOF( " LIKE '" ), " LIKE '" );
550 if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
554 Debug( LDAP_DEBUG_TRACE,
555 "==>backsql_process_sub_filter(%s): "
556 "sub_initial=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
557 f->f_sub_initial.bv_val, 0 );
558 #endif /* BACKSQL_TRACE */
560 start = bsi->bsi_flt_where.bb_val.bv_len;
561 backsql_strfcat_x( &bsi->bsi_flt_where,
562 bsi->bsi_op->o_tmpmemctx,
565 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
566 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
570 backsql_strfcat_x( &bsi->bsi_flt_where,
571 bsi->bsi_op->o_tmpmemctx,
574 if ( f->f_sub_any != NULL ) {
575 for ( i = 0; !BER_BVISNULL( &f->f_sub_any[ i ] ); i++ ) {
579 Debug( LDAP_DEBUG_TRACE,
580 "==>backsql_process_sub_filter(%s): "
581 "sub_any[%d]=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
582 i, f->f_sub_any[ i ].bv_val );
583 #endif /* BACKSQL_TRACE */
585 start = bsi->bsi_flt_where.bb_val.bv_len;
586 backsql_strfcat_x( &bsi->bsi_flt_where,
587 bsi->bsi_op->o_tmpmemctx,
591 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
593 * Note: toupper('%') = '%'
595 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
600 if ( !BER_BVISNULL( &f->f_sub_final ) ) {
604 Debug( LDAP_DEBUG_TRACE,
605 "==>backsql_process_sub_filter(%s): "
606 "sub_final=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
607 f->f_sub_final.bv_val, 0 );
608 #endif /* BACKSQL_TRACE */
610 start = bsi->bsi_flt_where.bb_val.bv_len;
611 backsql_strfcat_x( &bsi->bsi_flt_where,
612 bsi->bsi_op->o_tmpmemctx,
615 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
616 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
620 backsql_strfcat_x( &bsi->bsi_flt_where,
621 bsi->bsi_op->o_tmpmemctx,
623 (ber_len_t)STRLENOF( /* (' */ "')" ), /* (' */ "')" );
629 backsql_merge_from_tbls( backsql_srch_info *bsi, struct berval *from_tbls )
631 if ( BER_BVISNULL( from_tbls ) ) {
635 if ( !BER_BVISNULL( &bsi->bsi_from.bb_val ) ) {
639 ber_dupbv_x( &tmp, from_tbls, bsi->bsi_op->o_tmpmemctx );
641 for ( start = tmp.bv_val, end = strchr( start, ',' ); start; ) {
646 if ( strstr( bsi->bsi_from.bb_val.bv_val, start) == NULL )
648 backsql_strfcat_x( &bsi->bsi_from,
649 bsi->bsi_op->o_tmpmemctx,
654 /* in case there are spaces after the comma... */
655 for ( start = &end[1]; isspace( start[0] ); start++ );
657 end = strchr( start, ',' );
666 bsi->bsi_op->o_tmpfree( tmp.bv_val, bsi->bsi_op->o_tmpmemctx );
669 backsql_strfcat_x( &bsi->bsi_from,
670 bsi->bsi_op->o_tmpmemctx,
678 backsql_process_filter( backsql_srch_info *bsi, Filter *f )
680 backsql_at_map_rec **vat = NULL;
681 AttributeDescription *ad = NULL;
686 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter()\n", 0, 0, 0 );
687 if ( f->f_choice == SLAPD_FILTER_COMPUTED ) {
691 switch ( f->f_result ) {
692 case LDAP_COMPARE_TRUE:
693 BER_BVSTR( &flt, "10=10" );
697 case LDAP_COMPARE_FALSE:
698 BER_BVSTR( &flt, "11=0" );
702 case SLAPD_COMPARE_UNDEFINED:
703 BER_BVSTR( &flt, "12=0" );
712 Debug( LDAP_DEBUG_TRACE, "backsql_process_filter(): "
713 "filter computed (%s)\n", msg, 0, 0 );
714 backsql_strfcat_x( &bsi->bsi_flt_where,
715 bsi->bsi_op->o_tmpmemctx, "b", &flt );
720 switch( f->f_choice ) {
722 rc = backsql_process_filter_list( bsi, f->f_or,
727 case LDAP_FILTER_AND:
728 rc = backsql_process_filter_list( bsi, f->f_and,
733 case LDAP_FILTER_NOT:
734 backsql_strfcat_x( &bsi->bsi_flt_where,
735 bsi->bsi_op->o_tmpmemctx,
737 (ber_len_t)STRLENOF( "NOT (" /* ) */ ),
739 rc = backsql_process_filter( bsi, f->f_not );
740 backsql_strfcat_x( &bsi->bsi_flt_where,
741 bsi->bsi_op->o_tmpmemctx,
746 case LDAP_FILTER_PRESENT:
750 case LDAP_FILTER_EXT:
751 ad = f->f_mra->ma_desc;
752 if ( f->f_mr_dnattrs ) {
754 * if dn attrs filtering is requested, better return
755 * success and let test_filter() deal with candidate
756 * selection; otherwise we'd need to set conditions
757 * on the contents of the DN, e.g. "SELECT ... FROM
758 * ldap_entries AS attributeName WHERE attributeName.dn
759 * like '%attributeName=value%'"
761 backsql_strfcat_x( &bsi->bsi_flt_where,
762 bsi->bsi_op->o_tmpmemctx,
764 (ber_len_t)STRLENOF( "1=1" ), "1=1" );
765 bsi->bsi_status = LDAP_SUCCESS;
786 * Turn structuralObjectClass into objectClass
788 if ( ad == slap_schema.si_ad_objectClass
789 || ad == slap_schema.si_ad_structuralObjectClass )
792 * If the filter is LDAP_FILTER_PRESENT, then it's done;
793 * otherwise, let's see if we are lucky: filtering
794 * for "structural" objectclass or ancestor...
796 switch ( f->f_choice ) {
797 case LDAP_FILTER_EQUALITY:
799 ObjectClass *oc = oc_bvfind( &f->f_av_value );
802 Debug( LDAP_DEBUG_TRACE,
803 "backsql_process_filter(): "
804 "unknown objectClass \"%s\" "
806 f->f_av_value.bv_val, 0, 0 );
807 bsi->bsi_status = LDAP_OTHER;
813 * "structural" objectClass inheritance:
814 * - a search for "person" will also return
816 * - a search for "top" will return everything
818 if ( is_object_subclass( oc, bsi->bsi_oc->bom_oc ) ) {
819 static struct berval ldap_entry_objclasses = BER_BVC( "ldap_entry_objclasses" );
821 backsql_merge_from_tbls( bsi, &ldap_entry_objclasses );
823 backsql_strfcat_x( &bsi->bsi_flt_where,
824 bsi->bsi_op->o_tmpmemctx,
826 (ber_len_t)STRLENOF( "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */ ),
827 "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */,
828 &bsi->bsi_oc->bom_oc->soc_cname,
829 (ber_len_t)STRLENOF( /* ((' */ "'))" ),
831 bsi->bsi_status = LDAP_SUCCESS;
839 case LDAP_FILTER_PRESENT:
840 backsql_strfcat_x( &bsi->bsi_flt_where,
841 bsi->bsi_op->o_tmpmemctx,
843 (ber_len_t)STRLENOF( "3=3" ), "3=3" );
844 bsi->bsi_status = LDAP_SUCCESS;
848 /* FIXME: LDAP_FILTER_EXT? */
851 Debug( LDAP_DEBUG_TRACE,
852 "backsql_process_filter(): "
853 "illegal/unhandled filter "
854 "on objectClass attribute",
856 bsi->bsi_status = LDAP_OTHER;
861 } else if ( ad == slap_schema.si_ad_entryUUID ) {
863 #ifdef BACKSQL_ARBITRARY_KEY
864 struct berval keyval;
865 #else /* ! BACKSQL_ARBITRARY_KEY */
866 unsigned long keyval;
867 char keyvalbuf[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
868 #endif /* ! BACKSQL_ARBITRARY_KEY */
870 switch ( f->f_choice ) {
871 case LDAP_FILTER_EQUALITY:
872 backsql_entryUUID_decode( &f->f_av_value, &oc_id, &keyval );
874 if ( oc_id != bsi->bsi_oc->bom_id ) {
875 bsi->bsi_status = LDAP_SUCCESS;
880 #ifdef BACKSQL_ARBITRARY_KEY
881 backsql_strfcat_x( &bsi->bsi_flt_where,
882 bsi->bsi_op->o_tmpmemctx,
884 &bsi->bsi_oc->bom_keytbl, '.',
885 &bsi->bsi_oc->bom_keycol,
886 STRLENOF( " LIKE '" ), " LIKE '",
888 #else /* ! BACKSQL_ARBITRARY_KEY */
889 snprintf( keyvalbuf, sizeof( keyvalbuf ), "%lu", keyval );
890 backsql_strfcat_x( &bsi->bsi_flt_where,
891 bsi->bsi_op->o_tmpmemctx,
893 &bsi->bsi_oc->bom_keytbl, '.',
894 &bsi->bsi_oc->bom_keycol, '=', keyvalbuf );
895 #endif /* ! BACKSQL_ARBITRARY_KEY */
898 case LDAP_FILTER_PRESENT:
899 backsql_strfcat_x( &bsi->bsi_flt_where,
900 bsi->bsi_op->o_tmpmemctx,
902 (ber_len_t)STRLENOF( "4=4" ), "4=4" );
910 bsi->bsi_flags |= BSQL_SF_FILTER_ENTRYUUID;
914 #ifdef BACKSQL_SYNCPROV
915 } else if ( ad == slap_schema.si_ad_entryCSN ) {
917 * support for syncrepl as producer...
920 if ( !bsi->bsi_op->o_sync ) {
921 /* unsupported at present... */
922 bsi->bsi_status = LDAP_OTHER;
928 bsi->bsi_flags |= ( BSQL_SF_FILTER_ENTRYCSN | BSQL_SF_RETURN_ENTRYUUID);
930 /* if doing a syncrepl, try to return as much as possible,
931 * and always match the filter */
932 backsql_strfcat_x( &bsi->bsi_flt_where,
933 bsi->bsi_op->o_tmpmemctx,
935 (ber_len_t)STRLENOF( "5=5" ), "5=5" );
937 /* save for later use in operational attributes */
938 /* FIXME: saves only the first occurrence, because
939 * the filter during updates is written as
940 * "(&(entryCSN<={contextCSN})(entryCSN>={oldContextCSN})({filter}))"
941 * so we want our fake entryCSN to match the greatest
944 if ( bsi->bsi_op->o_private == NULL ) {
945 bsi->bsi_op->o_private = &f->f_av_value;
947 bsi->bsi_status = LDAP_SUCCESS;
951 #endif /* BACKSQL_SYNCPROV */
953 } else if ( ad == slap_schema.si_ad_hasSubordinates || ad == NULL ) {
955 * FIXME: this is not robust; e.g. a filter
956 * '(!(hasSubordinates=TRUE))' fails because
957 * in SQL it would read 'NOT (1=1)' instead
959 * Note however that hasSubordinates is boolean,
960 * so a more appropriate filter would be
961 * '(hasSubordinates=FALSE)'
963 * A more robust search for hasSubordinates
964 * would * require joining the ldap_entries table
965 * selecting if there are descendants of the
968 backsql_strfcat_x( &bsi->bsi_flt_where,
969 bsi->bsi_op->o_tmpmemctx,
971 (ber_len_t)STRLENOF( "6=6" ), "6=6" );
972 if ( ad == slap_schema.si_ad_hasSubordinates ) {
974 * instruct candidate selection algorithm
975 * and attribute list to try to detect
976 * if an entry has subordinates
978 bsi->bsi_flags |= BSQL_SF_FILTER_HASSUBORDINATE;
982 * clear attributes to fetch, to require ALL
983 * and try extended match on all attributes
985 backsql_attrlist_add( bsi, NULL );
992 * attribute inheritance:
994 if ( backsql_supad2at( bsi->bsi_oc, ad, &vat ) ) {
995 bsi->bsi_status = LDAP_OTHER;
1000 if ( vat == NULL ) {
1001 /* search anyway; other parts of the filter
1003 backsql_strfcat_x( &bsi->bsi_flt_where,
1004 bsi->bsi_op->o_tmpmemctx,
1006 (ber_len_t)STRLENOF( "7=7" ), "7=7" );
1007 bsi->bsi_status = LDAP_SUCCESS;
1012 /* if required, open extra level of parens */
1014 if ( vat[0]->bam_next || vat[1] ) {
1015 backsql_strfcat_x( &bsi->bsi_flt_where,
1016 bsi->bsi_op->o_tmpmemctx,
1024 if ( backsql_process_filter_attr( bsi, f, vat[i] ) == -1 ) {
1028 /* if more definitions of the same attr, apply */
1029 if ( vat[i]->bam_next ) {
1030 backsql_strfcat_x( &bsi->bsi_flt_where,
1031 bsi->bsi_op->o_tmpmemctx,
1033 STRLENOF( " OR " ), " OR " );
1034 vat[i] = vat[i]->bam_next;
1038 /* if more descendants of the same attr, apply */
1041 backsql_strfcat_x( &bsi->bsi_flt_where,
1042 bsi->bsi_op->o_tmpmemctx,
1044 STRLENOF( " OR " ), " OR " );
1048 /* if needed, close extra level of parens */
1050 backsql_strfcat_x( &bsi->bsi_flt_where,
1051 bsi->bsi_op->o_tmpmemctx,
1062 Debug( LDAP_DEBUG_TRACE,
1063 "<==backsql_process_filter() %s\n",
1064 rc == 1 ? "succeeded" : "failed", 0, 0);
1070 backsql_process_filter_eq( backsql_srch_info *bsi, backsql_at_map_rec *at,
1071 int casefold, struct berval *filter_value )
1074 * maybe we should check type of at->sel_expr here somehow,
1075 * to know whether upper_func is applicable, but for now
1076 * upper_func stuff is made for Oracle, where UPPER is
1077 * safely applicable to NUMBER etc.
1079 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1082 backsql_strfcat_x( &bsi->bsi_flt_where,
1083 bsi->bsi_op->o_tmpmemctx,
1086 &at->bam_sel_expr_u,
1087 (ber_len_t)STRLENOF( "='" ),
1090 start = bsi->bsi_flt_where.bb_val.bv_len;
1092 backsql_strfcat_x( &bsi->bsi_flt_where,
1093 bsi->bsi_op->o_tmpmemctx,
1096 (ber_len_t)STRLENOF( /* (' */ "')" ),
1099 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1102 backsql_strfcat_x( &bsi->bsi_flt_where,
1103 bsi->bsi_op->o_tmpmemctx,
1107 (ber_len_t)STRLENOF( "='" ), "='",
1109 (ber_len_t)STRLENOF( /* (' */ "')" ),
1117 backsql_process_filter_like( backsql_srch_info *bsi, backsql_at_map_rec *at,
1118 int casefold, struct berval *filter_value )
1121 * maybe we should check type of at->sel_expr here somehow,
1122 * to know whether upper_func is applicable, but for now
1123 * upper_func stuff is made for Oracle, where UPPER is
1124 * safely applicable to NUMBER etc.
1126 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1129 backsql_strfcat_x( &bsi->bsi_flt_where,
1130 bsi->bsi_op->o_tmpmemctx,
1133 &at->bam_sel_expr_u,
1134 (ber_len_t)STRLENOF( " LIKE '%" ),
1137 start = bsi->bsi_flt_where.bb_val.bv_len;
1139 backsql_strfcat_x( &bsi->bsi_flt_where,
1140 bsi->bsi_op->o_tmpmemctx,
1143 (ber_len_t)STRLENOF( /* (' */ "%')" ),
1146 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1149 backsql_strfcat_x( &bsi->bsi_flt_where,
1150 bsi->bsi_op->o_tmpmemctx,
1154 (ber_len_t)STRLENOF( " LIKE '%" ),
1157 (ber_len_t)STRLENOF( /* (' */ "%')" ),
1165 backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f, backsql_at_map_rec *at )
1167 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1169 struct berval *filter_value = NULL;
1170 MatchingRule *matching_rule = NULL;
1171 struct berval ordering = BER_BVC("<=");
1173 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter_attr(%s)\n",
1174 at->bam_ad->ad_cname.bv_val, 0, 0 );
1177 * need to add this attribute to list of attrs to load,
1178 * so that we can do test_filter() later
1180 backsql_attrlist_add( bsi, at->bam_ad );
1182 backsql_merge_from_tbls( bsi, &at->bam_from_tbls );
1184 if ( !BER_BVISNULL( &at->bam_join_where )
1185 && strstr( bsi->bsi_join_where.bb_val.bv_val,
1186 at->bam_join_where.bv_val ) == NULL )
1188 backsql_strfcat_x( &bsi->bsi_join_where,
1189 bsi->bsi_op->o_tmpmemctx,
1191 (ber_len_t)STRLENOF( " AND " ), " AND ",
1192 &at->bam_join_where );
1195 switch ( f->f_choice ) {
1196 case LDAP_FILTER_EQUALITY:
1197 filter_value = &f->f_av_value;
1198 matching_rule = at->bam_ad->ad_type->sat_equality;
1200 goto equality_match;
1202 /* fail over into next case */
1204 case LDAP_FILTER_EXT:
1205 filter_value = &f->f_mra->ma_value;
1206 matching_rule = f->f_mr_rule;
1209 /* always uppercase strings by now */
1210 #ifdef BACKSQL_UPPERCASE_FILTER
1211 if ( SLAP_MR_ASSOCIATED( matching_rule,
1212 bi->sql_caseIgnoreMatch ) )
1213 #endif /* BACKSQL_UPPERCASE_FILTER */
1218 /* FIXME: directoryString filtering should use a similar
1219 * approach to deal with non-prettified values like
1220 * " A non prettified value ", by using a LIKE
1221 * filter with all whitespaces collapsed to a single '%' */
1222 if ( SLAP_MR_ASSOCIATED( matching_rule,
1223 bi->sql_telephoneNumberMatch ) )
1229 * to check for matching telephone numbers
1230 * with intermized chars, e.g. val='1234'
1233 * val LIKE '%1%2%3%4%'
1236 bv.bv_len = 2 * filter_value->bv_len - 1;
1237 bv.bv_val = ch_malloc( bv.bv_len + 1 );
1239 bv.bv_val[ 0 ] = filter_value->bv_val[ 0 ];
1240 for ( i = 1; i < filter_value->bv_len; i++ ) {
1241 bv.bv_val[ 2 * i - 1 ] = '%';
1242 bv.bv_val[ 2 * i ] = filter_value->bv_val[ i ];
1244 bv.bv_val[ 2 * i - 1 ] = '\0';
1246 (void)backsql_process_filter_like( bsi, at, casefold, &bv );
1247 ch_free( bv.bv_val );
1252 /* NOTE: this is required by objectClass inheritance
1253 * and auxiliary objectClass use in filters for slightly
1254 * more efficient candidate selection. */
1255 /* FIXME: a bit too many specializations to deal with
1256 * very specific cases... */
1257 if ( at->bam_ad == slap_schema.si_ad_objectClass
1258 || at->bam_ad == slap_schema.si_ad_structuralObjectClass )
1260 backsql_strfcat_x( &bsi->bsi_flt_where,
1261 bsi->bsi_op->o_tmpmemctx,
1263 (ber_len_t)STRLENOF( "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */ ),
1264 "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */,
1266 (ber_len_t)STRLENOF( /* (' */ "')" ),
1272 * maybe we should check type of at->sel_expr here somehow,
1273 * to know whether upper_func is applicable, but for now
1274 * upper_func stuff is made for Oracle, where UPPER is
1275 * safely applicable to NUMBER etc.
1277 (void)backsql_process_filter_eq( bsi, at, casefold, filter_value );
1280 case LDAP_FILTER_GE:
1281 ordering.bv_val = ">=";
1283 /* fall thru to next case */
1285 case LDAP_FILTER_LE:
1286 filter_value = &f->f_av_value;
1288 /* always uppercase strings by now */
1289 #ifdef BACKSQL_UPPERCASE_FILTER
1290 if ( at->bam_ad->ad_type->sat_ordering &&
1291 SLAP_MR_ASSOCIATED( at->bam_ad->ad_type->sat_ordering,
1292 bi->sql_caseIgnoreMatch ) )
1293 #endif /* BACKSQL_UPPERCASE_FILTER */
1299 * FIXME: should we uppercase the operands?
1301 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1304 backsql_strfcat_x( &bsi->bsi_flt_where,
1305 bsi->bsi_op->o_tmpmemctx,
1308 &at->bam_sel_expr_u,
1312 start = bsi->bsi_flt_where.bb_val.bv_len;
1314 backsql_strfcat_x( &bsi->bsi_flt_where,
1315 bsi->bsi_op->o_tmpmemctx,
1318 (ber_len_t)STRLENOF( /* (' */ "')" ),
1321 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1324 backsql_strfcat_x( &bsi->bsi_flt_where,
1325 bsi->bsi_op->o_tmpmemctx,
1332 (ber_len_t)STRLENOF( /* (' */ "')" ),
1337 case LDAP_FILTER_PRESENT:
1338 backsql_strfcat_x( &bsi->bsi_flt_where,
1339 bsi->bsi_op->o_tmpmemctx,
1341 (ber_len_t)STRLENOF( "NOT (" /* ) */),
1344 (ber_len_t)STRLENOF( /* ( */ " IS NULL)" ),
1345 /* ( */ " IS NULL)" );
1348 case LDAP_FILTER_SUBSTRINGS:
1349 backsql_process_sub_filter( bsi, f, at );
1352 case LDAP_FILTER_APPROX:
1353 /* we do our best */
1356 * maybe we should check type of at->sel_expr here somehow,
1357 * to know whether upper_func is applicable, but for now
1358 * upper_func stuff is made for Oracle, where UPPER is
1359 * safely applicable to NUMBER etc.
1361 (void)backsql_process_filter_like( bsi, at, 1, &f->f_av_value );
1365 /* unhandled filter type; should not happen */
1367 backsql_strfcat_x( &bsi->bsi_flt_where,
1368 bsi->bsi_op->o_tmpmemctx,
1370 (ber_len_t)STRLENOF( "8=8" ), "8=8" );
1375 Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter_attr(%s)\n",
1376 at->bam_ad->ad_cname.bv_val, 0, 0 );
1382 backsql_srch_query( backsql_srch_info *bsi, struct berval *query )
1384 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1387 assert( query != NULL );
1388 BER_BVZERO( query );
1390 bsi->bsi_use_subtree_shortcut = 0;
1392 Debug( LDAP_DEBUG_TRACE, "==>backsql_srch_query()\n", 0, 0, 0 );
1393 BER_BVZERO( &bsi->bsi_sel.bb_val );
1394 BER_BVZERO( &bsi->bsi_sel.bb_val );
1395 bsi->bsi_sel.bb_len = 0;
1396 BER_BVZERO( &bsi->bsi_from.bb_val );
1397 bsi->bsi_from.bb_len = 0;
1398 BER_BVZERO( &bsi->bsi_join_where.bb_val );
1399 bsi->bsi_join_where.bb_len = 0;
1400 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1401 bsi->bsi_flt_where.bb_len = 0;
1403 backsql_strfcat_x( &bsi->bsi_sel,
1404 bsi->bsi_op->o_tmpmemctx,
1406 (ber_len_t)STRLENOF( "SELECT DISTINCT ldap_entries.id," ),
1407 "SELECT DISTINCT ldap_entries.id,",
1408 &bsi->bsi_oc->bom_keytbl,
1410 &bsi->bsi_oc->bom_keycol,
1413 if ( !BER_BVISNULL( &bi->sql_strcast_func ) ) {
1414 backsql_strfcat_x( &bsi->bsi_sel,
1415 bsi->bsi_op->o_tmpmemctx,
1417 &bi->sql_strcast_func,
1418 (ber_len_t)STRLENOF( "('" /* ') */ ),
1420 &bsi->bsi_oc->bom_oc->soc_cname,
1421 (ber_len_t)STRLENOF( /* (' */ "')" ),
1424 backsql_strfcat_x( &bsi->bsi_sel,
1425 bsi->bsi_op->o_tmpmemctx,
1428 &bsi->bsi_oc->bom_oc->soc_cname,
1432 backsql_strfcat_x( &bsi->bsi_sel,
1433 bsi->bsi_op->o_tmpmemctx,
1435 &bi->sql_dn_oc_aliasing );
1436 backsql_strfcat_x( &bsi->bsi_from,
1437 bsi->bsi_op->o_tmpmemctx,
1439 (ber_len_t)STRLENOF( " FROM ldap_entries," ),
1440 " FROM ldap_entries,",
1441 &bsi->bsi_oc->bom_keytbl );
1443 backsql_strfcat_x( &bsi->bsi_join_where,
1444 bsi->bsi_op->o_tmpmemctx,
1446 (ber_len_t)STRLENOF( " WHERE " ), " WHERE ",
1447 &bsi->bsi_oc->bom_keytbl,
1449 &bsi->bsi_oc->bom_keycol,
1450 (ber_len_t)STRLENOF( "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ),
1451 "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " );
1453 switch ( bsi->bsi_scope ) {
1454 case LDAP_SCOPE_BASE:
1455 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1456 backsql_strfcat_x( &bsi->bsi_join_where,
1457 bsi->bsi_op->o_tmpmemctx,
1459 &bi->sql_upper_func,
1460 (ber_len_t)STRLENOF( "(ldap_entries.dn)=?" ),
1461 "(ldap_entries.dn)=?" );
1463 backsql_strfcat_x( &bsi->bsi_join_where,
1464 bsi->bsi_op->o_tmpmemctx,
1466 (ber_len_t)STRLENOF( "ldap_entries.dn=?" ),
1467 "ldap_entries.dn=?" );
1471 case BACKSQL_SCOPE_BASE_LIKE:
1472 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1473 backsql_strfcat_x( &bsi->bsi_join_where,
1474 bsi->bsi_op->o_tmpmemctx,
1476 &bi->sql_upper_func,
1477 (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1478 "(ldap_entries.dn) LIKE ?" );
1480 backsql_strfcat_x( &bsi->bsi_join_where,
1481 bsi->bsi_op->o_tmpmemctx,
1483 (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1484 "ldap_entries.dn LIKE ?" );
1488 case LDAP_SCOPE_ONELEVEL:
1489 backsql_strfcat_x( &bsi->bsi_join_where,
1490 bsi->bsi_op->o_tmpmemctx,
1492 (ber_len_t)STRLENOF( "ldap_entries.parent=?" ),
1493 "ldap_entries.parent=?" );
1496 case LDAP_SCOPE_SUBORDINATE:
1497 case LDAP_SCOPE_SUBTREE:
1498 if ( BACKSQL_USE_SUBTREE_SHORTCUT( bi ) ) {
1500 BackendDB *bd = bsi->bsi_op->o_bd;
1502 assert( bd->be_nsuffix != NULL );
1504 for ( i = 0; !BER_BVISNULL( &bd->be_nsuffix[ i ] ); i++ )
1506 if ( dn_match( &bd->be_nsuffix[ i ],
1507 bsi->bsi_base_ndn ) )
1509 /* pass this to the candidate selection
1510 * routine so that the DN is not bound
1511 * to the select statement */
1512 bsi->bsi_use_subtree_shortcut = 1;
1518 if ( bsi->bsi_use_subtree_shortcut ) {
1519 /* Skip the base DN filter, as every entry will match it */
1520 backsql_strfcat_x( &bsi->bsi_join_where,
1521 bsi->bsi_op->o_tmpmemctx,
1523 (ber_len_t)STRLENOF( "9=9"), "9=9");
1525 } else if ( !BER_BVISNULL( &bi->sql_subtree_cond ) ) {
1526 /* This should always be true... */
1527 backsql_strfcat_x( &bsi->bsi_join_where,
1528 bsi->bsi_op->o_tmpmemctx,
1530 &bi->sql_subtree_cond );
1532 } else if ( BACKSQL_CANUPPERCASE( bi ) ) {
1533 backsql_strfcat_x( &bsi->bsi_join_where,
1534 bsi->bsi_op->o_tmpmemctx,
1536 &bi->sql_upper_func,
1537 (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1538 "(ldap_entries.dn) LIKE ?" );
1541 backsql_strfcat_x( &bsi->bsi_join_where,
1542 bsi->bsi_op->o_tmpmemctx,
1544 (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1545 "ldap_entries.dn LIKE ?" );
1554 /* If paged results are in effect, ignore low ldap_entries.id numbers */
1555 if ( get_pagedresults(bsi->bsi_op) > SLAP_CONTROL_IGNORED ) {
1556 unsigned long lowid = 0;
1558 /* Pick up the previous ldap_entries.id if the previous page ended in this objectClass */
1559 if ( bsi->bsi_oc->bom_id == PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie ) )
1561 lowid = PAGECOOKIE_TO_SQL_ID( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie );
1565 char lowidstring[48];
1568 lowidlen = snprintf( lowidstring, sizeof( lowidstring ),
1569 " AND ldap_entries.id>%lu", lowid );
1570 backsql_strfcat_x( &bsi->bsi_join_where,
1571 bsi->bsi_op->o_tmpmemctx,
1573 (ber_len_t)lowidlen,
1578 rc = backsql_process_filter( bsi, bsi->bsi_filter );
1580 struct berbuf bb = BB_NULL;
1582 backsql_strfcat_x( &bb,
1583 bsi->bsi_op->o_tmpmemctx,
1585 &bsi->bsi_sel.bb_val,
1586 &bsi->bsi_from.bb_val,
1587 &bsi->bsi_join_where.bb_val,
1588 (ber_len_t)STRLENOF( " AND " ), " AND ",
1589 &bsi->bsi_flt_where.bb_val );
1593 } else if ( rc < 0 ) {
1595 * Indicates that there's no possible way the filter matches
1596 * anything. No need to issue the query
1598 free( query->bv_val );
1599 BER_BVZERO( query );
1602 bsi->bsi_op->o_tmpfree( bsi->bsi_sel.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1603 BER_BVZERO( &bsi->bsi_sel.bb_val );
1604 bsi->bsi_sel.bb_len = 0;
1605 bsi->bsi_op->o_tmpfree( bsi->bsi_from.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1606 BER_BVZERO( &bsi->bsi_from.bb_val );
1607 bsi->bsi_from.bb_len = 0;
1608 bsi->bsi_op->o_tmpfree( bsi->bsi_join_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1609 BER_BVZERO( &bsi->bsi_join_where.bb_val );
1610 bsi->bsi_join_where.bb_len = 0;
1611 bsi->bsi_op->o_tmpfree( bsi->bsi_flt_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1612 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1613 bsi->bsi_flt_where.bb_len = 0;
1615 Debug( LDAP_DEBUG_TRACE, "<==backsql_srch_query() returns %s\n",
1616 query->bv_val ? query->bv_val : "NULL", 0, 0 );
1618 return ( rc <= 0 ? 1 : 0 );
1622 backsql_oc_get_candidates( void *v_oc, void *v_bsi )
1624 backsql_oc_map_rec *oc = v_oc;
1625 backsql_srch_info *bsi = v_bsi;
1626 Operation *op = bsi->bsi_op;
1627 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1628 struct berval query;
1629 SQLHSTMT sth = SQL_NULL_HSTMT;
1632 BACKSQL_ROW_NTS row;
1635 int n_candidates = bsi->bsi_n_candidates;
1638 * + 1 because we need room for '%';
1639 * + 1 because we need room for ',' for LDAP_SCOPE_SUBORDINATE;
1640 * this makes a subtree
1641 * search for a DN BACKSQL_MAX_DN_LEN long legal
1642 * if it returns that DN only
1644 char tmp_base_ndn[ BACKSQL_MAX_DN_LEN + 1 + 1 ];
1646 bsi->bsi_status = LDAP_SUCCESS;
1648 Debug( LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc=\"%s\"\n",
1649 BACKSQL_OC_NAME( oc ), 0, 0 );
1651 /* check for abandon */
1652 if ( op->o_abandon ) {
1653 bsi->bsi_status = SLAPD_ABANDON;
1654 return BACKSQL_AVL_STOP;
1657 /* If paged results have already completed this objectClass, skip it */
1658 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
1659 if ( oc->bom_id < PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)op->o_pagedresults_state)->ps_cookie ) )
1661 return BACKSQL_AVL_CONTINUE;
1665 if ( bsi->bsi_n_candidates == -1 ) {
1666 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1667 "unchecked limit has been overcome\n", 0, 0, 0 );
1668 /* should never get here */
1670 bsi->bsi_status = LDAP_ADMINLIMIT_EXCEEDED;
1671 return BACKSQL_AVL_STOP;
1675 res = backsql_srch_query( bsi, &query );
1677 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1678 "error while constructing query for objectclass \"%s\"\n",
1679 oc->bom_oc->soc_cname.bv_val, 0, 0 );
1681 * FIXME: need to separate errors from legally
1682 * impossible filters
1684 switch ( bsi->bsi_status ) {
1686 case LDAP_UNDEFINED_TYPE:
1687 case LDAP_NO_SUCH_OBJECT:
1688 /* we are conservative... */
1690 bsi->bsi_status = LDAP_SUCCESS;
1692 return BACKSQL_AVL_CONTINUE;
1694 case LDAP_ADMINLIMIT_EXCEEDED:
1696 /* don't try any more */
1697 return BACKSQL_AVL_STOP;
1701 if ( BER_BVISNULL( &query ) ) {
1702 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1703 "could not construct query for objectclass \"%s\"\n",
1704 oc->bom_oc->soc_cname.bv_val, 0, 0 );
1705 bsi->bsi_status = LDAP_SUCCESS;
1706 return BACKSQL_AVL_CONTINUE;
1709 Debug( LDAP_DEBUG_TRACE, "Constructed query: %s\n",
1710 query.bv_val, 0, 0 );
1712 rc = backsql_Prepare( bsi->bsi_dbh, &sth, query.bv_val, 0 );
1713 bsi->bsi_op->o_tmpfree( query.bv_val, bsi->bsi_op->o_tmpmemctx );
1714 BER_BVZERO( &query );
1715 if ( rc != SQL_SUCCESS ) {
1716 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1717 "error preparing query\n", 0, 0, 0 );
1718 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1719 bsi->bsi_status = LDAP_OTHER;
1720 return BACKSQL_AVL_CONTINUE;
1723 Debug( LDAP_DEBUG_TRACE, "id: '%ld'\n", bsi->bsi_oc->bom_id, 0, 0 );
1725 rc = backsql_BindParamInt( sth, 1, SQL_PARAM_INPUT,
1726 &bsi->bsi_oc->bom_id );
1727 if ( rc != SQL_SUCCESS ) {
1728 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1729 "error binding objectclass id parameter\n", 0, 0, 0 );
1730 bsi->bsi_status = LDAP_OTHER;
1731 return BACKSQL_AVL_CONTINUE;
1734 switch ( bsi->bsi_scope ) {
1735 case LDAP_SCOPE_BASE:
1736 case BACKSQL_SCOPE_BASE_LIKE:
1738 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1739 * however this should be handled earlier
1741 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1742 bsi->bsi_status = LDAP_OTHER;
1743 return BACKSQL_AVL_CONTINUE;
1746 AC_MEMCPY( tmp_base_ndn, bsi->bsi_base_ndn->bv_val,
1747 bsi->bsi_base_ndn->bv_len + 1 );
1749 /* uppercase DN only if the stored DN can be uppercased
1751 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1752 ldap_pvt_str2upper( tmp_base_ndn );
1755 Debug( LDAP_DEBUG_TRACE, "(base)dn: \"%s\"\n",
1756 tmp_base_ndn, 0, 0 );
1758 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1759 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1760 if ( rc != SQL_SUCCESS ) {
1761 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1762 "error binding base_ndn parameter\n", 0, 0, 0 );
1763 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh,
1765 bsi->bsi_status = LDAP_OTHER;
1766 return BACKSQL_AVL_CONTINUE;
1770 case LDAP_SCOPE_SUBORDINATE:
1771 case LDAP_SCOPE_SUBTREE:
1773 /* if short-cutting the search base,
1774 * don't bind any parameter */
1775 if ( bsi->bsi_use_subtree_shortcut ) {
1780 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1781 * however this should be handled earlier
1783 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1784 bsi->bsi_status = LDAP_OTHER;
1785 return BACKSQL_AVL_CONTINUE;
1789 * Sets the parameters for the SQL built earlier
1790 * NOTE that all the databases could actually use
1791 * the TimesTen version, which would be cleaner
1792 * and would also eliminate the need for the
1793 * subtree_cond line in the configuration file.
1794 * For now, I'm leaving it the way it is,
1795 * so non-TimesTen databases use the original code.
1796 * But at some point this should get cleaned up.
1798 * If "dn" is being used, do a suffix search.
1799 * If "dn_ru" is being used, do a prefix search.
1801 if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
1802 tmp_base_ndn[ 0 ] = '\0';
1804 for ( i = 0, j = bsi->bsi_base_ndn->bv_len - 1;
1806 tmp_base_ndn[ i ] = bsi->bsi_base_ndn->bv_val[ j ];
1809 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1810 tmp_base_ndn[ i++ ] = ',';
1813 tmp_base_ndn[ i ] = '%';
1814 tmp_base_ndn[ i + 1 ] = '\0';
1819 tmp_base_ndn[ i++ ] = '%';
1821 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1822 tmp_base_ndn[ i++ ] = ',';
1825 AC_MEMCPY( &tmp_base_ndn[ i ], bsi->bsi_base_ndn->bv_val,
1826 bsi->bsi_base_ndn->bv_len + 1 );
1829 /* uppercase DN only if the stored DN can be uppercased
1831 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1832 ldap_pvt_str2upper( tmp_base_ndn );
1835 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1836 Debug( LDAP_DEBUG_TRACE, "(children)dn: \"%s\"\n",
1837 tmp_base_ndn, 0, 0 );
1839 Debug( LDAP_DEBUG_TRACE, "(sub)dn: \"%s\"\n",
1840 tmp_base_ndn, 0, 0 );
1843 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1844 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1845 if ( rc != SQL_SUCCESS ) {
1846 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1847 "error binding base_ndn parameter (2)\n",
1849 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh,
1851 bsi->bsi_status = LDAP_OTHER;
1852 return BACKSQL_AVL_CONTINUE;
1857 case LDAP_SCOPE_ONELEVEL:
1858 assert( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) );
1860 #ifdef BACKSQL_ARBITRARY_KEY
1861 Debug( LDAP_DEBUG_TRACE, "(one)id: \"%s\"\n",
1862 bsi->bsi_base_id.eid_id.bv_val, 0, 0 );
1863 #else /* ! BACKSQL_ARBITRARY_KEY */
1864 Debug( LDAP_DEBUG_TRACE, "(one)id: '%lu'\n",
1865 bsi->bsi_base_id.eid_id, 0, 0 );
1866 #endif /* ! BACKSQL_ARBITRARY_KEY */
1867 rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT,
1868 &bsi->bsi_base_id.eid_id );
1869 if ( rc != SQL_SUCCESS ) {
1870 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1871 "error binding base id parameter\n", 0, 0, 0 );
1872 bsi->bsi_status = LDAP_OTHER;
1873 return BACKSQL_AVL_CONTINUE;
1878 rc = SQLExecute( sth );
1879 if ( !BACKSQL_SUCCESS( rc ) ) {
1880 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1881 "error executing query\n", 0, 0, 0 );
1882 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1883 SQLFreeStmt( sth, SQL_DROP );
1884 bsi->bsi_status = LDAP_OTHER;
1885 return BACKSQL_AVL_CONTINUE;
1888 backsql_BindRowAsStrings_x( sth, &row, bsi->bsi_op->o_tmpmemctx );
1889 rc = SQLFetch( sth );
1890 for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
1891 struct berval dn, pdn, ndn;
1892 backsql_entryID *c_id = NULL;
1895 ber_str2bv( row.cols[ 3 ], 0, 0, &dn );
1897 if ( backsql_api_odbc2dn( bsi->bsi_op, bsi->bsi_rs, &dn ) ) {
1901 ret = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
1902 if ( dn.bv_val != row.cols[ 3 ] ) {
1906 if ( ret != LDAP_SUCCESS ) {
1910 if ( bi->sql_baseObject && dn_match( &ndn, &bi->sql_baseObject->e_nname ) ) {
1914 c_id = (backsql_entryID *)op->o_tmpcalloc( 1,
1915 sizeof( backsql_entryID ), op->o_tmpmemctx );
1916 #ifdef BACKSQL_ARBITRARY_KEY
1917 ber_str2bv_x( row.cols[ 0 ], 0, 1, &c_id->eid_id,
1919 ber_str2bv_x( row.cols[ 1 ], 0, 1, &c_id->eid_keyval,
1921 #else /* ! BACKSQL_ARBITRARY_KEY */
1922 if ( lutil_atoulx( &c_id->eid_id, row.cols[ 0 ], 0 ) != 0 ) {
1925 if ( lutil_atoulx( &c_id->eid_keyval, row.cols[ 1 ], 0 ) != 0 ) {
1928 #endif /* ! BACKSQL_ARBITRARY_KEY */
1929 c_id->eid_oc = bsi->bsi_oc;
1930 c_id->eid_oc_id = bsi->bsi_oc->bom_id;
1933 c_id->eid_ndn = ndn;
1935 /* append at end of list ... */
1936 c_id->eid_next = NULL;
1937 *bsi->bsi_id_listtail = c_id;
1938 bsi->bsi_id_listtail = &c_id->eid_next;
1940 #ifdef BACKSQL_ARBITRARY_KEY
1941 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1942 "added entry id=%s, keyval=%s dn=\"%s\"\n",
1943 c_id->eid_id.bv_val, c_id->eid_keyval.bv_val,
1945 #else /* ! BACKSQL_ARBITRARY_KEY */
1946 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1947 "added entry id=%ld, keyval=%ld dn=\"%s\"\n",
1948 c_id->eid_id, c_id->eid_keyval, row.cols[ 3 ] );
1949 #endif /* ! BACKSQL_ARBITRARY_KEY */
1951 /* count candidates, for unchecked limit */
1952 bsi->bsi_n_candidates--;
1953 if ( bsi->bsi_n_candidates == -1 ) {
1959 if ( !BER_BVISNULL( &pdn ) ) {
1960 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
1962 if ( !BER_BVISNULL( &ndn ) ) {
1963 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
1965 if ( c_id != NULL ) {
1969 backsql_FreeRow_x( &row, bsi->bsi_op->o_tmpmemctx );
1970 SQLFreeStmt( sth, SQL_DROP );
1972 Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates(): %d\n",
1973 n_candidates - bsi->bsi_n_candidates, 0, 0 );
1975 return ( bsi->bsi_n_candidates == -1 ? BACKSQL_AVL_STOP : BACKSQL_AVL_CONTINUE );
1979 backsql_search( Operation *op, SlapReply *rs )
1981 backsql_info *bi = (backsql_info *)op->o_bd->be_private;
1982 SQLHDBC dbh = SQL_NULL_HDBC;
1984 Entry user_entry = { 0 },
1986 int manageDSAit = get_manageDSAit( op );
1987 time_t stoptime = 0;
1988 backsql_srch_info bsi = { 0 };
1989 backsql_entryID *eid = NULL;
1990 struct berval nbase = BER_BVNULL;
1991 unsigned long lastid = 0;
1993 Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): "
1994 "base=\"%s\", filter=\"%s\", scope=%d,",
1995 op->o_req_ndn.bv_val,
1996 op->ors_filterstr.bv_val,
1998 Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, "
1999 "attributes to load: %s\n",
2002 op->ors_attrs == NULL ? "all" : "custom list" );
2004 if ( op->o_req_ndn.bv_len > BACKSQL_MAX_DN_LEN ) {
2005 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2006 "search base length (%ld) exceeds max length (%d)\n",
2007 op->o_req_ndn.bv_len, BACKSQL_MAX_DN_LEN, 0 );
2009 * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate
2010 * since it is impossible that such a long DN exists
2013 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
2014 send_ldap_result( op, rs );
2018 sres = backsql_get_db_conn( op, &dbh );
2019 if ( sres != LDAP_SUCCESS ) {
2020 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2021 "could not get connection handle - exiting\n",
2024 rs->sr_text = sres == LDAP_OTHER ? "SQL-backend error" : NULL;
2025 send_ldap_result( op, rs );
2029 /* compute it anyway; root does not use it */
2030 stoptime = op->o_time + op->ors_tlimit;
2033 bsi.bsi_e = &base_entry;
2034 rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn,
2036 stoptime, op->ors_filter,
2037 dbh, op, rs, op->ors_attrs,
2038 ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) );
2039 switch ( rs->sr_err ) {
2044 if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
2045 dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) )
2047 rs->sr_err = LDAP_SUCCESS;
2049 rs->sr_matched = NULL;
2051 ber_bvarray_free( rs->sr_ref );
2057 /* an entry was created; free it */
2058 entry_clean( bsi.bsi_e );
2063 if ( !BER_BVISNULL( &base_entry.e_nname )
2064 && !access_allowed( op, &base_entry,
2065 slap_schema.si_ad_entry, NULL,
2066 ACL_DISCLOSE, NULL ) )
2068 rs->sr_err = LDAP_NO_SUCH_OBJECT;
2070 ber_bvarray_free( rs->sr_ref );
2073 rs->sr_matched = NULL;
2077 send_ldap_result( op, rs );
2080 ber_bvarray_free( rs->sr_ref );
2084 if ( !BER_BVISNULL( &base_entry.e_nname ) ) {
2085 entry_clean( &base_entry );
2090 /* NOTE: __NEW__ "search" access is required
2091 * on searchBase object */
2095 if ( get_assert( op ) &&
2096 ( test_filter( op, &base_entry, get_assertion( op ) )
2097 != LDAP_COMPARE_TRUE ) )
2099 rs->sr_err = LDAP_ASSERTION_FAILED;
2102 if ( ! access_allowed_mask( op, &base_entry,
2103 slap_schema.si_ad_entry,
2104 NULL, ACL_SEARCH, NULL, &mask ) )
2106 if ( rs->sr_err == LDAP_SUCCESS ) {
2107 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
2111 if ( rs->sr_err != LDAP_SUCCESS ) {
2112 if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
2113 rs->sr_err = LDAP_NO_SUCH_OBJECT;
2116 send_ldap_result( op, rs );
2123 bsi.bsi_n_candidates =
2124 ( op->ors_limit == NULL /* isroot == TRUE */ ? -2 :
2125 ( op->ors_limit->lms_s_unchecked == -1 ? -2 :
2126 ( op->ors_limit->lms_s_unchecked ) ) );
2128 /* If paged results are in effect, check the paging cookie */
2129 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
2130 rs->sr_err = parse_paged_cookie( op, rs );
2131 if ( rs->sr_err != LDAP_SUCCESS ) {
2132 send_ldap_result( op, rs );
2137 switch ( bsi.bsi_scope ) {
2138 case LDAP_SCOPE_BASE:
2139 case BACKSQL_SCOPE_BASE_LIKE:
2141 * probably already found...
2143 bsi.bsi_id_list = &bsi.bsi_base_id;
2144 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
2147 case LDAP_SCOPE_SUBTREE:
2149 * if baseObject is defined, and if it is the root
2150 * of the search, add it to the candidate list
2152 if ( bi->sql_baseObject && BACKSQL_IS_BASEOBJECT_ID( &bsi.bsi_base_id.eid_id ) )
2154 bsi.bsi_id_list = &bsi.bsi_base_id;
2155 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
2162 * for each objectclass we try to construct query which gets IDs
2163 * of entries matching LDAP query filter and scope (or at least
2164 * candidates), and get the IDs. Do this in ID order for paging.
2166 avl_apply( bi->sql_oc_by_id, backsql_oc_get_candidates,
2167 &bsi, BACKSQL_AVL_STOP, AVL_INORDER );
2169 /* check for abandon */
2170 if ( op->o_abandon ) {
2171 eid = bsi.bsi_id_list;
2172 rs->sr_err = SLAPD_ABANDON;
2177 if ( op->ors_limit != NULL /* isroot == FALSE */
2178 && op->ors_limit->lms_s_unchecked != -1
2179 && bsi.bsi_n_candidates == -1 )
2181 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
2182 send_ldap_result( op, rs );
2187 * now we load candidate entries (only those attributes
2188 * mentioned in attrs and filter), test it against full filter
2189 * and then send to client; don't free entry_id if baseObject...
2191 for ( eid = bsi.bsi_id_list;
2193 eid = backsql_free_entryID(
2194 eid, eid == &bsi.bsi_base_id ? 0 : 1, op->o_tmpmemctx ) )
2197 Attribute *a_hasSubordinate = NULL,
2198 *a_entryUUID = NULL,
2203 /* check for abandon */
2204 if ( op->o_abandon ) {
2205 rs->sr_err = SLAPD_ABANDON;
2209 /* check time limit */
2210 if ( op->ors_tlimit != SLAP_NO_LIMIT
2211 && slap_get_time() > stoptime )
2213 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
2214 rs->sr_ctrls = NULL;
2215 rs->sr_ref = rs->sr_v2ref;
2219 #ifdef BACKSQL_ARBITRARY_KEY
2220 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
2221 "for entry id=%s, oc_id=%ld, keyval=%s\n",
2222 eid->eid_id.bv_val, eid->eid_oc_id,
2223 eid->eid_keyval.bv_val );
2224 #else /* ! BACKSQL_ARBITRARY_KEY */
2225 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
2226 "for entry id=%ld, oc_id=%ld, keyval=%ld\n",
2227 eid->eid_id, eid->eid_oc_id, eid->eid_keyval );
2228 #endif /* ! BACKSQL_ARBITRARY_KEY */
2231 switch ( op->ors_scope ) {
2232 case LDAP_SCOPE_BASE:
2233 case BACKSQL_SCOPE_BASE_LIKE:
2234 if ( !dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
2239 case LDAP_SCOPE_ONE:
2241 struct berval rdn = eid->eid_ndn;
2243 rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
2244 if ( !dnIsOneLevelRDN( &rdn ) ) {
2250 case LDAP_SCOPE_SUBORDINATE:
2251 /* discard the baseObject entry */
2252 if ( dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
2256 case LDAP_SCOPE_SUBTREE:
2257 /* FIXME: this should never fail... */
2258 if ( !dnIsSuffix( &eid->eid_ndn, &op->o_req_ndn ) ) {
2264 if ( BACKSQL_IS_BASEOBJECT_ID( &eid->eid_id ) ) {
2265 /* don't recollect baseObject... */
2266 e = bi->sql_baseObject;
2268 } else if ( eid == &bsi.bsi_base_id ) {
2269 /* don't recollect searchBase object... */
2273 bsi.bsi_e = &user_entry;
2274 rc = backsql_id2entry( &bsi, eid );
2275 if ( rc != LDAP_SUCCESS ) {
2276 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2277 "error %d in backsql_id2entry() "
2278 "- skipping\n", rc, 0, 0 );
2284 if ( !manageDSAit &&
2285 op->ors_scope != LDAP_SCOPE_BASE &&
2286 op->ors_scope != BACKSQL_SCOPE_BASE_LIKE &&
2287 is_entry_referral( e ) )
2291 refs = get_entry_referrals( op, e );
2293 backsql_srch_info bsi2 = { 0 };
2294 Entry user_entry2 = { 0 };
2296 /* retry with the full entry... */
2297 bsi2.bsi_e = &user_entry2;
2298 rc = backsql_init_search( &bsi2,
2303 BACKSQL_ISF_GET_ENTRY );
2304 if ( rc == LDAP_SUCCESS ) {
2305 if ( is_entry_referral( &user_entry2 ) )
2307 refs = get_entry_referrals( op,
2310 rs->sr_err = LDAP_OTHER;
2312 backsql_entry_clean( op, &user_entry2 );
2314 if ( bsi2.bsi_attrs != NULL ) {
2315 op->o_tmpfree( bsi2.bsi_attrs,
2321 rs->sr_ref = referral_rewrite( refs,
2325 ber_bvarray_free( refs );
2329 rs->sr_err = LDAP_REFERRAL;
2332 rs->sr_text = "bad referral object";
2336 rs->sr_matched = user_entry.e_name.bv_val;
2337 send_search_reference( op, rs );
2339 ber_bvarray_free( rs->sr_ref );
2341 rs->sr_matched = NULL;
2342 rs->sr_entry = NULL;
2343 if ( rs->sr_err == LDAP_REFERRAL ) {
2344 rs->sr_err = LDAP_SUCCESS;
2351 * We use this flag since we need to parse the filter
2352 * anyway; we should have used the frontend API function
2353 * filter_has_subordinates()
2355 if ( bsi.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) {
2356 rc = backsql_has_children( op, dbh, &e->e_nname );
2359 case LDAP_COMPARE_TRUE:
2360 case LDAP_COMPARE_FALSE:
2361 a_hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE );
2362 if ( a_hasSubordinate != NULL ) {
2363 for ( ap = &user_entry.e_attrs;
2365 ap = &(*ap)->a_next );
2367 *ap = a_hasSubordinate;
2373 Debug(LDAP_DEBUG_TRACE,
2374 "backsql_search(): "
2375 "has_children failed( %d)\n",
2382 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYUUID ) {
2383 a_entryUUID = backsql_operational_entryUUID( bi, eid );
2384 if ( a_entryUUID != NULL ) {
2386 ap = &user_entry.e_attrs;
2389 for ( ; *ap; ap = &(*ap)->a_next );
2395 #ifdef BACKSQL_SYNCPROV
2396 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYCSN ) {
2397 a_entryCSN = backsql_operational_entryCSN( op );
2398 if ( a_entryCSN != NULL ) {
2400 ap = &user_entry.e_attrs;
2403 for ( ; *ap; ap = &(*ap)->a_next );
2408 #endif /* BACKSQL_SYNCPROV */
2410 if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE )
2412 /* If paged results are in effect, see if the page limit was exceeded */
2413 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
2414 if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size )
2417 send_paged_response( op, rs, &lastid );
2420 lastid = SQL_TO_PAGECOOKIE( eid->eid_id, eid->eid_oc_id );
2422 rs->sr_attrs = op->ors_attrs;
2423 rs->sr_operational_attrs = NULL;
2425 e->e_private = (void *)eid;
2426 rs->sr_flags = ( e == &user_entry ) ? REP_ENTRY_MODIFIABLE : 0;
2427 /* FIXME: need the whole entry (ITS#3480) */
2428 rs->sr_err = send_search_entry( op, rs );
2429 e->e_private = NULL;
2430 rs->sr_entry = NULL;
2431 rs->sr_attrs = NULL;
2432 rs->sr_operational_attrs = NULL;
2434 switch ( rs->sr_err ) {
2435 case LDAP_UNAVAILABLE:
2437 * FIXME: send_search_entry failed;
2440 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2441 "connection lost\n", 0, 0, 0 );
2444 case LDAP_SIZELIMIT_EXCEEDED:
2450 if ( e == &user_entry ) {
2451 backsql_entry_clean( op, &user_entry );
2458 if ( rs->sr_nentries > 0 ) {
2459 rs->sr_ref = rs->sr_v2ref;
2460 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
2464 rs->sr_err = bsi.bsi_status;
2468 if ( rs->sr_err != SLAPD_ABANDON ) {
2469 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
2470 send_paged_response( op, rs, NULL );
2472 send_ldap_result( op, rs );
2476 /* cleanup in case of abandon */
2477 for ( ; eid != NULL;
2478 eid = backsql_free_entryID(
2479 eid, eid == &bsi.bsi_base_id ? 0 : 1, op->o_tmpmemctx ) )
2482 backsql_entry_clean( op, &base_entry );
2484 /* in case we got here accidentally */
2485 backsql_entry_clean( op, &user_entry );
2487 if ( rs->sr_v2ref ) {
2488 ber_bvarray_free( rs->sr_v2ref );
2489 rs->sr_v2ref = NULL;
2492 #ifdef BACKSQL_SYNCPROV
2494 Operation op2 = *op;
2495 SlapReply rs2 = { 0 };
2496 Entry *e = entry_alloc();
2497 slap_callback cb = { 0 };
2499 op2.o_tag = LDAP_REQ_ADD;
2500 op2.o_bd = select_backend( &op->o_bd->be_nsuffix[0], 0 );
2502 op2.o_callback = &cb;
2504 ber_dupbv( &e->e_name, op->o_bd->be_suffix );
2505 ber_dupbv( &e->e_nname, op->o_bd->be_nsuffix );
2507 cb.sc_response = slap_null_cb;
2509 op2.o_bd->be_add( &op2, &rs2 );
2511 if ( op2.ora_e == e )
2514 #endif /* BACKSQL_SYNCPROV */
2517 (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
2519 if ( bsi.bsi_attrs != NULL ) {
2520 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
2523 if ( !BER_BVISNULL( &nbase )
2524 && nbase.bv_val != op->o_req_ndn.bv_val )
2526 ch_free( nbase.bv_val );
2529 /* restore scope ... FIXME: this should be done before ANY
2530 * frontend call that uses op */
2531 if ( op->ors_scope == BACKSQL_SCOPE_BASE_LIKE ) {
2532 op->ors_scope = LDAP_SCOPE_BASE;
2535 Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n", 0, 0, 0 );
2540 /* return LDAP_SUCCESS IFF we can retrieve the specified entry.
2547 AttributeDescription *at,
2551 backsql_srch_info bsi = { 0 };
2552 SQLHDBC dbh = SQL_NULL_HDBC;
2554 SlapReply rs = { 0 };
2555 AttributeName anlist[ 2 ];
2559 rc = backsql_get_db_conn( op, &dbh );
2560 if ( rc != LDAP_SUCCESS ) {
2565 anlist[ 0 ].an_name = at->ad_cname;
2566 anlist[ 0 ].an_desc = at;
2567 BER_BVZERO( &anlist[ 1 ].an_name );
2570 bsi.bsi_e = entry_alloc();
2571 rc = backsql_init_search( &bsi,
2575 dbh, op, &rs, at ? anlist : NULL,
2576 BACKSQL_ISF_GET_ENTRY );
2578 if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
2579 (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
2582 if ( rc == LDAP_SUCCESS ) {
2584 #if 0 /* not supported at present */
2585 /* find attribute values */
2586 if ( is_entry_alias( bsi.bsi_e ) ) {
2587 Debug( LDAP_DEBUG_ACL,
2588 "<= backsql_entry_get: entry is an alias\n",
2590 rc = LDAP_ALIAS_PROBLEM;
2591 goto return_results;
2595 if ( is_entry_referral( bsi.bsi_e ) ) {
2596 Debug( LDAP_DEBUG_ACL,
2597 "<= backsql_entry_get: entry is a referral\n",
2600 goto return_results;
2603 if ( oc && !is_entry_objectclass( bsi.bsi_e, oc, 0 ) ) {
2604 Debug( LDAP_DEBUG_ACL,
2605 "<= backsql_entry_get: "
2606 "failed to find objectClass\n",
2608 rc = LDAP_NO_SUCH_ATTRIBUTE;
2609 goto return_results;
2616 if ( bsi.bsi_attrs != NULL ) {
2617 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
2620 if ( rc != LDAP_SUCCESS ) {
2622 entry_free( bsi.bsi_e );
2630 backsql_entry_clean(
2636 ctx = ldap_pvt_thread_pool_context();
2638 if ( ctx == NULL || ctx != op->o_tmpmemctx ) {
2639 if ( !BER_BVISNULL( &e->e_name ) ) {
2640 op->o_tmpfree( e->e_name.bv_val, op->o_tmpmemctx );
2641 BER_BVZERO( &e->e_name );
2644 if ( !BER_BVISNULL( &e->e_nname ) ) {
2645 op->o_tmpfree( e->e_nname.bv_val, op->o_tmpmemctx );
2646 BER_BVZERO( &e->e_nname );
2654 backsql_entry_release(
2659 backsql_entry_clean( op, e );
2667 /* This function is copied verbatim from back-bdb/search.c */
2669 parse_paged_cookie( Operation *op, SlapReply *rs )
2671 int rc = LDAP_SUCCESS;
2672 PagedResultsState *ps = op->o_pagedresults_state;
2674 /* this function must be invoked only if the pagedResults
2675 * control has been detected, parsed and partially checked
2676 * by the frontend */
2677 assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
2679 /* cookie decoding/checks deferred to backend... */
2680 if ( ps->ps_cookieval.bv_len ) {
2681 PagedResultsCookie reqcookie;
2682 if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) {
2684 rs->sr_text = "paged results cookie is invalid";
2685 rc = LDAP_PROTOCOL_ERROR;
2689 AC_MEMCPY( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie ));
2691 if ( reqcookie > ps->ps_cookie ) {
2693 rs->sr_text = "paged results cookie is invalid";
2694 rc = LDAP_PROTOCOL_ERROR;
2697 } else if ( reqcookie < ps->ps_cookie ) {
2698 rs->sr_text = "paged results cookie is invalid or old";
2699 rc = LDAP_UNWILLING_TO_PERFORM;
2704 /* Initial request. Initialize state. */
2714 /* This function is copied nearly verbatim from back-bdb/search.c */
2716 send_paged_response(
2719 unsigned long *lastid )
2721 LDAPControl ctrl, *ctrls[2];
2722 BerElementBuffer berbuf;
2723 BerElement *ber = (BerElement *)&berbuf;
2724 PagedResultsCookie respcookie;
2725 struct berval cookie;
2727 Debug(LDAP_DEBUG_ARGS,
2728 "send_paged_response: lastid=0x%08lx nentries=%d\n",
2729 lastid ? *lastid : 0, rs->sr_nentries, NULL );
2731 BER_BVZERO( &ctrl.ldctl_value );
2735 ber_init2( ber, NULL, LBER_USE_DER );
2738 respcookie = ( PagedResultsCookie )(*lastid);
2739 cookie.bv_len = sizeof( respcookie );
2740 cookie.bv_val = (char *)&respcookie;
2743 respcookie = ( PagedResultsCookie )0;
2744 BER_BVSTR( &cookie, "" );
2747 op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
2748 op->o_conn->c_pagedresults_state.ps_count =
2749 ((PagedResultsState *)op->o_pagedresults_state)->ps_count +
2752 /* return size of 0 -- no estimate */
2753 ber_printf( ber, "{iO}", 0, &cookie );
2755 if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
2759 ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
2760 ctrls[0]->ldctl_iscritical = 0;
2762 rs->sr_ctrls = ctrls;
2763 rs->sr_err = LDAP_SUCCESS;
2764 send_ldap_result( op, rs );
2765 rs->sr_ctrls = NULL;
2768 (void) ber_free_buf( ber );