2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1999-2007 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;
82 for ( ; !BER_BVISNULL( &bsi->bsi_attrs[ n_attrs ].an_name ); n_attrs++ ) {
83 an = &bsi->bsi_attrs[ n_attrs ];
85 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
86 "attribute \"%s\" is in list\n",
87 an->an_name.bv_val, 0, 0 );
89 * We can live with strcmp because the attribute
90 * list has been normalized before calling be_search
92 if ( !BACKSQL_NCMP( &an->an_name, &ad->ad_cname ) ) {
97 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
98 "adding \"%s\" to list\n", ad->ad_cname.bv_val, 0, 0 );
100 an = (AttributeName *)bsi->bsi_op->o_tmprealloc( bsi->bsi_attrs,
101 sizeof( AttributeName ) * ( n_attrs + 2 ),
102 bsi->bsi_op->o_tmpmemctx );
107 an[ n_attrs ].an_name = ad->ad_cname;
108 an[ n_attrs ].an_desc = ad;
109 BER_BVZERO( &an[ n_attrs + 1 ].an_name );
117 * Initializes the search structure.
119 * If get_base_id != 0, the field bsi_base_id is filled
120 * with the entryID of bsi_base_ndn; it must be freed
121 * by backsql_free_entryID() when no longer required.
123 * NOTE: base must be normalized
127 backsql_srch_info *bsi,
128 struct berval *nbase,
135 AttributeName *attrs,
138 backsql_info *bi = (backsql_info *)op->o_bd->be_private;
139 int rc = LDAP_SUCCESS;
141 bsi->bsi_base_ndn = nbase;
142 bsi->bsi_use_subtree_shortcut = 0;
143 BER_BVZERO( &bsi->bsi_base_id.eid_dn );
144 BER_BVZERO( &bsi->bsi_base_id.eid_ndn );
145 bsi->bsi_scope = scope;
146 bsi->bsi_filter = filter;
150 bsi->bsi_flags = BSQL_SF_NONE;
152 bsi->bsi_attrs = NULL;
154 if ( BACKSQL_FETCH_ALL_ATTRS( bi ) ) {
156 * if requested, simply try to fetch all attributes
158 bsi->bsi_flags |= BSQL_SF_ALL_ATTRS;
161 if ( BACKSQL_FETCH_ALL_USERATTRS( bi ) ) {
162 bsi->bsi_flags |= BSQL_SF_ALL_USER;
164 } else if ( BACKSQL_FETCH_ALL_OPATTRS( bi ) ) {
165 bsi->bsi_flags |= BSQL_SF_ALL_OPER;
168 if ( attrs == NULL ) {
169 /* NULL means all user attributes */
170 bsi->bsi_flags |= BSQL_SF_ALL_USER;
176 bsi->bsi_attrs = (AttributeName *)bsi->bsi_op->o_tmpalloc(
177 sizeof( AttributeName ),
178 bsi->bsi_op->o_tmpmemctx );
179 BER_BVZERO( &bsi->bsi_attrs[ 0 ].an_name );
181 for ( p = attrs; !BER_BVISNULL( &p->an_name ); p++ ) {
182 if ( BACKSQL_NCMP( &p->an_name, &AllUser ) == 0 ) {
184 bsi->bsi_flags |= BSQL_SF_ALL_USER;
186 /* if all attrs are requested, there's
187 * no need to continue */
188 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
189 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
190 bsi->bsi_op->o_tmpmemctx );
191 bsi->bsi_attrs = NULL;
196 } else if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 ) {
198 bsi->bsi_flags |= BSQL_SF_ALL_OPER;
200 /* if all attrs are requested, there's
201 * no need to continue */
202 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
203 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
204 bsi->bsi_op->o_tmpmemctx );
205 bsi->bsi_attrs = NULL;
210 } else if ( BACKSQL_NCMP( &p->an_name, &NoAttrs ) == 0 ) {
214 } else if ( p->an_desc == slap_schema.si_ad_objectClass ) {
218 backsql_attrlist_add( bsi, p->an_desc );
221 if ( got_oc == 0 && !( bsi->bsi_flags & BSQL_SF_ALL_USER ) ) {
222 /* add objectClass if not present,
223 * because it is required to understand
224 * if an entry is a referral, an alias
226 backsql_attrlist_add( bsi, slap_schema.si_ad_objectClass );
230 if ( !BSQL_ISF_ALL_ATTRS( bsi ) && bi->sql_anlist ) {
233 /* use hints if available */
234 for ( p = bi->sql_anlist; !BER_BVISNULL( &p->an_name ); p++ ) {
235 if ( BACKSQL_NCMP( &p->an_name, &AllUser ) == 0 ) {
237 bsi->bsi_flags |= BSQL_SF_ALL_USER;
239 /* if all attrs are requested, there's
240 * no need to continue */
241 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
242 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
243 bsi->bsi_op->o_tmpmemctx );
244 bsi->bsi_attrs = NULL;
249 } else if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 ) {
251 bsi->bsi_flags |= BSQL_SF_ALL_OPER;
253 /* if all attrs are requested, there's
254 * no need to continue */
255 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
256 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
257 bsi->bsi_op->o_tmpmemctx );
258 bsi->bsi_attrs = NULL;
264 backsql_attrlist_add( bsi, p->an_desc );
270 bsi->bsi_id_list = NULL;
271 bsi->bsi_id_listtail = &bsi->bsi_id_list;
272 bsi->bsi_n_candidates = 0;
273 bsi->bsi_stoptime = stoptime;
274 BER_BVZERO( &bsi->bsi_sel.bb_val );
275 bsi->bsi_sel.bb_len = 0;
276 BER_BVZERO( &bsi->bsi_from.bb_val );
277 bsi->bsi_from.bb_len = 0;
278 BER_BVZERO( &bsi->bsi_join_where.bb_val );
279 bsi->bsi_join_where.bb_len = 0;
280 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
281 bsi->bsi_flt_where.bb_len = 0;
282 bsi->bsi_filter_oc = NULL;
284 if ( BACKSQL_IS_GET_ID( flags ) ) {
285 int matched = BACKSQL_IS_MATCHED( flags );
286 int getentry = BACKSQL_IS_GET_ENTRY( flags );
289 assert( op->o_bd->be_private != NULL );
291 rc = backsql_dn2id( op, rs, dbh, nbase, &bsi->bsi_base_id,
294 /* the entry is collected either if requested for by getentry
295 * or if get noSuchObject and requested to climb the tree,
296 * so that a matchedDN or a referral can be returned */
297 if ( ( rc == LDAP_NO_SUCH_OBJECT && matched ) || getentry ) {
298 if ( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) ) {
299 assert( bsi->bsi_e != NULL );
301 if ( dn_match( nbase, &bsi->bsi_base_id.eid_ndn ) )
307 * let's see if it is a referral and, in case, get it
309 backsql_attrlist_add( bsi, slap_schema.si_ad_ref );
310 rc = backsql_id2entry( bsi, &bsi->bsi_base_id );
311 if ( rc == LDAP_SUCCESS ) {
312 if ( is_entry_referral( bsi->bsi_e ) )
314 BerVarray erefs = get_entry_referrals( op, bsi->bsi_e );
316 rc = rs->sr_err = LDAP_REFERRAL;
317 rs->sr_ref = referral_rewrite( erefs,
318 &bsi->bsi_e->e_nname,
321 ber_bvarray_free( erefs );
324 rc = rs->sr_err = LDAP_OTHER;
325 rs->sr_text = "bad referral object";
328 } else if ( !gotit ) {
329 rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
339 bsi->bsi_status = rc;
347 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
348 bsi->bsi_op->o_tmpmemctx );
356 backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op )
364 backsql_strfcat_x( &bsi->bsi_flt_where,
365 bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */ );
368 res = backsql_process_filter( bsi, f );
371 * TimesTen : If the query has no answers,
372 * don't bother to run the query.
383 case LDAP_FILTER_AND:
384 backsql_strfcat_x( &bsi->bsi_flt_where,
385 bsi->bsi_op->o_tmpmemctx, "l",
386 (ber_len_t)STRLENOF( " AND " ),
391 backsql_strfcat_x( &bsi->bsi_flt_where,
392 bsi->bsi_op->o_tmpmemctx, "l",
393 (ber_len_t)STRLENOF( " OR " ),
399 backsql_strfcat_x( &bsi->bsi_flt_where,
400 bsi->bsi_op->o_tmpmemctx, "c", /* ( */ ')' );
406 backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f,
407 backsql_at_map_rec *at )
409 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
417 /* always uppercase strings by now */
418 #ifdef BACKSQL_UPPERCASE_FILTER
419 if ( f->f_sub_desc->ad_type->sat_substr &&
420 SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
421 bi->sql_caseIgnoreMatch ) )
422 #endif /* BACKSQL_UPPERCASE_FILTER */
427 if ( f->f_sub_desc->ad_type->sat_substr &&
428 SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
429 bi->sql_telephoneNumberMatch ) )
436 * to check for matching telephone numbers
437 * with intermixed chars, e.g. val='1234'
440 * val LIKE '%1%2%3%4%'
444 if ( f->f_sub_initial.bv_val ) {
445 bv.bv_len += f->f_sub_initial.bv_len;
447 if ( f->f_sub_any != NULL ) {
448 for ( a = 0; f->f_sub_any[ a ].bv_val != NULL; a++ ) {
449 bv.bv_len += f->f_sub_any[ a ].bv_len;
452 if ( f->f_sub_final.bv_val ) {
453 bv.bv_len += f->f_sub_final.bv_len;
455 bv.bv_len = 2 * bv.bv_len - 1;
456 bv.bv_val = ch_malloc( bv.bv_len + 1 );
459 if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
460 bv.bv_val[ s ] = f->f_sub_initial.bv_val[ 0 ];
461 for ( i = 1; i < f->f_sub_initial.bv_len; i++ ) {
462 bv.bv_val[ s + 2 * i - 1 ] = '%';
463 bv.bv_val[ s + 2 * i ] = f->f_sub_initial.bv_val[ i ];
465 bv.bv_val[ s + 2 * i - 1 ] = '%';
469 if ( f->f_sub_any != NULL ) {
470 for ( a = 0; !BER_BVISNULL( &f->f_sub_any[ a ] ); a++ ) {
471 bv.bv_val[ s ] = f->f_sub_any[ a ].bv_val[ 0 ];
472 for ( i = 1; i < f->f_sub_any[ a ].bv_len; i++ ) {
473 bv.bv_val[ s + 2 * i - 1 ] = '%';
474 bv.bv_val[ s + 2 * i ] = f->f_sub_any[ a ].bv_val[ i ];
476 bv.bv_val[ s + 2 * i - 1 ] = '%';
481 if ( !BER_BVISNULL( &f->f_sub_final ) ) {
482 bv.bv_val[ s ] = f->f_sub_final.bv_val[ 0 ];
483 for ( i = 1; i < f->f_sub_final.bv_len; i++ ) {
484 bv.bv_val[ s + 2 * i - 1 ] = '%';
485 bv.bv_val[ s + 2 * i ] = f->f_sub_final.bv_val[ i ];
487 bv.bv_val[ s + 2 * i - 1 ] = '%';
491 bv.bv_val[ s - 1 ] = '\0';
493 (void)backsql_process_filter_like( bsi, at, casefold, &bv );
494 ch_free( bv.bv_val );
500 * When dealing with case-sensitive strings
501 * we may omit normalization; however, normalized
502 * SQL filters are more liberal.
505 backsql_strfcat_x( &bsi->bsi_flt_where,
506 bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */ );
509 Debug( LDAP_DEBUG_TRACE, "backsql_process_sub_filter(%s):\n",
510 at->bam_ad->ad_cname.bv_val, 0, 0 );
511 Debug(LDAP_DEBUG_TRACE, " expr: '%s%s%s'\n", at->bam_sel_expr.bv_val,
512 at->bam_sel_expr_u.bv_val ? "' '" : "",
513 at->bam_sel_expr_u.bv_val ? at->bam_sel_expr_u.bv_val : "" );
514 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
516 * If a pre-upper-cased version of the column
517 * or a precompiled upper function exists, use it
519 backsql_strfcat_x( &bsi->bsi_flt_where,
520 bsi->bsi_op->o_tmpmemctx,
523 (ber_len_t)STRLENOF( " LIKE '" ),
527 backsql_strfcat_x( &bsi->bsi_flt_where,
528 bsi->bsi_op->o_tmpmemctx,
531 (ber_len_t)STRLENOF( " LIKE '" ), " LIKE '" );
534 if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
538 Debug( LDAP_DEBUG_TRACE,
539 "==>backsql_process_sub_filter(%s): "
540 "sub_initial=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
541 f->f_sub_initial.bv_val, 0 );
542 #endif /* BACKSQL_TRACE */
544 start = bsi->bsi_flt_where.bb_val.bv_len;
545 backsql_strfcat_x( &bsi->bsi_flt_where,
546 bsi->bsi_op->o_tmpmemctx,
549 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
550 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
554 backsql_strfcat_x( &bsi->bsi_flt_where,
555 bsi->bsi_op->o_tmpmemctx,
558 if ( f->f_sub_any != NULL ) {
559 for ( i = 0; !BER_BVISNULL( &f->f_sub_any[ i ] ); i++ ) {
563 Debug( LDAP_DEBUG_TRACE,
564 "==>backsql_process_sub_filter(%s): "
565 "sub_any[%d]=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
566 i, f->f_sub_any[ i ].bv_val );
567 #endif /* BACKSQL_TRACE */
569 start = bsi->bsi_flt_where.bb_val.bv_len;
570 backsql_strfcat_x( &bsi->bsi_flt_where,
571 bsi->bsi_op->o_tmpmemctx,
575 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
577 * Note: toupper('%') = '%'
579 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
584 if ( !BER_BVISNULL( &f->f_sub_final ) ) {
588 Debug( LDAP_DEBUG_TRACE,
589 "==>backsql_process_sub_filter(%s): "
590 "sub_final=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
591 f->f_sub_final.bv_val, 0 );
592 #endif /* BACKSQL_TRACE */
594 start = bsi->bsi_flt_where.bb_val.bv_len;
595 backsql_strfcat_x( &bsi->bsi_flt_where,
596 bsi->bsi_op->o_tmpmemctx,
599 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
600 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
604 backsql_strfcat_x( &bsi->bsi_flt_where,
605 bsi->bsi_op->o_tmpmemctx,
607 (ber_len_t)STRLENOF( /* (' */ "')" ), /* (' */ "')" );
613 backsql_merge_from_tbls( backsql_srch_info *bsi, struct berval *from_tbls )
615 if ( BER_BVISNULL( from_tbls ) ) {
619 if ( !BER_BVISNULL( &bsi->bsi_from.bb_val ) ) {
623 ber_dupbv_x( &tmp, from_tbls, bsi->bsi_op->o_tmpmemctx );
625 for ( start = tmp.bv_val, end = strchr( start, ',' ); start; ) {
630 if ( strstr( bsi->bsi_from.bb_val.bv_val, start) == NULL )
632 backsql_strfcat_x( &bsi->bsi_from,
633 bsi->bsi_op->o_tmpmemctx,
638 /* in case there are spaces after the comma... */
639 for ( start = &end[1]; isspace( start[0] ); start++ );
641 end = strchr( start, ',' );
650 bsi->bsi_op->o_tmpfree( tmp.bv_val, bsi->bsi_op->o_tmpmemctx );
653 backsql_strfcat_x( &bsi->bsi_from,
654 bsi->bsi_op->o_tmpmemctx,
662 backsql_process_filter( backsql_srch_info *bsi, Filter *f )
664 backsql_at_map_rec **vat = NULL;
665 AttributeDescription *ad = NULL;
670 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter()\n", 0, 0, 0 );
671 if ( f->f_choice == SLAPD_FILTER_COMPUTED ) {
675 switch ( f->f_result ) {
676 case LDAP_COMPARE_TRUE:
677 BER_BVSTR( &flt, "10=10" );
681 case LDAP_COMPARE_FALSE:
682 BER_BVSTR( &flt, "11=0" );
686 case SLAPD_COMPARE_UNDEFINED:
687 BER_BVSTR( &flt, "12=0" );
696 Debug( LDAP_DEBUG_TRACE, "backsql_process_filter(): "
697 "filter computed (%s)\n", msg, 0, 0 );
698 backsql_strfcat_x( &bsi->bsi_flt_where,
699 bsi->bsi_op->o_tmpmemctx, "b", &flt );
704 switch( f->f_choice ) {
706 rc = backsql_process_filter_list( bsi, f->f_or,
711 case LDAP_FILTER_AND:
712 rc = backsql_process_filter_list( bsi, f->f_and,
717 case LDAP_FILTER_NOT:
718 backsql_strfcat_x( &bsi->bsi_flt_where,
719 bsi->bsi_op->o_tmpmemctx,
721 (ber_len_t)STRLENOF( "NOT (" /* ) */ ),
723 rc = backsql_process_filter( bsi, f->f_not );
724 backsql_strfcat_x( &bsi->bsi_flt_where,
725 bsi->bsi_op->o_tmpmemctx,
730 case LDAP_FILTER_PRESENT:
734 case LDAP_FILTER_EXT:
735 ad = f->f_mra->ma_desc;
736 if ( f->f_mr_dnattrs ) {
738 * if dn attrs filtering is requested, better return
739 * success and let test_filter() deal with candidate
740 * selection; otherwise we'd need to set conditions
741 * on the contents of the DN, e.g. "SELECT ... FROM
742 * ldap_entries AS attributeName WHERE attributeName.dn
743 * like '%attributeName=value%'"
745 backsql_strfcat_x( &bsi->bsi_flt_where,
746 bsi->bsi_op->o_tmpmemctx,
748 (ber_len_t)STRLENOF( "1=1" ), "1=1" );
749 bsi->bsi_status = LDAP_SUCCESS;
770 * Turn structuralObjectClass into objectClass
772 if ( ad == slap_schema.si_ad_objectClass
773 || ad == slap_schema.si_ad_structuralObjectClass )
776 * If the filter is LDAP_FILTER_PRESENT, then it's done;
777 * otherwise, let's see if we are lucky: filtering
778 * for "structural" objectclass or ancestor...
780 switch ( f->f_choice ) {
781 case LDAP_FILTER_EQUALITY:
783 ObjectClass *oc = oc_bvfind( &f->f_av_value );
786 Debug( LDAP_DEBUG_TRACE,
787 "backsql_process_filter(): "
788 "unknown objectClass \"%s\" "
790 f->f_av_value.bv_val, 0, 0 );
791 bsi->bsi_status = LDAP_OTHER;
797 * "structural" objectClass inheritance:
798 * - a search for "person" will also return
800 * - a search for "top" will return everything
802 if ( is_object_subclass( oc, bsi->bsi_oc->bom_oc ) ) {
803 static struct berval ldap_entry_objclasses = BER_BVC( "ldap_entry_objclasses" );
805 backsql_merge_from_tbls( bsi, &ldap_entry_objclasses );
807 backsql_strfcat_x( &bsi->bsi_flt_where,
808 bsi->bsi_op->o_tmpmemctx,
810 (ber_len_t)STRLENOF( "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */ ),
811 "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */,
812 &bsi->bsi_oc->bom_oc->soc_cname,
813 (ber_len_t)STRLENOF( /* ((' */ "'))" ),
815 bsi->bsi_status = LDAP_SUCCESS;
823 case LDAP_FILTER_PRESENT:
824 backsql_strfcat_x( &bsi->bsi_flt_where,
825 bsi->bsi_op->o_tmpmemctx,
827 (ber_len_t)STRLENOF( "3=3" ), "3=3" );
828 bsi->bsi_status = LDAP_SUCCESS;
832 /* FIXME: LDAP_FILTER_EXT? */
835 Debug( LDAP_DEBUG_TRACE,
836 "backsql_process_filter(): "
837 "illegal/unhandled filter "
838 "on objectClass attribute",
840 bsi->bsi_status = LDAP_OTHER;
845 } else if ( ad == slap_schema.si_ad_entryUUID ) {
847 #ifdef BACKSQL_ARBITRARY_KEY
848 struct berval keyval;
849 #else /* ! BACKSQL_ARBITRARY_KEY */
850 unsigned long keyval;
851 char keyvalbuf[] = "18446744073709551615";
852 #endif /* ! BACKSQL_ARBITRARY_KEY */
854 switch ( f->f_choice ) {
855 case LDAP_FILTER_EQUALITY:
856 backsql_entryUUID_decode( &f->f_av_value, &oc_id, &keyval );
858 if ( oc_id != bsi->bsi_oc->bom_id ) {
859 bsi->bsi_status = LDAP_SUCCESS;
864 #ifdef BACKSQL_ARBITRARY_KEY
865 backsql_strfcat_x( &bsi->bsi_flt_where,
866 bsi->bsi_op->o_tmpmemctx,
868 &bsi->bsi_oc->bom_keytbl, '.',
869 &bsi->bsi_oc->bom_keycol,
870 STRLENOF( " LIKE '" ), " LIKE '",
872 #else /* ! BACKSQL_ARBITRARY_KEY */
873 snprintf( keyvalbuf, sizeof( keyvalbuf ), "%lu", keyval );
874 backsql_strfcat_x( &bsi->bsi_flt_where,
875 bsi->bsi_op->o_tmpmemctx,
877 &bsi->bsi_oc->bom_keytbl, '.',
878 &bsi->bsi_oc->bom_keycol, '=', keyvalbuf );
879 #endif /* ! BACKSQL_ARBITRARY_KEY */
882 case LDAP_FILTER_PRESENT:
883 backsql_strfcat_x( &bsi->bsi_flt_where,
884 bsi->bsi_op->o_tmpmemctx,
886 (ber_len_t)STRLENOF( "4=4" ), "4=4" );
894 bsi->bsi_flags |= BSQL_SF_FILTER_ENTRYUUID;
898 #ifdef BACKSQL_SYNCPROV
899 } else if ( ad == slap_schema.si_ad_entryCSN ) {
901 * support for syncrepl as producer...
904 if ( !bsi->bsi_op->o_sync ) {
905 /* unsupported at present... */
906 bsi->bsi_status = LDAP_OTHER;
912 bsi->bsi_flags |= ( BSQL_SF_FILTER_ENTRYCSN | BSQL_SF_RETURN_ENTRYUUID);
914 /* if doing a syncrepl, try to return as much as possible,
915 * and always match the filter */
916 backsql_strfcat_x( &bsi->bsi_flt_where,
917 bsi->bsi_op->o_tmpmemctx,
919 (ber_len_t)STRLENOF( "5=5" ), "5=5" );
921 /* save for later use in operational attributes */
922 /* FIXME: saves only the first occurrence, because
923 * the filter during updates is written as
924 * "(&(entryCSN<={contextCSN})(entryCSN>={oldContextCSN})({filter}))"
925 * so we want our fake entryCSN to match the greatest
928 if ( bsi->bsi_op->o_private == NULL ) {
929 bsi->bsi_op->o_private = &f->f_av_value;
931 bsi->bsi_status = LDAP_SUCCESS;
935 #endif /* BACKSQL_SYNCPROV */
937 } else if ( ad == slap_schema.si_ad_hasSubordinates || ad == NULL ) {
939 * FIXME: this is not robust; e.g. a filter
940 * '(!(hasSubordinates=TRUE))' fails because
941 * in SQL it would read 'NOT (1=1)' instead
943 * Note however that hasSubordinates is boolean,
944 * so a more appropriate filter would be
945 * '(hasSubordinates=FALSE)'
947 * A more robust search for hasSubordinates
948 * would * require joining the ldap_entries table
949 * selecting if there are descendants of the
952 backsql_strfcat_x( &bsi->bsi_flt_where,
953 bsi->bsi_op->o_tmpmemctx,
955 (ber_len_t)STRLENOF( "6=6" ), "6=6" );
956 if ( ad == slap_schema.si_ad_hasSubordinates ) {
958 * instruct candidate selection algorithm
959 * and attribute list to try to detect
960 * if an entry has subordinates
962 bsi->bsi_flags |= BSQL_SF_FILTER_HASSUBORDINATE;
966 * clear attributes to fetch, to require ALL
967 * and try extended match on all attributes
969 backsql_attrlist_add( bsi, NULL );
976 * attribute inheritance:
978 if ( backsql_supad2at( bsi->bsi_oc, ad, &vat ) ) {
979 bsi->bsi_status = LDAP_OTHER;
985 /* search anyway; other parts of the filter
987 backsql_strfcat_x( &bsi->bsi_flt_where,
988 bsi->bsi_op->o_tmpmemctx,
990 (ber_len_t)STRLENOF( "7=7" ), "7=7" );
991 bsi->bsi_status = LDAP_SUCCESS;
996 /* if required, open extra level of parens */
998 if ( vat[0]->bam_next || vat[1] ) {
999 backsql_strfcat_x( &bsi->bsi_flt_where,
1000 bsi->bsi_op->o_tmpmemctx,
1008 if ( backsql_process_filter_attr( bsi, f, vat[i] ) == -1 ) {
1012 /* if more definitions of the same attr, apply */
1013 if ( vat[i]->bam_next ) {
1014 backsql_strfcat_x( &bsi->bsi_flt_where,
1015 bsi->bsi_op->o_tmpmemctx,
1017 STRLENOF( " OR " ), " OR " );
1018 vat[i] = vat[i]->bam_next;
1022 /* if more descendants of the same attr, apply */
1025 backsql_strfcat_x( &bsi->bsi_flt_where,
1026 bsi->bsi_op->o_tmpmemctx,
1028 STRLENOF( " OR " ), " OR " );
1032 /* if needed, close extra level of parens */
1034 backsql_strfcat_x( &bsi->bsi_flt_where,
1035 bsi->bsi_op->o_tmpmemctx,
1046 Debug( LDAP_DEBUG_TRACE,
1047 "<==backsql_process_filter() %s\n",
1048 rc == 1 ? "succeeded" : "failed", 0, 0);
1054 backsql_process_filter_eq( backsql_srch_info *bsi, backsql_at_map_rec *at,
1055 int casefold, struct berval *filter_value )
1058 * maybe we should check type of at->sel_expr here somehow,
1059 * to know whether upper_func is applicable, but for now
1060 * upper_func stuff is made for Oracle, where UPPER is
1061 * safely applicable to NUMBER etc.
1063 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1066 backsql_strfcat_x( &bsi->bsi_flt_where,
1067 bsi->bsi_op->o_tmpmemctx,
1070 &at->bam_sel_expr_u,
1071 (ber_len_t)STRLENOF( "='" ),
1074 start = bsi->bsi_flt_where.bb_val.bv_len;
1076 backsql_strfcat_x( &bsi->bsi_flt_where,
1077 bsi->bsi_op->o_tmpmemctx,
1080 (ber_len_t)STRLENOF( /* (' */ "')" ),
1083 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1086 backsql_strfcat_x( &bsi->bsi_flt_where,
1087 bsi->bsi_op->o_tmpmemctx,
1091 (ber_len_t)STRLENOF( "='" ), "='",
1093 (ber_len_t)STRLENOF( /* (' */ "')" ),
1101 backsql_process_filter_like( backsql_srch_info *bsi, backsql_at_map_rec *at,
1102 int casefold, struct berval *filter_value )
1105 * maybe we should check type of at->sel_expr here somehow,
1106 * to know whether upper_func is applicable, but for now
1107 * upper_func stuff is made for Oracle, where UPPER is
1108 * safely applicable to NUMBER etc.
1110 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1113 backsql_strfcat_x( &bsi->bsi_flt_where,
1114 bsi->bsi_op->o_tmpmemctx,
1117 &at->bam_sel_expr_u,
1118 (ber_len_t)STRLENOF( " LIKE '%" ),
1121 start = bsi->bsi_flt_where.bb_val.bv_len;
1123 backsql_strfcat_x( &bsi->bsi_flt_where,
1124 bsi->bsi_op->o_tmpmemctx,
1127 (ber_len_t)STRLENOF( /* (' */ "%')" ),
1130 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1133 backsql_strfcat_x( &bsi->bsi_flt_where,
1134 bsi->bsi_op->o_tmpmemctx,
1138 (ber_len_t)STRLENOF( " LIKE '%" ),
1141 (ber_len_t)STRLENOF( /* (' */ "%')" ),
1149 backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f, backsql_at_map_rec *at )
1151 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1153 struct berval *filter_value = NULL;
1154 MatchingRule *matching_rule = NULL;
1155 struct berval ordering = BER_BVC("<=");
1157 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter_attr(%s)\n",
1158 at->bam_ad->ad_cname.bv_val, 0, 0 );
1161 * need to add this attribute to list of attrs to load,
1162 * so that we can do test_filter() later
1164 backsql_attrlist_add( bsi, at->bam_ad );
1166 backsql_merge_from_tbls( bsi, &at->bam_from_tbls );
1168 if ( !BER_BVISNULL( &at->bam_join_where )
1169 && strstr( bsi->bsi_join_where.bb_val.bv_val,
1170 at->bam_join_where.bv_val ) == NULL )
1172 backsql_strfcat_x( &bsi->bsi_join_where,
1173 bsi->bsi_op->o_tmpmemctx,
1175 (ber_len_t)STRLENOF( " AND " ), " AND ",
1176 &at->bam_join_where );
1179 switch ( f->f_choice ) {
1180 case LDAP_FILTER_EQUALITY:
1181 filter_value = &f->f_av_value;
1182 matching_rule = at->bam_ad->ad_type->sat_equality;
1184 goto equality_match;
1186 /* fail over into next case */
1188 case LDAP_FILTER_EXT:
1189 filter_value = &f->f_mra->ma_value;
1190 matching_rule = f->f_mr_rule;
1193 /* always uppercase strings by now */
1194 #ifdef BACKSQL_UPPERCASE_FILTER
1195 if ( SLAP_MR_ASSOCIATED( matching_rule,
1196 bi->sql_caseIgnoreMatch ) )
1197 #endif /* BACKSQL_UPPERCASE_FILTER */
1202 /* FIXME: directoryString filtering should use a similar
1203 * approach to deal with non-prettified values like
1204 * " A non prettified value ", by using a LIKE
1205 * filter with all whitespaces collapsed to a single '%' */
1206 if ( SLAP_MR_ASSOCIATED( matching_rule,
1207 bi->sql_telephoneNumberMatch ) )
1213 * to check for matching telephone numbers
1214 * with intermized chars, e.g. val='1234'
1217 * val LIKE '%1%2%3%4%'
1220 bv.bv_len = 2 * filter_value->bv_len - 1;
1221 bv.bv_val = ch_malloc( bv.bv_len + 1 );
1223 bv.bv_val[ 0 ] = filter_value->bv_val[ 0 ];
1224 for ( i = 1; i < filter_value->bv_len; i++ ) {
1225 bv.bv_val[ 2 * i - 1 ] = '%';
1226 bv.bv_val[ 2 * i ] = filter_value->bv_val[ i ];
1228 bv.bv_val[ 2 * i - 1 ] = '\0';
1230 (void)backsql_process_filter_like( bsi, at, casefold, &bv );
1231 ch_free( bv.bv_val );
1236 /* NOTE: this is required by objectClass inheritance
1237 * and auxiliary objectClass use in filters for slightly
1238 * more efficient candidate selection. */
1239 /* FIXME: a bit too many specializations to deal with
1240 * very specific cases... */
1241 if ( at->bam_ad == slap_schema.si_ad_objectClass
1242 || at->bam_ad == slap_schema.si_ad_structuralObjectClass )
1244 backsql_strfcat_x( &bsi->bsi_flt_where,
1245 bsi->bsi_op->o_tmpmemctx,
1247 (ber_len_t)STRLENOF( "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */ ),
1248 "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */,
1250 (ber_len_t)STRLENOF( /* (' */ "')" ),
1256 * maybe we should check type of at->sel_expr here somehow,
1257 * to know whether upper_func is applicable, but for now
1258 * upper_func stuff is made for Oracle, where UPPER is
1259 * safely applicable to NUMBER etc.
1261 (void)backsql_process_filter_eq( bsi, at, casefold, filter_value );
1264 case LDAP_FILTER_GE:
1265 ordering.bv_val = ">=";
1267 /* fall thru to next case */
1269 case LDAP_FILTER_LE:
1270 filter_value = &f->f_av_value;
1272 /* always uppercase strings by now */
1273 #ifdef BACKSQL_UPPERCASE_FILTER
1274 if ( at->bam_ad->ad_type->sat_ordering &&
1275 SLAP_MR_ASSOCIATED( at->bam_ad->ad_type->sat_ordering,
1276 bi->sql_caseIgnoreMatch ) )
1277 #endif /* BACKSQL_UPPERCASE_FILTER */
1283 * FIXME: should we uppercase the operands?
1285 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1288 backsql_strfcat_x( &bsi->bsi_flt_where,
1289 bsi->bsi_op->o_tmpmemctx,
1292 &at->bam_sel_expr_u,
1296 start = bsi->bsi_flt_where.bb_val.bv_len;
1298 backsql_strfcat_x( &bsi->bsi_flt_where,
1299 bsi->bsi_op->o_tmpmemctx,
1302 (ber_len_t)STRLENOF( /* (' */ "')" ),
1305 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1308 backsql_strfcat_x( &bsi->bsi_flt_where,
1309 bsi->bsi_op->o_tmpmemctx,
1316 (ber_len_t)STRLENOF( /* (' */ "')" ),
1321 case LDAP_FILTER_PRESENT:
1322 backsql_strfcat_x( &bsi->bsi_flt_where,
1323 bsi->bsi_op->o_tmpmemctx,
1325 (ber_len_t)STRLENOF( "NOT (" /* ) */),
1328 (ber_len_t)STRLENOF( /* ( */ " IS NULL)" ),
1329 /* ( */ " IS NULL)" );
1332 case LDAP_FILTER_SUBSTRINGS:
1333 backsql_process_sub_filter( bsi, f, at );
1336 case LDAP_FILTER_APPROX:
1337 /* we do our best */
1340 * maybe we should check type of at->sel_expr here somehow,
1341 * to know whether upper_func is applicable, but for now
1342 * upper_func stuff is made for Oracle, where UPPER is
1343 * safely applicable to NUMBER etc.
1345 (void)backsql_process_filter_like( bsi, at, 1, &f->f_av_value );
1349 /* unhandled filter type; should not happen */
1351 backsql_strfcat_x( &bsi->bsi_flt_where,
1352 bsi->bsi_op->o_tmpmemctx,
1354 (ber_len_t)STRLENOF( "8=8" ), "8=8" );
1359 Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter_attr(%s)\n",
1360 at->bam_ad->ad_cname.bv_val, 0, 0 );
1366 backsql_srch_query( backsql_srch_info *bsi, struct berval *query )
1368 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1371 assert( query != NULL );
1372 BER_BVZERO( query );
1374 bsi->bsi_use_subtree_shortcut = 0;
1376 Debug( LDAP_DEBUG_TRACE, "==>backsql_srch_query()\n", 0, 0, 0 );
1377 BER_BVZERO( &bsi->bsi_sel.bb_val );
1378 BER_BVZERO( &bsi->bsi_sel.bb_val );
1379 bsi->bsi_sel.bb_len = 0;
1380 BER_BVZERO( &bsi->bsi_from.bb_val );
1381 bsi->bsi_from.bb_len = 0;
1382 BER_BVZERO( &bsi->bsi_join_where.bb_val );
1383 bsi->bsi_join_where.bb_len = 0;
1384 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1385 bsi->bsi_flt_where.bb_len = 0;
1387 backsql_strfcat_x( &bsi->bsi_sel,
1388 bsi->bsi_op->o_tmpmemctx,
1390 (ber_len_t)STRLENOF( "SELECT DISTINCT ldap_entries.id," ),
1391 "SELECT DISTINCT ldap_entries.id,",
1392 &bsi->bsi_oc->bom_keytbl,
1394 &bsi->bsi_oc->bom_keycol,
1397 if ( !BER_BVISNULL( &bi->sql_strcast_func ) ) {
1398 backsql_strfcat_x( &bsi->bsi_sel,
1399 bsi->bsi_op->o_tmpmemctx,
1401 &bi->sql_strcast_func,
1402 (ber_len_t)STRLENOF( "('" /* ') */ ),
1404 &bsi->bsi_oc->bom_oc->soc_cname,
1405 (ber_len_t)STRLENOF( /* (' */ "')" ),
1408 backsql_strfcat_x( &bsi->bsi_sel,
1409 bsi->bsi_op->o_tmpmemctx,
1412 &bsi->bsi_oc->bom_oc->soc_cname,
1416 backsql_strfcat_x( &bsi->bsi_sel,
1417 bsi->bsi_op->o_tmpmemctx,
1419 &bi->sql_dn_oc_aliasing );
1420 backsql_strfcat_x( &bsi->bsi_from,
1421 bsi->bsi_op->o_tmpmemctx,
1423 (ber_len_t)STRLENOF( " FROM ldap_entries," ),
1424 " FROM ldap_entries,",
1425 &bsi->bsi_oc->bom_keytbl );
1427 backsql_strfcat_x( &bsi->bsi_join_where,
1428 bsi->bsi_op->o_tmpmemctx,
1430 (ber_len_t)STRLENOF( " WHERE " ), " WHERE ",
1431 &bsi->bsi_oc->bom_keytbl,
1433 &bsi->bsi_oc->bom_keycol,
1434 (ber_len_t)STRLENOF( "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ),
1435 "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " );
1437 switch ( bsi->bsi_scope ) {
1438 case LDAP_SCOPE_BASE:
1439 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1440 backsql_strfcat_x( &bsi->bsi_join_where,
1441 bsi->bsi_op->o_tmpmemctx,
1443 &bi->sql_upper_func,
1444 (ber_len_t)STRLENOF( "(ldap_entries.dn)=?" ),
1445 "(ldap_entries.dn)=?" );
1447 backsql_strfcat_x( &bsi->bsi_join_where,
1448 bsi->bsi_op->o_tmpmemctx,
1450 (ber_len_t)STRLENOF( "ldap_entries.dn=?" ),
1451 "ldap_entries.dn=?" );
1455 case BACKSQL_SCOPE_BASE_LIKE:
1456 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1457 backsql_strfcat_x( &bsi->bsi_join_where,
1458 bsi->bsi_op->o_tmpmemctx,
1460 &bi->sql_upper_func,
1461 (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1462 "(ldap_entries.dn) LIKE ?" );
1464 backsql_strfcat_x( &bsi->bsi_join_where,
1465 bsi->bsi_op->o_tmpmemctx,
1467 (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1468 "ldap_entries.dn LIKE ?" );
1472 case LDAP_SCOPE_ONELEVEL:
1473 backsql_strfcat_x( &bsi->bsi_join_where,
1474 bsi->bsi_op->o_tmpmemctx,
1476 (ber_len_t)STRLENOF( "ldap_entries.parent=?" ),
1477 "ldap_entries.parent=?" );
1480 case LDAP_SCOPE_SUBORDINATE:
1481 case LDAP_SCOPE_SUBTREE:
1482 if ( BACKSQL_USE_SUBTREE_SHORTCUT( bi ) ) {
1484 BackendDB *bd = bsi->bsi_op->o_bd;
1486 assert( bd->be_nsuffix != NULL );
1488 for ( i = 0; !BER_BVISNULL( &bd->be_nsuffix[ i ] ); i++ )
1490 if ( dn_match( &bd->be_nsuffix[ i ],
1491 bsi->bsi_base_ndn ) )
1493 /* pass this to the candidate selection
1494 * routine so that the DN is not bound
1495 * to the select statement */
1496 bsi->bsi_use_subtree_shortcut = 1;
1502 if ( bsi->bsi_use_subtree_shortcut ) {
1503 /* Skip the base DN filter, as every entry will match it */
1504 backsql_strfcat_x( &bsi->bsi_join_where,
1505 bsi->bsi_op->o_tmpmemctx,
1507 (ber_len_t)STRLENOF( "9=9"), "9=9");
1509 } else if ( !BER_BVISNULL( &bi->sql_subtree_cond ) ) {
1510 backsql_strfcat_x( &bsi->bsi_join_where,
1511 bsi->bsi_op->o_tmpmemctx,
1513 &bi->sql_subtree_cond );
1515 } else if ( BACKSQL_CANUPPERCASE( bi ) ) {
1516 backsql_strfcat_x( &bsi->bsi_join_where,
1517 bsi->bsi_op->o_tmpmemctx,
1519 &bi->sql_upper_func,
1520 (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1521 "(ldap_entries.dn) LIKE ?" );
1524 backsql_strfcat_x( &bsi->bsi_join_where,
1525 bsi->bsi_op->o_tmpmemctx,
1527 (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1528 "ldap_entries.dn LIKE ?" );
1537 /* If paged results are in effect, ignore low ldap_entries.id numbers */
1538 if ( get_pagedresults(bsi->bsi_op) > SLAP_CONTROL_IGNORED ) {
1539 unsigned long lowid = 0;
1541 /* Pick up the previous ldap_entries.id if the previous page ended in this objectClass */
1542 if ( bsi->bsi_oc->bom_id == PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie ) )
1544 lowid = PAGECOOKIE_TO_SQL_ID( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie );
1547 char lowidstring[48];
1550 lowidlen = snprintf( lowidstring, 48, " AND ldap_entries.id>%d", lowid );
1551 backsql_strfcat_x( &bsi->bsi_join_where,
1552 bsi->bsi_op->o_tmpmemctx,
1554 (ber_len_t)lowidlen,
1559 rc = backsql_process_filter( bsi, bsi->bsi_filter );
1561 struct berbuf bb = BB_NULL;
1563 backsql_strfcat_x( &bb,
1564 bsi->bsi_op->o_tmpmemctx,
1566 &bsi->bsi_sel.bb_val,
1567 &bsi->bsi_from.bb_val,
1568 &bsi->bsi_join_where.bb_val,
1569 (ber_len_t)STRLENOF( " AND " ), " AND ",
1570 &bsi->bsi_flt_where.bb_val );
1574 } else if ( rc < 0 ) {
1576 * Indicates that there's no possible way the filter matches
1577 * anything. No need to issue the query
1579 free( query->bv_val );
1580 BER_BVZERO( query );
1583 bsi->bsi_op->o_tmpfree( bsi->bsi_sel.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1584 BER_BVZERO( &bsi->bsi_sel.bb_val );
1585 bsi->bsi_sel.bb_len = 0;
1586 bsi->bsi_op->o_tmpfree( bsi->bsi_from.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1587 BER_BVZERO( &bsi->bsi_from.bb_val );
1588 bsi->bsi_from.bb_len = 0;
1589 bsi->bsi_op->o_tmpfree( bsi->bsi_join_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1590 BER_BVZERO( &bsi->bsi_join_where.bb_val );
1591 bsi->bsi_join_where.bb_len = 0;
1592 bsi->bsi_op->o_tmpfree( bsi->bsi_flt_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1593 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1594 bsi->bsi_flt_where.bb_len = 0;
1596 Debug( LDAP_DEBUG_TRACE, "<==backsql_srch_query() returns %s\n",
1597 query->bv_val ? query->bv_val : "NULL", 0, 0 );
1599 return ( rc <= 0 ? 1 : 0 );
1603 backsql_oc_get_candidates( void *v_oc, void *v_bsi )
1605 backsql_oc_map_rec *oc = v_oc;
1606 backsql_srch_info *bsi = v_bsi;
1607 Operation *op = bsi->bsi_op;
1608 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1609 struct berval query;
1610 SQLHSTMT sth = SQL_NULL_HSTMT;
1613 BACKSQL_ROW_NTS row;
1616 int n_candidates = bsi->bsi_n_candidates;
1619 * + 1 because we need room for '%';
1620 * + 1 because we need room for ',' for LDAP_SCOPE_SUBORDINATE;
1621 * this makes a subtree
1622 * search for a DN BACKSQL_MAX_DN_LEN long legal
1623 * if it returns that DN only
1625 char tmp_base_ndn[ BACKSQL_MAX_DN_LEN + 1 + 1 ];
1627 bsi->bsi_status = LDAP_SUCCESS;
1629 Debug( LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc=\"%s\"\n",
1630 BACKSQL_OC_NAME( oc ), 0, 0 );
1632 /* check for abandon */
1633 if ( op->o_abandon ) {
1634 bsi->bsi_status = SLAPD_ABANDON;
1635 return BACKSQL_AVL_STOP;
1638 /* If paged results have already completed this objectClass, skip it */
1639 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
1640 if ( oc->bom_id < PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)op->o_pagedresults_state)->ps_cookie ) )
1642 return BACKSQL_AVL_CONTINUE;
1646 if ( bsi->bsi_n_candidates == -1 ) {
1647 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1648 "unchecked limit has been overcome\n", 0, 0, 0 );
1649 /* should never get here */
1651 bsi->bsi_status = LDAP_ADMINLIMIT_EXCEEDED;
1652 return BACKSQL_AVL_STOP;
1656 res = backsql_srch_query( bsi, &query );
1658 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1659 "error while constructing query for objectclass \"%s\"\n",
1660 oc->bom_oc->soc_cname.bv_val, 0, 0 );
1662 * FIXME: need to separate errors from legally
1663 * impossible filters
1665 switch ( bsi->bsi_status ) {
1667 case LDAP_UNDEFINED_TYPE:
1668 case LDAP_NO_SUCH_OBJECT:
1669 /* we are conservative... */
1671 bsi->bsi_status = LDAP_SUCCESS;
1673 return BACKSQL_AVL_CONTINUE;
1675 case LDAP_ADMINLIMIT_EXCEEDED:
1677 /* don't try any more */
1678 return BACKSQL_AVL_STOP;
1682 if ( BER_BVISNULL( &query ) ) {
1683 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1684 "could not construct query for objectclass \"%s\"\n",
1685 oc->bom_oc->soc_cname.bv_val, 0, 0 );
1686 bsi->bsi_status = LDAP_SUCCESS;
1687 return BACKSQL_AVL_CONTINUE;
1690 Debug( LDAP_DEBUG_TRACE, "Constructed query: %s\n",
1691 query.bv_val, 0, 0 );
1693 rc = backsql_Prepare( bsi->bsi_dbh, &sth, query.bv_val, 0 );
1694 bsi->bsi_op->o_tmpfree( query.bv_val, bsi->bsi_op->o_tmpmemctx );
1695 BER_BVZERO( &query );
1696 if ( rc != SQL_SUCCESS ) {
1697 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1698 "error preparing query\n", 0, 0, 0 );
1699 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1700 bsi->bsi_status = LDAP_OTHER;
1701 return BACKSQL_AVL_CONTINUE;
1704 Debug( LDAP_DEBUG_TRACE, "id: '%ld'\n", bsi->bsi_oc->bom_id, 0, 0 );
1706 rc = backsql_BindParamInt( sth, 1, SQL_PARAM_INPUT,
1707 &bsi->bsi_oc->bom_id );
1708 if ( rc != SQL_SUCCESS ) {
1709 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1710 "error binding objectclass id parameter\n", 0, 0, 0 );
1711 bsi->bsi_status = LDAP_OTHER;
1712 return BACKSQL_AVL_CONTINUE;
1715 switch ( bsi->bsi_scope ) {
1716 case LDAP_SCOPE_BASE:
1717 case BACKSQL_SCOPE_BASE_LIKE:
1719 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1720 * however this should be handled earlier
1722 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1723 bsi->bsi_status = LDAP_OTHER;
1724 return BACKSQL_AVL_CONTINUE;
1727 AC_MEMCPY( tmp_base_ndn, bsi->bsi_base_ndn->bv_val,
1728 bsi->bsi_base_ndn->bv_len + 1 );
1730 /* uppercase DN only if the stored DN can be uppercased
1732 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1733 ldap_pvt_str2upper( tmp_base_ndn );
1736 Debug( LDAP_DEBUG_TRACE, "(base)dn: \"%s\"\n",
1737 tmp_base_ndn, 0, 0 );
1739 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1740 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1741 if ( rc != SQL_SUCCESS ) {
1742 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1743 "error binding base_ndn parameter\n", 0, 0, 0 );
1744 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh,
1746 bsi->bsi_status = LDAP_OTHER;
1747 return BACKSQL_AVL_CONTINUE;
1751 case LDAP_SCOPE_SUBORDINATE:
1752 case LDAP_SCOPE_SUBTREE:
1754 /* if short-cutting the search base,
1755 * don't bind any parameter */
1756 if ( bsi->bsi_use_subtree_shortcut ) {
1761 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1762 * however this should be handled earlier
1764 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1765 bsi->bsi_status = LDAP_OTHER;
1766 return BACKSQL_AVL_CONTINUE;
1770 * Sets the parameters for the SQL built earlier
1771 * NOTE that all the databases could actually use
1772 * the TimesTen version, which would be cleaner
1773 * and would also eliminate the need for the
1774 * subtree_cond line in the configuration file.
1775 * For now, I'm leaving it the way it is,
1776 * so non-TimesTen databases use the original code.
1777 * But at some point this should get cleaned up.
1779 * If "dn" is being used, do a suffix search.
1780 * If "dn_ru" is being used, do a prefix search.
1782 if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
1783 tmp_base_ndn[ 0 ] = '\0';
1785 for ( i = 0, j = bsi->bsi_base_ndn->bv_len - 1;
1787 tmp_base_ndn[ i ] = bsi->bsi_base_ndn->bv_val[ j ];
1790 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1791 tmp_base_ndn[ i++ ] = ',';
1794 tmp_base_ndn[ i ] = '%';
1795 tmp_base_ndn[ i + 1 ] = '\0';
1800 tmp_base_ndn[ i++ ] = '%';
1802 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1803 tmp_base_ndn[ i++ ] = ',';
1806 AC_MEMCPY( &tmp_base_ndn[ i ], bsi->bsi_base_ndn->bv_val,
1807 bsi->bsi_base_ndn->bv_len + 1 );
1810 /* uppercase DN only if the stored DN can be uppercased
1812 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1813 ldap_pvt_str2upper( tmp_base_ndn );
1816 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1817 Debug( LDAP_DEBUG_TRACE, "(children)dn: \"%s\"\n",
1818 tmp_base_ndn, 0, 0 );
1820 Debug( LDAP_DEBUG_TRACE, "(sub)dn: \"%s\"\n",
1821 tmp_base_ndn, 0, 0 );
1824 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1825 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1826 if ( rc != SQL_SUCCESS ) {
1827 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1828 "error binding base_ndn parameter (2)\n",
1830 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh,
1832 bsi->bsi_status = LDAP_OTHER;
1833 return BACKSQL_AVL_CONTINUE;
1838 case LDAP_SCOPE_ONELEVEL:
1839 assert( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) );
1841 #ifdef BACKSQL_ARBITRARY_KEY
1842 Debug( LDAP_DEBUG_TRACE, "(one)id: \"%s\"\n",
1843 bsi->bsi_base_id.eid_id.bv_val, 0, 0 );
1844 #else /* ! BACKSQL_ARBITRARY_KEY */
1845 Debug( LDAP_DEBUG_TRACE, "(one)id: '%lu'\n",
1846 bsi->bsi_base_id.eid_id, 0, 0 );
1847 #endif /* ! BACKSQL_ARBITRARY_KEY */
1848 rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT,
1849 &bsi->bsi_base_id.eid_id );
1850 if ( rc != SQL_SUCCESS ) {
1851 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1852 "error binding base id parameter\n", 0, 0, 0 );
1853 bsi->bsi_status = LDAP_OTHER;
1854 return BACKSQL_AVL_CONTINUE;
1859 rc = SQLExecute( sth );
1860 if ( !BACKSQL_SUCCESS( rc ) ) {
1861 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1862 "error executing query\n", 0, 0, 0 );
1863 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1864 SQLFreeStmt( sth, SQL_DROP );
1865 bsi->bsi_status = LDAP_OTHER;
1866 return BACKSQL_AVL_CONTINUE;
1869 backsql_BindRowAsStrings_x( sth, &row, bsi->bsi_op->o_tmpmemctx );
1870 rc = SQLFetch( sth );
1871 for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
1872 struct berval dn, pdn, ndn;
1873 backsql_entryID *c_id = NULL;
1876 ber_str2bv( row.cols[ 3 ], 0, 0, &dn );
1878 if ( backsql_api_odbc2dn( bsi->bsi_op, bsi->bsi_rs, &dn ) ) {
1882 ret = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
1883 if ( dn.bv_val != row.cols[ 3 ] ) {
1887 if ( ret != LDAP_SUCCESS ) {
1891 if ( bi->sql_baseObject && dn_match( &ndn, &bi->sql_baseObject->e_nname ) ) {
1895 c_id = (backsql_entryID *)op->o_tmpcalloc( 1,
1896 sizeof( backsql_entryID ), op->o_tmpmemctx );
1897 #ifdef BACKSQL_ARBITRARY_KEY
1898 ber_str2bv_x( row.cols[ 0 ], 0, 1, &c_id->eid_id,
1900 ber_str2bv_x( row.cols[ 1 ], 0, 1, &c_id->eid_keyval,
1902 #else /* ! BACKSQL_ARBITRARY_KEY */
1903 if ( lutil_atoulx( &c_id->eid_id, row.cols[ 0 ], 0 ) != 0 ) {
1906 if ( lutil_atoulx( &c_id->eid_keyval, row.cols[ 1 ], 0 ) != 0 ) {
1909 #endif /* ! BACKSQL_ARBITRARY_KEY */
1910 c_id->eid_oc_id = bsi->bsi_oc->bom_id;
1913 c_id->eid_ndn = ndn;
1915 /* append at end of list ... */
1916 c_id->eid_next = NULL;
1917 *bsi->bsi_id_listtail = c_id;
1918 bsi->bsi_id_listtail = &c_id->eid_next;
1920 #ifdef BACKSQL_ARBITRARY_KEY
1921 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1922 "added entry id=%s, keyval=%s dn=\"%s\"\n",
1923 c_id->eid_id.bv_val, c_id->eid_keyval.bv_val,
1925 #else /* ! BACKSQL_ARBITRARY_KEY */
1926 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1927 "added entry id=%ld, keyval=%ld dn=\"%s\"\n",
1928 c_id->eid_id, c_id->eid_keyval, row.cols[ 3 ] );
1929 #endif /* ! BACKSQL_ARBITRARY_KEY */
1931 /* count candidates, for unchecked limit */
1932 bsi->bsi_n_candidates--;
1933 if ( bsi->bsi_n_candidates == -1 ) {
1939 if ( !BER_BVISNULL( &pdn ) ) {
1940 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
1942 if ( !BER_BVISNULL( &ndn ) ) {
1943 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
1945 if ( c_id != NULL ) {
1949 backsql_FreeRow_x( &row, bsi->bsi_op->o_tmpmemctx );
1950 SQLFreeStmt( sth, SQL_DROP );
1952 Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates(): %d\n",
1953 n_candidates - bsi->bsi_n_candidates, 0, 0 );
1955 return ( bsi->bsi_n_candidates == -1 ? BACKSQL_AVL_STOP : BACKSQL_AVL_CONTINUE );
1959 backsql_search( Operation *op, SlapReply *rs )
1961 backsql_info *bi = (backsql_info *)op->o_bd->be_private;
1962 SQLHDBC dbh = SQL_NULL_HDBC;
1964 Entry user_entry = { 0 },
1966 int manageDSAit = get_manageDSAit( op );
1967 time_t stoptime = 0;
1968 backsql_srch_info bsi = { 0 };
1969 backsql_entryID *eid = NULL;
1970 struct berval nbase = BER_BVNULL;
1971 unsigned long lastid = 0;
1973 Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): "
1974 "base=\"%s\", filter=\"%s\", scope=%d,",
1975 op->o_req_ndn.bv_val,
1976 op->ors_filterstr.bv_val,
1978 Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, "
1979 "attributes to load: %s\n",
1982 op->ors_attrs == NULL ? "all" : "custom list" );
1984 if ( op->o_req_ndn.bv_len > BACKSQL_MAX_DN_LEN ) {
1985 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1986 "search base length (%ld) exceeds max length (%d)\n",
1987 op->o_req_ndn.bv_len, BACKSQL_MAX_DN_LEN, 0 );
1989 * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate
1990 * since it is impossible that such a long DN exists
1993 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1994 send_ldap_result( op, rs );
1998 sres = backsql_get_db_conn( op, &dbh );
1999 if ( sres != LDAP_SUCCESS ) {
2000 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2001 "could not get connection handle - exiting\n",
2004 rs->sr_text = sres == LDAP_OTHER ? "SQL-backend error" : NULL;
2005 send_ldap_result( op, rs );
2009 /* compute it anyway; root does not use it */
2010 stoptime = op->o_time + op->ors_tlimit;
2013 bsi.bsi_e = &base_entry;
2014 rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn,
2016 stoptime, op->ors_filter,
2017 dbh, op, rs, op->ors_attrs,
2018 ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) );
2019 switch ( rs->sr_err ) {
2024 if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
2025 dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) )
2027 rs->sr_err = LDAP_SUCCESS;
2029 rs->sr_matched = NULL;
2031 ber_bvarray_free( rs->sr_ref );
2037 /* an entry was created; free it */
2038 entry_clean( bsi.bsi_e );
2043 if ( !BER_BVISNULL( &base_entry.e_nname )
2044 && !access_allowed( op, &base_entry,
2045 slap_schema.si_ad_entry, NULL,
2046 ACL_DISCLOSE, NULL ) )
2048 rs->sr_err = LDAP_NO_SUCH_OBJECT;
2050 ber_bvarray_free( rs->sr_ref );
2053 rs->sr_matched = NULL;
2057 send_ldap_result( op, rs );
2060 ber_bvarray_free( rs->sr_ref );
2064 if ( !BER_BVISNULL( &base_entry.e_nname ) ) {
2065 entry_clean( &base_entry );
2070 /* NOTE: __NEW__ "search" access is required
2071 * on searchBase object */
2075 if ( get_assert( op ) &&
2076 ( test_filter( op, &base_entry, get_assertion( op ) )
2077 != LDAP_COMPARE_TRUE ) )
2079 rs->sr_err = LDAP_ASSERTION_FAILED;
2082 if ( ! access_allowed_mask( op, &base_entry,
2083 slap_schema.si_ad_entry,
2084 NULL, ACL_SEARCH, NULL, &mask ) )
2086 if ( rs->sr_err == LDAP_SUCCESS ) {
2087 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
2091 if ( rs->sr_err != LDAP_SUCCESS ) {
2092 if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
2093 rs->sr_err = LDAP_NO_SUCH_OBJECT;
2096 send_ldap_result( op, rs );
2103 bsi.bsi_n_candidates =
2104 ( op->ors_limit == NULL /* isroot == TRUE */ ? -2 :
2105 ( op->ors_limit->lms_s_unchecked == -1 ? -2 :
2106 ( op->ors_limit->lms_s_unchecked ) ) );
2108 /* If paged results are in effect, check the paging cookie */
2109 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
2110 PagedResultsState *ps = op->o_pagedresults_state;
2111 rs->sr_err = parse_paged_cookie( op, rs );
2112 if ( rs->sr_err != LDAP_SUCCESS ) {
2113 send_ldap_result( op, rs );
2118 switch ( bsi.bsi_scope ) {
2119 case LDAP_SCOPE_BASE:
2120 case BACKSQL_SCOPE_BASE_LIKE:
2122 * probably already found...
2124 bsi.bsi_id_list = &bsi.bsi_base_id;
2125 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
2128 case LDAP_SCOPE_SUBTREE:
2130 * if baseObject is defined, and if it is the root
2131 * of the search, add it to the candidate list
2133 if ( bi->sql_baseObject && BACKSQL_IS_BASEOBJECT_ID( &bsi.bsi_base_id.eid_id ) )
2135 bsi.bsi_id_list = &bsi.bsi_base_id;
2136 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
2143 * for each objectclass we try to construct query which gets IDs
2144 * of entries matching LDAP query filter and scope (or at least
2145 * candidates), and get the IDs. Do this in ID order for paging.
2147 avl_apply( bi->sql_oc_by_id, backsql_oc_get_candidates,
2148 &bsi, BACKSQL_AVL_STOP, AVL_INORDER );
2150 /* check for abandon */
2151 if ( op->o_abandon ) {
2152 eid = bsi.bsi_id_list;
2153 rs->sr_err = SLAPD_ABANDON;
2158 if ( op->ors_limit != NULL /* isroot == FALSE */
2159 && op->ors_limit->lms_s_unchecked != -1
2160 && bsi.bsi_n_candidates == -1 )
2162 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
2163 send_ldap_result( op, rs );
2168 * now we load candidate entries (only those attributes
2169 * mentioned in attrs and filter), test it against full filter
2170 * and then send to client; don't free entry_id if baseObject...
2172 for ( eid = bsi.bsi_id_list;
2174 eid = backsql_free_entryID( op,
2175 eid, eid == &bsi.bsi_base_id ? 0 : 1 ) )
2178 Attribute *a_hasSubordinate = NULL,
2179 *a_entryUUID = NULL,
2184 /* check for abandon */
2185 if ( op->o_abandon ) {
2186 rs->sr_err = SLAPD_ABANDON;
2190 /* check time limit */
2191 if ( op->ors_tlimit != SLAP_NO_LIMIT
2192 && slap_get_time() > stoptime )
2194 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
2195 rs->sr_ctrls = NULL;
2196 rs->sr_ref = rs->sr_v2ref;
2200 #ifdef BACKSQL_ARBITRARY_KEY
2201 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
2202 "for entry id=%s, oc_id=%ld, keyval=%s\n",
2203 eid->eid_id.bv_val, eid->eid_oc_id,
2204 eid->eid_keyval.bv_val );
2205 #else /* ! BACKSQL_ARBITRARY_KEY */
2206 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
2207 "for entry id=%ld, oc_id=%ld, keyval=%ld\n",
2208 eid->eid_id, eid->eid_oc_id, eid->eid_keyval );
2209 #endif /* ! BACKSQL_ARBITRARY_KEY */
2212 switch ( op->ors_scope ) {
2213 case LDAP_SCOPE_BASE:
2214 case BACKSQL_SCOPE_BASE_LIKE:
2215 if ( !dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
2220 case LDAP_SCOPE_ONE:
2222 struct berval rdn = eid->eid_ndn;
2224 rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
2225 if ( !dnIsOneLevelRDN( &rdn ) ) {
2231 case LDAP_SCOPE_SUBORDINATE:
2232 /* discard the baseObject entry */
2233 if ( dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
2237 case LDAP_SCOPE_SUBTREE:
2238 /* FIXME: this should never fail... */
2239 if ( !dnIsSuffix( &eid->eid_ndn, &op->o_req_ndn ) ) {
2245 if ( BACKSQL_IS_BASEOBJECT_ID( &eid->eid_id ) ) {
2246 /* don't recollect baseObject... */
2247 e = bi->sql_baseObject;
2249 } else if ( eid == &bsi.bsi_base_id ) {
2250 /* don't recollect searchBase object... */
2254 bsi.bsi_e = &user_entry;
2255 rc = backsql_id2entry( &bsi, eid );
2256 if ( rc != LDAP_SUCCESS ) {
2257 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2258 "error %d in backsql_id2entry() "
2259 "- skipping\n", rc, 0, 0 );
2265 if ( !manageDSAit &&
2266 op->ors_scope != LDAP_SCOPE_BASE &&
2267 op->ors_scope != BACKSQL_SCOPE_BASE_LIKE &&
2268 is_entry_referral( e ) )
2272 refs = get_entry_referrals( op, e );
2274 backsql_srch_info bsi2 = { 0 };
2275 Entry user_entry2 = { 0 };
2277 /* retry with the full entry... */
2278 bsi2.bsi_e = &user_entry2;
2279 rc = backsql_init_search( &bsi2,
2284 BACKSQL_ISF_GET_ENTRY );
2285 if ( rc == LDAP_SUCCESS ) {
2286 if ( is_entry_referral( &user_entry2 ) )
2288 refs = get_entry_referrals( op,
2291 rs->sr_err = LDAP_OTHER;
2293 backsql_entry_clean( op, &user_entry2 );
2295 if ( bsi2.bsi_attrs != NULL ) {
2296 op->o_tmpfree( bsi2.bsi_attrs,
2302 rs->sr_ref = referral_rewrite( refs,
2306 ber_bvarray_free( refs );
2310 rs->sr_err = LDAP_REFERRAL;
2313 rs->sr_text = "bad referral object";
2317 rs->sr_matched = user_entry.e_name.bv_val;
2318 send_search_reference( op, rs );
2320 ber_bvarray_free( rs->sr_ref );
2322 rs->sr_matched = NULL;
2323 rs->sr_entry = NULL;
2329 * We use this flag since we need to parse the filter
2330 * anyway; we should have used the frontend API function
2331 * filter_has_subordinates()
2333 if ( bsi.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) {
2334 rc = backsql_has_children( op, dbh, &e->e_nname );
2337 case LDAP_COMPARE_TRUE:
2338 case LDAP_COMPARE_FALSE:
2339 a_hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE );
2340 if ( a_hasSubordinate != NULL ) {
2341 for ( ap = &user_entry.e_attrs;
2343 ap = &(*ap)->a_next );
2345 *ap = a_hasSubordinate;
2351 Debug(LDAP_DEBUG_TRACE,
2352 "backsql_search(): "
2353 "has_children failed( %d)\n",
2360 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYUUID ) {
2361 a_entryUUID = backsql_operational_entryUUID( bi, eid );
2362 if ( a_entryUUID != NULL ) {
2364 ap = &user_entry.e_attrs;
2367 for ( ; *ap; ap = &(*ap)->a_next );
2373 #ifdef BACKSQL_SYNCPROV
2374 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYCSN ) {
2375 a_entryCSN = backsql_operational_entryCSN( op );
2376 if ( a_entryCSN != NULL ) {
2378 ap = &user_entry.e_attrs;
2381 for ( ; *ap; ap = &(*ap)->a_next );
2386 #endif /* BACKSQL_SYNCPROV */
2388 if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE )
2390 /* If paged results are in effect, see if the page limit was exceeded */
2391 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
2392 if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size )
2395 send_paged_response( op, rs, &lastid );
2398 lastid = SQL_TO_PAGECOOKIE( eid->eid_id, eid->eid_oc_id );
2400 rs->sr_attrs = op->ors_attrs;
2401 rs->sr_operational_attrs = NULL;
2403 rs->sr_flags = ( e == &user_entry ) ? REP_ENTRY_MODIFIABLE : 0;
2404 /* FIXME: need the whole entry (ITS#3480) */
2405 rs->sr_err = send_search_entry( op, rs );
2406 rs->sr_entry = NULL;
2407 rs->sr_attrs = NULL;
2408 rs->sr_operational_attrs = NULL;
2410 switch ( rs->sr_err ) {
2411 case LDAP_UNAVAILABLE:
2413 * FIXME: send_search_entry failed;
2416 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2417 "connection lost\n", 0, 0, 0 );
2420 case LDAP_SIZELIMIT_EXCEEDED:
2426 if ( e == &user_entry ) {
2427 backsql_entry_clean( op, &user_entry );
2434 if ( rs->sr_nentries > 0 ) {
2435 rs->sr_ref = rs->sr_v2ref;
2436 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
2440 rs->sr_err = bsi.bsi_status;
2444 if ( rs->sr_err != SLAPD_ABANDON ) {
2445 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
2446 send_paged_response( op, rs, NULL );
2448 send_ldap_result( op, rs );
2452 /* cleanup in case of abandon */
2453 for ( ; eid != NULL;
2454 eid = backsql_free_entryID( op,
2455 eid, eid == &bsi.bsi_base_id ? 0 : 1 ) )
2458 backsql_entry_clean( op, &base_entry );
2460 /* in case we got here accidentally */
2461 backsql_entry_clean( op, &user_entry );
2463 if ( rs->sr_v2ref ) {
2464 ber_bvarray_free( rs->sr_v2ref );
2465 rs->sr_v2ref = NULL;
2468 #ifdef BACKSQL_SYNCPROV
2470 Operation op2 = *op;
2471 SlapReply rs2 = { 0 };
2472 Entry *e = entry_alloc();
2473 slap_callback cb = { 0 };
2475 op2.o_tag = LDAP_REQ_ADD;
2476 op2.o_bd = select_backend( &op->o_bd->be_nsuffix[0], 0, 0 );
2478 op2.o_callback = &cb;
2480 ber_dupbv( &e->e_name, op->o_bd->be_suffix );
2481 ber_dupbv( &e->e_nname, op->o_bd->be_nsuffix );
2483 cb.sc_response = slap_null_cb;
2485 op2.o_bd->be_add( &op2, &rs2 );
2487 if ( op2.ora_e == e )
2490 #endif /* BACKSQL_SYNCPROV */
2493 (void)backsql_free_entryID( op, &bsi.bsi_base_id, 0 );
2495 if ( bsi.bsi_attrs != NULL ) {
2496 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
2499 if ( !BER_BVISNULL( &nbase )
2500 && nbase.bv_val != op->o_req_ndn.bv_val )
2502 ch_free( nbase.bv_val );
2505 /* restore scope ... FIXME: this should be done before ANY
2506 * frontend call that uses op */
2507 if ( op->ors_scope == BACKSQL_SCOPE_BASE_LIKE ) {
2508 op->ors_scope = LDAP_SCOPE_BASE;
2511 Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n", 0, 0, 0 );
2516 /* return LDAP_SUCCESS IFF we can retrieve the specified entry.
2523 AttributeDescription *at,
2527 backsql_srch_info bsi = { 0 };
2528 SQLHDBC dbh = SQL_NULL_HDBC;
2530 SlapReply rs = { 0 };
2531 AttributeName anlist[ 2 ];
2535 rc = backsql_get_db_conn( op, &dbh );
2541 anlist[ 0 ].an_name = at->ad_cname;
2542 anlist[ 0 ].an_desc = at;
2543 BER_BVZERO( &anlist[ 1 ].an_name );
2546 bsi.bsi_e = entry_alloc();
2547 rc = backsql_init_search( &bsi,
2551 dbh, op, &rs, at ? anlist : NULL,
2552 BACKSQL_ISF_GET_ENTRY );
2554 if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
2555 (void)backsql_free_entryID( op, &bsi.bsi_base_id, 0 );
2558 if ( rc == LDAP_SUCCESS ) {
2560 #if 0 /* not supported at present */
2561 /* find attribute values */
2562 if ( is_entry_alias( bsi.bsi_e ) ) {
2563 Debug( LDAP_DEBUG_ACL,
2564 "<= backsql_entry_get: entry is an alias\n",
2566 rc = LDAP_ALIAS_PROBLEM;
2567 goto return_results;
2571 if ( is_entry_referral( bsi.bsi_e ) ) {
2572 Debug( LDAP_DEBUG_ACL,
2573 "<= backsql_entry_get: entry is a referral\n",
2576 goto return_results;
2579 if ( oc && !is_entry_objectclass( bsi.bsi_e, oc, 0 ) ) {
2580 Debug( LDAP_DEBUG_ACL,
2581 "<= backsql_entry_get: "
2582 "failed to find objectClass\n",
2584 rc = LDAP_NO_SUCH_ATTRIBUTE;
2585 goto return_results;
2592 if ( bsi.bsi_attrs != NULL ) {
2593 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
2596 if ( rc != LDAP_SUCCESS ) {
2598 entry_free( bsi.bsi_e );
2606 backsql_entry_clean(
2612 ctx = ldap_pvt_thread_pool_context();
2614 if ( ctx == NULL || ctx != op->o_tmpmemctx ) {
2615 if ( !BER_BVISNULL( &e->e_name ) ) {
2616 op->o_tmpfree( e->e_name.bv_val, op->o_tmpmemctx );
2617 BER_BVZERO( &e->e_name );
2620 if ( !BER_BVISNULL( &e->e_nname ) ) {
2621 op->o_tmpfree( e->e_nname.bv_val, op->o_tmpmemctx );
2622 BER_BVZERO( &e->e_nname );
2630 backsql_entry_release(
2635 backsql_entry_clean( op, e );
2643 /* This function is copied verbatim from back-bdb/search.c */
2645 parse_paged_cookie( Operation *op, SlapReply *rs )
2648 int rc = LDAP_SUCCESS;
2652 struct berval cookie = BER_BVNULL;
2653 PagedResultsState *ps = op->o_pagedresults_state;
2655 /* this function must be invoked only if the pagedResults
2656 * control has been detected, parsed and partially checked
2657 * by the frontend */
2658 assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
2660 /* look for the appropriate ctrl structure */
2661 for ( c = op->o_ctrls; c[0] != NULL; c++ ) {
2662 if ( strcmp( c[0]->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS ) == 0 )
2668 if ( c[0] == NULL ) {
2669 rs->sr_text = "missing pagedResults control";
2670 return LDAP_PROTOCOL_ERROR;
2673 /* Tested by frontend */
2674 assert( c[0]->ldctl_value.bv_len > 0 );
2676 /* Parse the control value
2677 * realSearchControlValue ::= SEQUENCE {
2678 * size INTEGER (0..maxInt),
2679 * -- requested page size from client
2680 * -- result set size estimate from server
2681 * cookie OCTET STRING
2684 ber = ber_init( &c[0]->ldctl_value );
2685 if ( ber == NULL ) {
2686 rs->sr_text = "internal error";
2690 tag = ber_scanf( ber, "{im}", &size, &cookie );
2692 /* Tested by frontend */
2693 assert( tag != LBER_ERROR );
2694 assert( size >= 0 );
2696 /* cookie decoding/checks deferred to backend... */
2697 if ( cookie.bv_len ) {
2698 PagedResultsCookie reqcookie;
2699 if( cookie.bv_len != sizeof( reqcookie ) ) {
2701 rs->sr_text = "paged results cookie is invalid";
2702 rc = LDAP_PROTOCOL_ERROR;
2706 AC_MEMCPY( &reqcookie, cookie.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. */
2723 if ( op->o_conn->c_pagedresults_state.ps_cookie != 0 ) {
2724 /* There's another pagedResults control on the
2725 * same connection; reject new pagedResults controls
2726 * (allowed by RFC2696) */
2727 rs->sr_text = "paged results cookie unavailable; try later";
2728 rc = LDAP_UNWILLING_TO_PERFORM;
2737 (void)ber_free( ber, 1 );
2742 /* This function is copied nearly verbatim from back-bdb/search.c */
2744 send_paged_response(
2747 unsigned long *lastid )
2749 LDAPControl ctrl, *ctrls[2];
2750 BerElementBuffer berbuf;
2751 BerElement *ber = (BerElement *)&berbuf;
2752 PagedResultsCookie respcookie;
2753 struct berval cookie;
2755 Debug(LDAP_DEBUG_ARGS,
2756 "send_paged_response: lastid=0x%08lx nentries=%d\n",
2757 lastid ? *lastid : 0, rs->sr_nentries, NULL );
2759 BER_BVZERO( &ctrl.ldctl_value );
2763 ber_init2( ber, NULL, LBER_USE_DER );
2766 respcookie = ( PagedResultsCookie )(*lastid);
2767 cookie.bv_len = sizeof( respcookie );
2768 cookie.bv_val = (char *)&respcookie;
2771 respcookie = ( PagedResultsCookie )0;
2772 BER_BVSTR( &cookie, "" );
2775 op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
2776 op->o_conn->c_pagedresults_state.ps_count =
2777 ((PagedResultsState *)op->o_pagedresults_state)->ps_count +
2780 /* return size of 0 -- no estimate */
2781 ber_printf( ber, "{iO}", 0, &cookie );
2783 if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
2787 ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
2788 ctrls[0]->ldctl_iscritical = 0;
2790 rs->sr_ctrls = ctrls;
2791 rs->sr_err = LDAP_SUCCESS;
2792 send_ldap_result( op, rs );
2793 rs->sr_ctrls = NULL;
2796 (void) ber_free_buf( ber );