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;
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, &AllUser ) == 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, &AllOper ) == 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, &NoAttrs ) == 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, &AllUser ) == 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, &AllOper ) == 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;
344 bsi->bsi_status = rc;
352 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
353 bsi->bsi_op->o_tmpmemctx );
361 backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op )
369 backsql_strfcat_x( &bsi->bsi_flt_where,
370 bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */ );
373 res = backsql_process_filter( bsi, f );
376 * TimesTen : If the query has no answers,
377 * don't bother to run the query.
388 case LDAP_FILTER_AND:
389 backsql_strfcat_x( &bsi->bsi_flt_where,
390 bsi->bsi_op->o_tmpmemctx, "l",
391 (ber_len_t)STRLENOF( " AND " ),
396 backsql_strfcat_x( &bsi->bsi_flt_where,
397 bsi->bsi_op->o_tmpmemctx, "l",
398 (ber_len_t)STRLENOF( " OR " ),
404 backsql_strfcat_x( &bsi->bsi_flt_where,
405 bsi->bsi_op->o_tmpmemctx, "c", /* ( */ ')' );
411 backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f,
412 backsql_at_map_rec *at )
414 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
422 /* always uppercase strings by now */
423 #ifdef BACKSQL_UPPERCASE_FILTER
424 if ( f->f_sub_desc->ad_type->sat_substr &&
425 SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
426 bi->sql_caseIgnoreMatch ) )
427 #endif /* BACKSQL_UPPERCASE_FILTER */
432 if ( f->f_sub_desc->ad_type->sat_substr &&
433 SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
434 bi->sql_telephoneNumberMatch ) )
441 * to check for matching telephone numbers
442 * with intermixed chars, e.g. val='1234'
445 * val LIKE '%1%2%3%4%'
449 if ( f->f_sub_initial.bv_val ) {
450 bv.bv_len += f->f_sub_initial.bv_len;
452 if ( f->f_sub_any != NULL ) {
453 for ( a = 0; f->f_sub_any[ a ].bv_val != NULL; a++ ) {
454 bv.bv_len += f->f_sub_any[ a ].bv_len;
457 if ( f->f_sub_final.bv_val ) {
458 bv.bv_len += f->f_sub_final.bv_len;
460 bv.bv_len = 2 * bv.bv_len - 1;
461 bv.bv_val = ch_malloc( bv.bv_len + 1 );
464 if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
465 bv.bv_val[ s ] = f->f_sub_initial.bv_val[ 0 ];
466 for ( i = 1; i < f->f_sub_initial.bv_len; i++ ) {
467 bv.bv_val[ s + 2 * i - 1 ] = '%';
468 bv.bv_val[ s + 2 * i ] = f->f_sub_initial.bv_val[ i ];
470 bv.bv_val[ s + 2 * i - 1 ] = '%';
474 if ( f->f_sub_any != NULL ) {
475 for ( a = 0; !BER_BVISNULL( &f->f_sub_any[ a ] ); a++ ) {
476 bv.bv_val[ s ] = f->f_sub_any[ a ].bv_val[ 0 ];
477 for ( i = 1; i < f->f_sub_any[ a ].bv_len; i++ ) {
478 bv.bv_val[ s + 2 * i - 1 ] = '%';
479 bv.bv_val[ s + 2 * i ] = f->f_sub_any[ a ].bv_val[ i ];
481 bv.bv_val[ s + 2 * i - 1 ] = '%';
486 if ( !BER_BVISNULL( &f->f_sub_final ) ) {
487 bv.bv_val[ s ] = f->f_sub_final.bv_val[ 0 ];
488 for ( i = 1; i < f->f_sub_final.bv_len; i++ ) {
489 bv.bv_val[ s + 2 * i - 1 ] = '%';
490 bv.bv_val[ s + 2 * i ] = f->f_sub_final.bv_val[ i ];
492 bv.bv_val[ s + 2 * i - 1 ] = '%';
496 bv.bv_val[ s - 1 ] = '\0';
498 (void)backsql_process_filter_like( bsi, at, casefold, &bv );
499 ch_free( bv.bv_val );
505 * When dealing with case-sensitive strings
506 * we may omit normalization; however, normalized
507 * SQL filters are more liberal.
510 backsql_strfcat_x( &bsi->bsi_flt_where,
511 bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */ );
514 Debug( LDAP_DEBUG_TRACE, "backsql_process_sub_filter(%s):\n",
515 at->bam_ad->ad_cname.bv_val, 0, 0 );
516 Debug(LDAP_DEBUG_TRACE, " expr: '%s%s%s'\n", at->bam_sel_expr.bv_val,
517 at->bam_sel_expr_u.bv_val ? "' '" : "",
518 at->bam_sel_expr_u.bv_val ? at->bam_sel_expr_u.bv_val : "" );
519 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
521 * If a pre-upper-cased version of the column
522 * or a precompiled upper function exists, use it
524 backsql_strfcat_x( &bsi->bsi_flt_where,
525 bsi->bsi_op->o_tmpmemctx,
528 (ber_len_t)STRLENOF( " LIKE '" ),
532 backsql_strfcat_x( &bsi->bsi_flt_where,
533 bsi->bsi_op->o_tmpmemctx,
536 (ber_len_t)STRLENOF( " LIKE '" ), " LIKE '" );
539 if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
543 Debug( LDAP_DEBUG_TRACE,
544 "==>backsql_process_sub_filter(%s): "
545 "sub_initial=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
546 f->f_sub_initial.bv_val, 0 );
547 #endif /* BACKSQL_TRACE */
549 start = bsi->bsi_flt_where.bb_val.bv_len;
550 backsql_strfcat_x( &bsi->bsi_flt_where,
551 bsi->bsi_op->o_tmpmemctx,
554 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
555 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
559 backsql_strfcat_x( &bsi->bsi_flt_where,
560 bsi->bsi_op->o_tmpmemctx,
563 if ( f->f_sub_any != NULL ) {
564 for ( i = 0; !BER_BVISNULL( &f->f_sub_any[ i ] ); i++ ) {
568 Debug( LDAP_DEBUG_TRACE,
569 "==>backsql_process_sub_filter(%s): "
570 "sub_any[%d]=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
571 i, f->f_sub_any[ i ].bv_val );
572 #endif /* BACKSQL_TRACE */
574 start = bsi->bsi_flt_where.bb_val.bv_len;
575 backsql_strfcat_x( &bsi->bsi_flt_where,
576 bsi->bsi_op->o_tmpmemctx,
580 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
582 * Note: toupper('%') = '%'
584 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
589 if ( !BER_BVISNULL( &f->f_sub_final ) ) {
593 Debug( LDAP_DEBUG_TRACE,
594 "==>backsql_process_sub_filter(%s): "
595 "sub_final=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
596 f->f_sub_final.bv_val, 0 );
597 #endif /* BACKSQL_TRACE */
599 start = bsi->bsi_flt_where.bb_val.bv_len;
600 backsql_strfcat_x( &bsi->bsi_flt_where,
601 bsi->bsi_op->o_tmpmemctx,
604 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
605 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
609 backsql_strfcat_x( &bsi->bsi_flt_where,
610 bsi->bsi_op->o_tmpmemctx,
612 (ber_len_t)STRLENOF( /* (' */ "')" ), /* (' */ "')" );
618 backsql_merge_from_tbls( backsql_srch_info *bsi, struct berval *from_tbls )
620 if ( BER_BVISNULL( from_tbls ) ) {
624 if ( !BER_BVISNULL( &bsi->bsi_from.bb_val ) ) {
628 ber_dupbv_x( &tmp, from_tbls, bsi->bsi_op->o_tmpmemctx );
630 for ( start = tmp.bv_val, end = strchr( start, ',' ); start; ) {
635 if ( strstr( bsi->bsi_from.bb_val.bv_val, start) == NULL )
637 backsql_strfcat_x( &bsi->bsi_from,
638 bsi->bsi_op->o_tmpmemctx,
643 /* in case there are spaces after the comma... */
644 for ( start = &end[1]; isspace( start[0] ); start++ );
646 end = strchr( start, ',' );
655 bsi->bsi_op->o_tmpfree( tmp.bv_val, bsi->bsi_op->o_tmpmemctx );
658 backsql_strfcat_x( &bsi->bsi_from,
659 bsi->bsi_op->o_tmpmemctx,
667 backsql_process_filter( backsql_srch_info *bsi, Filter *f )
669 backsql_at_map_rec **vat = NULL;
670 AttributeDescription *ad = NULL;
675 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter()\n", 0, 0, 0 );
676 if ( f->f_choice == SLAPD_FILTER_COMPUTED ) {
680 switch ( f->f_result ) {
681 case LDAP_COMPARE_TRUE:
682 BER_BVSTR( &flt, "10=10" );
686 case LDAP_COMPARE_FALSE:
687 BER_BVSTR( &flt, "11=0" );
691 case SLAPD_COMPARE_UNDEFINED:
692 BER_BVSTR( &flt, "12=0" );
701 Debug( LDAP_DEBUG_TRACE, "backsql_process_filter(): "
702 "filter computed (%s)\n", msg, 0, 0 );
703 backsql_strfcat_x( &bsi->bsi_flt_where,
704 bsi->bsi_op->o_tmpmemctx, "b", &flt );
709 switch( f->f_choice ) {
711 rc = backsql_process_filter_list( bsi, f->f_or,
716 case LDAP_FILTER_AND:
717 rc = backsql_process_filter_list( bsi, f->f_and,
722 case LDAP_FILTER_NOT:
723 backsql_strfcat_x( &bsi->bsi_flt_where,
724 bsi->bsi_op->o_tmpmemctx,
726 (ber_len_t)STRLENOF( "NOT (" /* ) */ ),
728 rc = backsql_process_filter( bsi, f->f_not );
729 backsql_strfcat_x( &bsi->bsi_flt_where,
730 bsi->bsi_op->o_tmpmemctx,
735 case LDAP_FILTER_PRESENT:
739 case LDAP_FILTER_EXT:
740 ad = f->f_mra->ma_desc;
741 if ( f->f_mr_dnattrs ) {
743 * if dn attrs filtering is requested, better return
744 * success and let test_filter() deal with candidate
745 * selection; otherwise we'd need to set conditions
746 * on the contents of the DN, e.g. "SELECT ... FROM
747 * ldap_entries AS attributeName WHERE attributeName.dn
748 * like '%attributeName=value%'"
750 backsql_strfcat_x( &bsi->bsi_flt_where,
751 bsi->bsi_op->o_tmpmemctx,
753 (ber_len_t)STRLENOF( "1=1" ), "1=1" );
754 bsi->bsi_status = LDAP_SUCCESS;
775 * Turn structuralObjectClass into objectClass
777 if ( ad == slap_schema.si_ad_objectClass
778 || ad == slap_schema.si_ad_structuralObjectClass )
781 * If the filter is LDAP_FILTER_PRESENT, then it's done;
782 * otherwise, let's see if we are lucky: filtering
783 * for "structural" objectclass or ancestor...
785 switch ( f->f_choice ) {
786 case LDAP_FILTER_EQUALITY:
788 ObjectClass *oc = oc_bvfind( &f->f_av_value );
791 Debug( LDAP_DEBUG_TRACE,
792 "backsql_process_filter(): "
793 "unknown objectClass \"%s\" "
795 f->f_av_value.bv_val, 0, 0 );
796 bsi->bsi_status = LDAP_OTHER;
802 * "structural" objectClass inheritance:
803 * - a search for "person" will also return
805 * - a search for "top" will return everything
807 if ( is_object_subclass( oc, bsi->bsi_oc->bom_oc ) ) {
808 static struct berval ldap_entry_objclasses = BER_BVC( "ldap_entry_objclasses" );
810 backsql_merge_from_tbls( bsi, &ldap_entry_objclasses );
812 backsql_strfcat_x( &bsi->bsi_flt_where,
813 bsi->bsi_op->o_tmpmemctx,
815 (ber_len_t)STRLENOF( "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */ ),
816 "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */,
817 &bsi->bsi_oc->bom_oc->soc_cname,
818 (ber_len_t)STRLENOF( /* ((' */ "'))" ),
820 bsi->bsi_status = LDAP_SUCCESS;
828 case LDAP_FILTER_PRESENT:
829 backsql_strfcat_x( &bsi->bsi_flt_where,
830 bsi->bsi_op->o_tmpmemctx,
832 (ber_len_t)STRLENOF( "3=3" ), "3=3" );
833 bsi->bsi_status = LDAP_SUCCESS;
837 /* FIXME: LDAP_FILTER_EXT? */
840 Debug( LDAP_DEBUG_TRACE,
841 "backsql_process_filter(): "
842 "illegal/unhandled filter "
843 "on objectClass attribute",
845 bsi->bsi_status = LDAP_OTHER;
850 } else if ( ad == slap_schema.si_ad_entryUUID ) {
852 #ifdef BACKSQL_ARBITRARY_KEY
853 struct berval keyval;
854 #else /* ! BACKSQL_ARBITRARY_KEY */
855 unsigned long keyval;
856 char keyvalbuf[] = "18446744073709551615";
857 #endif /* ! BACKSQL_ARBITRARY_KEY */
859 switch ( f->f_choice ) {
860 case LDAP_FILTER_EQUALITY:
861 backsql_entryUUID_decode( &f->f_av_value, &oc_id, &keyval );
863 if ( oc_id != bsi->bsi_oc->bom_id ) {
864 bsi->bsi_status = LDAP_SUCCESS;
869 #ifdef BACKSQL_ARBITRARY_KEY
870 backsql_strfcat_x( &bsi->bsi_flt_where,
871 bsi->bsi_op->o_tmpmemctx,
873 &bsi->bsi_oc->bom_keytbl, '.',
874 &bsi->bsi_oc->bom_keycol,
875 STRLENOF( " LIKE '" ), " LIKE '",
877 #else /* ! BACKSQL_ARBITRARY_KEY */
878 snprintf( keyvalbuf, sizeof( keyvalbuf ), "%lu", keyval );
879 backsql_strfcat_x( &bsi->bsi_flt_where,
880 bsi->bsi_op->o_tmpmemctx,
882 &bsi->bsi_oc->bom_keytbl, '.',
883 &bsi->bsi_oc->bom_keycol, '=', keyvalbuf );
884 #endif /* ! BACKSQL_ARBITRARY_KEY */
887 case LDAP_FILTER_PRESENT:
888 backsql_strfcat_x( &bsi->bsi_flt_where,
889 bsi->bsi_op->o_tmpmemctx,
891 (ber_len_t)STRLENOF( "4=4" ), "4=4" );
899 bsi->bsi_flags |= BSQL_SF_FILTER_ENTRYUUID;
903 #ifdef BACKSQL_SYNCPROV
904 } else if ( ad == slap_schema.si_ad_entryCSN ) {
906 * support for syncrepl as producer...
909 if ( !bsi->bsi_op->o_sync ) {
910 /* unsupported at present... */
911 bsi->bsi_status = LDAP_OTHER;
917 bsi->bsi_flags |= ( BSQL_SF_FILTER_ENTRYCSN | BSQL_SF_RETURN_ENTRYUUID);
919 /* if doing a syncrepl, try to return as much as possible,
920 * and always match the filter */
921 backsql_strfcat_x( &bsi->bsi_flt_where,
922 bsi->bsi_op->o_tmpmemctx,
924 (ber_len_t)STRLENOF( "5=5" ), "5=5" );
926 /* save for later use in operational attributes */
927 /* FIXME: saves only the first occurrence, because
928 * the filter during updates is written as
929 * "(&(entryCSN<={contextCSN})(entryCSN>={oldContextCSN})({filter}))"
930 * so we want our fake entryCSN to match the greatest
933 if ( bsi->bsi_op->o_private == NULL ) {
934 bsi->bsi_op->o_private = &f->f_av_value;
936 bsi->bsi_status = LDAP_SUCCESS;
940 #endif /* BACKSQL_SYNCPROV */
942 } else if ( ad == slap_schema.si_ad_hasSubordinates || ad == NULL ) {
944 * FIXME: this is not robust; e.g. a filter
945 * '(!(hasSubordinates=TRUE))' fails because
946 * in SQL it would read 'NOT (1=1)' instead
948 * Note however that hasSubordinates is boolean,
949 * so a more appropriate filter would be
950 * '(hasSubordinates=FALSE)'
952 * A more robust search for hasSubordinates
953 * would * require joining the ldap_entries table
954 * selecting if there are descendants of the
957 backsql_strfcat_x( &bsi->bsi_flt_where,
958 bsi->bsi_op->o_tmpmemctx,
960 (ber_len_t)STRLENOF( "6=6" ), "6=6" );
961 if ( ad == slap_schema.si_ad_hasSubordinates ) {
963 * instruct candidate selection algorithm
964 * and attribute list to try to detect
965 * if an entry has subordinates
967 bsi->bsi_flags |= BSQL_SF_FILTER_HASSUBORDINATE;
971 * clear attributes to fetch, to require ALL
972 * and try extended match on all attributes
974 backsql_attrlist_add( bsi, NULL );
981 * attribute inheritance:
983 if ( backsql_supad2at( bsi->bsi_oc, ad, &vat ) ) {
984 bsi->bsi_status = LDAP_OTHER;
990 /* search anyway; other parts of the filter
992 backsql_strfcat_x( &bsi->bsi_flt_where,
993 bsi->bsi_op->o_tmpmemctx,
995 (ber_len_t)STRLENOF( "7=7" ), "7=7" );
996 bsi->bsi_status = LDAP_SUCCESS;
1001 /* if required, open extra level of parens */
1003 if ( vat[0]->bam_next || vat[1] ) {
1004 backsql_strfcat_x( &bsi->bsi_flt_where,
1005 bsi->bsi_op->o_tmpmemctx,
1013 if ( backsql_process_filter_attr( bsi, f, vat[i] ) == -1 ) {
1017 /* if more definitions of the same attr, apply */
1018 if ( vat[i]->bam_next ) {
1019 backsql_strfcat_x( &bsi->bsi_flt_where,
1020 bsi->bsi_op->o_tmpmemctx,
1022 STRLENOF( " OR " ), " OR " );
1023 vat[i] = vat[i]->bam_next;
1027 /* if more descendants of the same attr, apply */
1030 backsql_strfcat_x( &bsi->bsi_flt_where,
1031 bsi->bsi_op->o_tmpmemctx,
1033 STRLENOF( " OR " ), " OR " );
1037 /* if needed, close extra level of parens */
1039 backsql_strfcat_x( &bsi->bsi_flt_where,
1040 bsi->bsi_op->o_tmpmemctx,
1051 Debug( LDAP_DEBUG_TRACE,
1052 "<==backsql_process_filter() %s\n",
1053 rc == 1 ? "succeeded" : "failed", 0, 0);
1059 backsql_process_filter_eq( backsql_srch_info *bsi, backsql_at_map_rec *at,
1060 int casefold, struct berval *filter_value )
1063 * maybe we should check type of at->sel_expr here somehow,
1064 * to know whether upper_func is applicable, but for now
1065 * upper_func stuff is made for Oracle, where UPPER is
1066 * safely applicable to NUMBER etc.
1068 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1071 backsql_strfcat_x( &bsi->bsi_flt_where,
1072 bsi->bsi_op->o_tmpmemctx,
1075 &at->bam_sel_expr_u,
1076 (ber_len_t)STRLENOF( "='" ),
1079 start = bsi->bsi_flt_where.bb_val.bv_len;
1081 backsql_strfcat_x( &bsi->bsi_flt_where,
1082 bsi->bsi_op->o_tmpmemctx,
1085 (ber_len_t)STRLENOF( /* (' */ "')" ),
1088 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1091 backsql_strfcat_x( &bsi->bsi_flt_where,
1092 bsi->bsi_op->o_tmpmemctx,
1096 (ber_len_t)STRLENOF( "='" ), "='",
1098 (ber_len_t)STRLENOF( /* (' */ "')" ),
1106 backsql_process_filter_like( backsql_srch_info *bsi, backsql_at_map_rec *at,
1107 int casefold, struct berval *filter_value )
1110 * maybe we should check type of at->sel_expr here somehow,
1111 * to know whether upper_func is applicable, but for now
1112 * upper_func stuff is made for Oracle, where UPPER is
1113 * safely applicable to NUMBER etc.
1115 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1118 backsql_strfcat_x( &bsi->bsi_flt_where,
1119 bsi->bsi_op->o_tmpmemctx,
1122 &at->bam_sel_expr_u,
1123 (ber_len_t)STRLENOF( " LIKE '%" ),
1126 start = bsi->bsi_flt_where.bb_val.bv_len;
1128 backsql_strfcat_x( &bsi->bsi_flt_where,
1129 bsi->bsi_op->o_tmpmemctx,
1132 (ber_len_t)STRLENOF( /* (' */ "%')" ),
1135 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1138 backsql_strfcat_x( &bsi->bsi_flt_where,
1139 bsi->bsi_op->o_tmpmemctx,
1143 (ber_len_t)STRLENOF( " LIKE '%" ),
1146 (ber_len_t)STRLENOF( /* (' */ "%')" ),
1154 backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f, backsql_at_map_rec *at )
1156 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1158 struct berval *filter_value = NULL;
1159 MatchingRule *matching_rule = NULL;
1160 struct berval ordering = BER_BVC("<=");
1162 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter_attr(%s)\n",
1163 at->bam_ad->ad_cname.bv_val, 0, 0 );
1166 * need to add this attribute to list of attrs to load,
1167 * so that we can do test_filter() later
1169 backsql_attrlist_add( bsi, at->bam_ad );
1171 backsql_merge_from_tbls( bsi, &at->bam_from_tbls );
1173 if ( !BER_BVISNULL( &at->bam_join_where )
1174 && strstr( bsi->bsi_join_where.bb_val.bv_val,
1175 at->bam_join_where.bv_val ) == NULL )
1177 backsql_strfcat_x( &bsi->bsi_join_where,
1178 bsi->bsi_op->o_tmpmemctx,
1180 (ber_len_t)STRLENOF( " AND " ), " AND ",
1181 &at->bam_join_where );
1184 switch ( f->f_choice ) {
1185 case LDAP_FILTER_EQUALITY:
1186 filter_value = &f->f_av_value;
1187 matching_rule = at->bam_ad->ad_type->sat_equality;
1189 goto equality_match;
1191 /* fail over into next case */
1193 case LDAP_FILTER_EXT:
1194 filter_value = &f->f_mra->ma_value;
1195 matching_rule = f->f_mr_rule;
1198 /* always uppercase strings by now */
1199 #ifdef BACKSQL_UPPERCASE_FILTER
1200 if ( SLAP_MR_ASSOCIATED( matching_rule,
1201 bi->sql_caseIgnoreMatch ) )
1202 #endif /* BACKSQL_UPPERCASE_FILTER */
1207 /* FIXME: directoryString filtering should use a similar
1208 * approach to deal with non-prettified values like
1209 * " A non prettified value ", by using a LIKE
1210 * filter with all whitespaces collapsed to a single '%' */
1211 if ( SLAP_MR_ASSOCIATED( matching_rule,
1212 bi->sql_telephoneNumberMatch ) )
1218 * to check for matching telephone numbers
1219 * with intermized chars, e.g. val='1234'
1222 * val LIKE '%1%2%3%4%'
1225 bv.bv_len = 2 * filter_value->bv_len - 1;
1226 bv.bv_val = ch_malloc( bv.bv_len + 1 );
1228 bv.bv_val[ 0 ] = filter_value->bv_val[ 0 ];
1229 for ( i = 1; i < filter_value->bv_len; i++ ) {
1230 bv.bv_val[ 2 * i - 1 ] = '%';
1231 bv.bv_val[ 2 * i ] = filter_value->bv_val[ i ];
1233 bv.bv_val[ 2 * i - 1 ] = '\0';
1235 (void)backsql_process_filter_like( bsi, at, casefold, &bv );
1236 ch_free( bv.bv_val );
1241 /* NOTE: this is required by objectClass inheritance
1242 * and auxiliary objectClass use in filters for slightly
1243 * more efficient candidate selection. */
1244 /* FIXME: a bit too many specializations to deal with
1245 * very specific cases... */
1246 if ( at->bam_ad == slap_schema.si_ad_objectClass
1247 || at->bam_ad == slap_schema.si_ad_structuralObjectClass )
1249 backsql_strfcat_x( &bsi->bsi_flt_where,
1250 bsi->bsi_op->o_tmpmemctx,
1252 (ber_len_t)STRLENOF( "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */ ),
1253 "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */,
1255 (ber_len_t)STRLENOF( /* (' */ "')" ),
1261 * maybe we should check type of at->sel_expr here somehow,
1262 * to know whether upper_func is applicable, but for now
1263 * upper_func stuff is made for Oracle, where UPPER is
1264 * safely applicable to NUMBER etc.
1266 (void)backsql_process_filter_eq( bsi, at, casefold, filter_value );
1269 case LDAP_FILTER_GE:
1270 ordering.bv_val = ">=";
1272 /* fall thru to next case */
1274 case LDAP_FILTER_LE:
1275 filter_value = &f->f_av_value;
1277 /* always uppercase strings by now */
1278 #ifdef BACKSQL_UPPERCASE_FILTER
1279 if ( at->bam_ad->ad_type->sat_ordering &&
1280 SLAP_MR_ASSOCIATED( at->bam_ad->ad_type->sat_ordering,
1281 bi->sql_caseIgnoreMatch ) )
1282 #endif /* BACKSQL_UPPERCASE_FILTER */
1288 * FIXME: should we uppercase the operands?
1290 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1293 backsql_strfcat_x( &bsi->bsi_flt_where,
1294 bsi->bsi_op->o_tmpmemctx,
1297 &at->bam_sel_expr_u,
1301 start = bsi->bsi_flt_where.bb_val.bv_len;
1303 backsql_strfcat_x( &bsi->bsi_flt_where,
1304 bsi->bsi_op->o_tmpmemctx,
1307 (ber_len_t)STRLENOF( /* (' */ "')" ),
1310 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1313 backsql_strfcat_x( &bsi->bsi_flt_where,
1314 bsi->bsi_op->o_tmpmemctx,
1321 (ber_len_t)STRLENOF( /* (' */ "')" ),
1326 case LDAP_FILTER_PRESENT:
1327 backsql_strfcat_x( &bsi->bsi_flt_where,
1328 bsi->bsi_op->o_tmpmemctx,
1330 (ber_len_t)STRLENOF( "NOT (" /* ) */),
1333 (ber_len_t)STRLENOF( /* ( */ " IS NULL)" ),
1334 /* ( */ " IS NULL)" );
1337 case LDAP_FILTER_SUBSTRINGS:
1338 backsql_process_sub_filter( bsi, f, at );
1341 case LDAP_FILTER_APPROX:
1342 /* we do our best */
1345 * maybe we should check type of at->sel_expr here somehow,
1346 * to know whether upper_func is applicable, but for now
1347 * upper_func stuff is made for Oracle, where UPPER is
1348 * safely applicable to NUMBER etc.
1350 (void)backsql_process_filter_like( bsi, at, 1, &f->f_av_value );
1354 /* unhandled filter type; should not happen */
1356 backsql_strfcat_x( &bsi->bsi_flt_where,
1357 bsi->bsi_op->o_tmpmemctx,
1359 (ber_len_t)STRLENOF( "8=8" ), "8=8" );
1364 Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter_attr(%s)\n",
1365 at->bam_ad->ad_cname.bv_val, 0, 0 );
1371 backsql_srch_query( backsql_srch_info *bsi, struct berval *query )
1373 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1376 assert( query != NULL );
1377 BER_BVZERO( query );
1379 bsi->bsi_use_subtree_shortcut = 0;
1381 Debug( LDAP_DEBUG_TRACE, "==>backsql_srch_query()\n", 0, 0, 0 );
1382 BER_BVZERO( &bsi->bsi_sel.bb_val );
1383 BER_BVZERO( &bsi->bsi_sel.bb_val );
1384 bsi->bsi_sel.bb_len = 0;
1385 BER_BVZERO( &bsi->bsi_from.bb_val );
1386 bsi->bsi_from.bb_len = 0;
1387 BER_BVZERO( &bsi->bsi_join_where.bb_val );
1388 bsi->bsi_join_where.bb_len = 0;
1389 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1390 bsi->bsi_flt_where.bb_len = 0;
1392 backsql_strfcat_x( &bsi->bsi_sel,
1393 bsi->bsi_op->o_tmpmemctx,
1395 (ber_len_t)STRLENOF( "SELECT DISTINCT ldap_entries.id," ),
1396 "SELECT DISTINCT ldap_entries.id,",
1397 &bsi->bsi_oc->bom_keytbl,
1399 &bsi->bsi_oc->bom_keycol,
1402 if ( !BER_BVISNULL( &bi->sql_strcast_func ) ) {
1403 backsql_strfcat_x( &bsi->bsi_sel,
1404 bsi->bsi_op->o_tmpmemctx,
1406 &bi->sql_strcast_func,
1407 (ber_len_t)STRLENOF( "('" /* ') */ ),
1409 &bsi->bsi_oc->bom_oc->soc_cname,
1410 (ber_len_t)STRLENOF( /* (' */ "')" ),
1413 backsql_strfcat_x( &bsi->bsi_sel,
1414 bsi->bsi_op->o_tmpmemctx,
1417 &bsi->bsi_oc->bom_oc->soc_cname,
1421 backsql_strfcat_x( &bsi->bsi_sel,
1422 bsi->bsi_op->o_tmpmemctx,
1424 &bi->sql_dn_oc_aliasing );
1425 backsql_strfcat_x( &bsi->bsi_from,
1426 bsi->bsi_op->o_tmpmemctx,
1428 (ber_len_t)STRLENOF( " FROM ldap_entries," ),
1429 " FROM ldap_entries,",
1430 &bsi->bsi_oc->bom_keytbl );
1432 backsql_strfcat_x( &bsi->bsi_join_where,
1433 bsi->bsi_op->o_tmpmemctx,
1435 (ber_len_t)STRLENOF( " WHERE " ), " WHERE ",
1436 &bsi->bsi_oc->bom_keytbl,
1438 &bsi->bsi_oc->bom_keycol,
1439 (ber_len_t)STRLENOF( "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ),
1440 "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " );
1442 switch ( bsi->bsi_scope ) {
1443 case LDAP_SCOPE_BASE:
1444 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1445 backsql_strfcat_x( &bsi->bsi_join_where,
1446 bsi->bsi_op->o_tmpmemctx,
1448 &bi->sql_upper_func,
1449 (ber_len_t)STRLENOF( "(ldap_entries.dn)=?" ),
1450 "(ldap_entries.dn)=?" );
1452 backsql_strfcat_x( &bsi->bsi_join_where,
1453 bsi->bsi_op->o_tmpmemctx,
1455 (ber_len_t)STRLENOF( "ldap_entries.dn=?" ),
1456 "ldap_entries.dn=?" );
1460 case BACKSQL_SCOPE_BASE_LIKE:
1461 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1462 backsql_strfcat_x( &bsi->bsi_join_where,
1463 bsi->bsi_op->o_tmpmemctx,
1465 &bi->sql_upper_func,
1466 (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1467 "(ldap_entries.dn) LIKE ?" );
1469 backsql_strfcat_x( &bsi->bsi_join_where,
1470 bsi->bsi_op->o_tmpmemctx,
1472 (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1473 "ldap_entries.dn LIKE ?" );
1477 case LDAP_SCOPE_ONELEVEL:
1478 backsql_strfcat_x( &bsi->bsi_join_where,
1479 bsi->bsi_op->o_tmpmemctx,
1481 (ber_len_t)STRLENOF( "ldap_entries.parent=?" ),
1482 "ldap_entries.parent=?" );
1485 case LDAP_SCOPE_SUBORDINATE:
1486 case LDAP_SCOPE_SUBTREE:
1487 if ( BACKSQL_USE_SUBTREE_SHORTCUT( bi ) ) {
1489 BackendDB *bd = bsi->bsi_op->o_bd;
1491 assert( bd->be_nsuffix != NULL );
1493 for ( i = 0; !BER_BVISNULL( &bd->be_nsuffix[ i ] ); i++ )
1495 if ( dn_match( &bd->be_nsuffix[ i ],
1496 bsi->bsi_base_ndn ) )
1498 /* pass this to the candidate selection
1499 * routine so that the DN is not bound
1500 * to the select statement */
1501 bsi->bsi_use_subtree_shortcut = 1;
1507 if ( bsi->bsi_use_subtree_shortcut ) {
1508 /* Skip the base DN filter, as every entry will match it */
1509 backsql_strfcat_x( &bsi->bsi_join_where,
1510 bsi->bsi_op->o_tmpmemctx,
1512 (ber_len_t)STRLENOF( "9=9"), "9=9");
1514 } else if ( !BER_BVISNULL( &bi->sql_subtree_cond ) ) {
1515 backsql_strfcat_x( &bsi->bsi_join_where,
1516 bsi->bsi_op->o_tmpmemctx,
1518 &bi->sql_subtree_cond );
1520 } else if ( BACKSQL_CANUPPERCASE( bi ) ) {
1521 backsql_strfcat_x( &bsi->bsi_join_where,
1522 bsi->bsi_op->o_tmpmemctx,
1524 &bi->sql_upper_func,
1525 (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1526 "(ldap_entries.dn) LIKE ?" );
1529 backsql_strfcat_x( &bsi->bsi_join_where,
1530 bsi->bsi_op->o_tmpmemctx,
1532 (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1533 "ldap_entries.dn LIKE ?" );
1542 /* If paged results are in effect, ignore low ldap_entries.id numbers */
1543 if ( get_pagedresults(bsi->bsi_op) > SLAP_CONTROL_IGNORED ) {
1544 unsigned long lowid = 0;
1546 /* Pick up the previous ldap_entries.id if the previous page ended in this objectClass */
1547 if ( bsi->bsi_oc->bom_id == PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie ) )
1549 lowid = PAGECOOKIE_TO_SQL_ID( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie );
1552 char lowidstring[48];
1555 lowidlen = snprintf( lowidstring, 48, " AND ldap_entries.id>%d", lowid );
1556 backsql_strfcat_x( &bsi->bsi_join_where,
1557 bsi->bsi_op->o_tmpmemctx,
1559 (ber_len_t)lowidlen,
1564 rc = backsql_process_filter( bsi, bsi->bsi_filter );
1566 struct berbuf bb = BB_NULL;
1568 backsql_strfcat_x( &bb,
1569 bsi->bsi_op->o_tmpmemctx,
1571 &bsi->bsi_sel.bb_val,
1572 &bsi->bsi_from.bb_val,
1573 &bsi->bsi_join_where.bb_val,
1574 (ber_len_t)STRLENOF( " AND " ), " AND ",
1575 &bsi->bsi_flt_where.bb_val );
1579 } else if ( rc < 0 ) {
1581 * Indicates that there's no possible way the filter matches
1582 * anything. No need to issue the query
1584 free( query->bv_val );
1585 BER_BVZERO( query );
1588 bsi->bsi_op->o_tmpfree( bsi->bsi_sel.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1589 BER_BVZERO( &bsi->bsi_sel.bb_val );
1590 bsi->bsi_sel.bb_len = 0;
1591 bsi->bsi_op->o_tmpfree( bsi->bsi_from.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1592 BER_BVZERO( &bsi->bsi_from.bb_val );
1593 bsi->bsi_from.bb_len = 0;
1594 bsi->bsi_op->o_tmpfree( bsi->bsi_join_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1595 BER_BVZERO( &bsi->bsi_join_where.bb_val );
1596 bsi->bsi_join_where.bb_len = 0;
1597 bsi->bsi_op->o_tmpfree( bsi->bsi_flt_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1598 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1599 bsi->bsi_flt_where.bb_len = 0;
1601 Debug( LDAP_DEBUG_TRACE, "<==backsql_srch_query() returns %s\n",
1602 query->bv_val ? query->bv_val : "NULL", 0, 0 );
1604 return ( rc <= 0 ? 1 : 0 );
1608 backsql_oc_get_candidates( void *v_oc, void *v_bsi )
1610 backsql_oc_map_rec *oc = v_oc;
1611 backsql_srch_info *bsi = v_bsi;
1612 Operation *op = bsi->bsi_op;
1613 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1614 struct berval query;
1615 SQLHSTMT sth = SQL_NULL_HSTMT;
1618 BACKSQL_ROW_NTS row;
1621 int n_candidates = bsi->bsi_n_candidates;
1624 * + 1 because we need room for '%';
1625 * + 1 because we need room for ',' for LDAP_SCOPE_SUBORDINATE;
1626 * this makes a subtree
1627 * search for a DN BACKSQL_MAX_DN_LEN long legal
1628 * if it returns that DN only
1630 char tmp_base_ndn[ BACKSQL_MAX_DN_LEN + 1 + 1 ];
1632 bsi->bsi_status = LDAP_SUCCESS;
1634 Debug( LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc=\"%s\"\n",
1635 BACKSQL_OC_NAME( oc ), 0, 0 );
1637 /* check for abandon */
1638 if ( op->o_abandon ) {
1639 bsi->bsi_status = SLAPD_ABANDON;
1640 return BACKSQL_AVL_STOP;
1643 /* If paged results have already completed this objectClass, skip it */
1644 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
1645 if ( oc->bom_id < PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)op->o_pagedresults_state)->ps_cookie ) )
1647 return BACKSQL_AVL_CONTINUE;
1651 if ( bsi->bsi_n_candidates == -1 ) {
1652 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1653 "unchecked limit has been overcome\n", 0, 0, 0 );
1654 /* should never get here */
1656 bsi->bsi_status = LDAP_ADMINLIMIT_EXCEEDED;
1657 return BACKSQL_AVL_STOP;
1661 res = backsql_srch_query( bsi, &query );
1663 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1664 "error while constructing query for objectclass \"%s\"\n",
1665 oc->bom_oc->soc_cname.bv_val, 0, 0 );
1667 * FIXME: need to separate errors from legally
1668 * impossible filters
1670 switch ( bsi->bsi_status ) {
1672 case LDAP_UNDEFINED_TYPE:
1673 case LDAP_NO_SUCH_OBJECT:
1674 /* we are conservative... */
1676 bsi->bsi_status = LDAP_SUCCESS;
1678 return BACKSQL_AVL_CONTINUE;
1680 case LDAP_ADMINLIMIT_EXCEEDED:
1682 /* don't try any more */
1683 return BACKSQL_AVL_STOP;
1687 if ( BER_BVISNULL( &query ) ) {
1688 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1689 "could not construct query for objectclass \"%s\"\n",
1690 oc->bom_oc->soc_cname.bv_val, 0, 0 );
1691 bsi->bsi_status = LDAP_SUCCESS;
1692 return BACKSQL_AVL_CONTINUE;
1695 Debug( LDAP_DEBUG_TRACE, "Constructed query: %s\n",
1696 query.bv_val, 0, 0 );
1698 rc = backsql_Prepare( bsi->bsi_dbh, &sth, query.bv_val, 0 );
1699 bsi->bsi_op->o_tmpfree( query.bv_val, bsi->bsi_op->o_tmpmemctx );
1700 BER_BVZERO( &query );
1701 if ( rc != SQL_SUCCESS ) {
1702 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1703 "error preparing query\n", 0, 0, 0 );
1704 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1705 bsi->bsi_status = LDAP_OTHER;
1706 return BACKSQL_AVL_CONTINUE;
1709 Debug( LDAP_DEBUG_TRACE, "id: '%ld'\n", bsi->bsi_oc->bom_id, 0, 0 );
1711 rc = backsql_BindParamInt( sth, 1, SQL_PARAM_INPUT,
1712 &bsi->bsi_oc->bom_id );
1713 if ( rc != SQL_SUCCESS ) {
1714 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1715 "error binding objectclass id parameter\n", 0, 0, 0 );
1716 bsi->bsi_status = LDAP_OTHER;
1717 return BACKSQL_AVL_CONTINUE;
1720 switch ( bsi->bsi_scope ) {
1721 case LDAP_SCOPE_BASE:
1722 case BACKSQL_SCOPE_BASE_LIKE:
1724 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1725 * however this should be handled earlier
1727 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1728 bsi->bsi_status = LDAP_OTHER;
1729 return BACKSQL_AVL_CONTINUE;
1732 AC_MEMCPY( tmp_base_ndn, bsi->bsi_base_ndn->bv_val,
1733 bsi->bsi_base_ndn->bv_len + 1 );
1735 /* uppercase DN only if the stored DN can be uppercased
1737 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1738 ldap_pvt_str2upper( tmp_base_ndn );
1741 Debug( LDAP_DEBUG_TRACE, "(base)dn: \"%s\"\n",
1742 tmp_base_ndn, 0, 0 );
1744 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1745 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1746 if ( rc != SQL_SUCCESS ) {
1747 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1748 "error binding base_ndn parameter\n", 0, 0, 0 );
1749 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh,
1751 bsi->bsi_status = LDAP_OTHER;
1752 return BACKSQL_AVL_CONTINUE;
1756 case LDAP_SCOPE_SUBORDINATE:
1757 case LDAP_SCOPE_SUBTREE:
1759 /* if short-cutting the search base,
1760 * don't bind any parameter */
1761 if ( bsi->bsi_use_subtree_shortcut ) {
1766 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1767 * however this should be handled earlier
1769 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1770 bsi->bsi_status = LDAP_OTHER;
1771 return BACKSQL_AVL_CONTINUE;
1775 * Sets the parameters for the SQL built earlier
1776 * NOTE that all the databases could actually use
1777 * the TimesTen version, which would be cleaner
1778 * and would also eliminate the need for the
1779 * subtree_cond line in the configuration file.
1780 * For now, I'm leaving it the way it is,
1781 * so non-TimesTen databases use the original code.
1782 * But at some point this should get cleaned up.
1784 * If "dn" is being used, do a suffix search.
1785 * If "dn_ru" is being used, do a prefix search.
1787 if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
1788 tmp_base_ndn[ 0 ] = '\0';
1790 for ( i = 0, j = bsi->bsi_base_ndn->bv_len - 1;
1792 tmp_base_ndn[ i ] = bsi->bsi_base_ndn->bv_val[ j ];
1795 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1796 tmp_base_ndn[ i++ ] = ',';
1799 tmp_base_ndn[ i ] = '%';
1800 tmp_base_ndn[ i + 1 ] = '\0';
1805 tmp_base_ndn[ i++ ] = '%';
1807 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1808 tmp_base_ndn[ i++ ] = ',';
1811 AC_MEMCPY( &tmp_base_ndn[ i ], bsi->bsi_base_ndn->bv_val,
1812 bsi->bsi_base_ndn->bv_len + 1 );
1815 /* uppercase DN only if the stored DN can be uppercased
1817 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1818 ldap_pvt_str2upper( tmp_base_ndn );
1821 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1822 Debug( LDAP_DEBUG_TRACE, "(children)dn: \"%s\"\n",
1823 tmp_base_ndn, 0, 0 );
1825 Debug( LDAP_DEBUG_TRACE, "(sub)dn: \"%s\"\n",
1826 tmp_base_ndn, 0, 0 );
1829 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1830 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1831 if ( rc != SQL_SUCCESS ) {
1832 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1833 "error binding base_ndn parameter (2)\n",
1835 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh,
1837 bsi->bsi_status = LDAP_OTHER;
1838 return BACKSQL_AVL_CONTINUE;
1843 case LDAP_SCOPE_ONELEVEL:
1844 assert( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) );
1846 #ifdef BACKSQL_ARBITRARY_KEY
1847 Debug( LDAP_DEBUG_TRACE, "(one)id: \"%s\"\n",
1848 bsi->bsi_base_id.eid_id.bv_val, 0, 0 );
1849 #else /* ! BACKSQL_ARBITRARY_KEY */
1850 Debug( LDAP_DEBUG_TRACE, "(one)id: '%lu'\n",
1851 bsi->bsi_base_id.eid_id, 0, 0 );
1852 #endif /* ! BACKSQL_ARBITRARY_KEY */
1853 rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT,
1854 &bsi->bsi_base_id.eid_id );
1855 if ( rc != SQL_SUCCESS ) {
1856 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1857 "error binding base id parameter\n", 0, 0, 0 );
1858 bsi->bsi_status = LDAP_OTHER;
1859 return BACKSQL_AVL_CONTINUE;
1864 rc = SQLExecute( sth );
1865 if ( !BACKSQL_SUCCESS( rc ) ) {
1866 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1867 "error executing query\n", 0, 0, 0 );
1868 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1869 SQLFreeStmt( sth, SQL_DROP );
1870 bsi->bsi_status = LDAP_OTHER;
1871 return BACKSQL_AVL_CONTINUE;
1874 backsql_BindRowAsStrings_x( sth, &row, bsi->bsi_op->o_tmpmemctx );
1875 rc = SQLFetch( sth );
1876 for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
1877 struct berval dn, pdn, ndn;
1878 backsql_entryID *c_id = NULL;
1881 ber_str2bv( row.cols[ 3 ], 0, 0, &dn );
1883 if ( backsql_api_odbc2dn( bsi->bsi_op, bsi->bsi_rs, &dn ) ) {
1887 ret = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
1888 if ( dn.bv_val != row.cols[ 3 ] ) {
1892 if ( ret != LDAP_SUCCESS ) {
1896 if ( bi->sql_baseObject && dn_match( &ndn, &bi->sql_baseObject->e_nname ) ) {
1900 c_id = (backsql_entryID *)op->o_tmpcalloc( 1,
1901 sizeof( backsql_entryID ), op->o_tmpmemctx );
1902 #ifdef BACKSQL_ARBITRARY_KEY
1903 ber_str2bv_x( row.cols[ 0 ], 0, 1, &c_id->eid_id,
1905 ber_str2bv_x( row.cols[ 1 ], 0, 1, &c_id->eid_keyval,
1907 #else /* ! BACKSQL_ARBITRARY_KEY */
1908 if ( lutil_atoulx( &c_id->eid_id, row.cols[ 0 ], 0 ) != 0 ) {
1911 if ( lutil_atoulx( &c_id->eid_keyval, row.cols[ 1 ], 0 ) != 0 ) {
1914 #endif /* ! BACKSQL_ARBITRARY_KEY */
1915 c_id->eid_oc_id = bsi->bsi_oc->bom_id;
1918 c_id->eid_ndn = ndn;
1920 /* append at end of list ... */
1921 c_id->eid_next = NULL;
1922 *bsi->bsi_id_listtail = c_id;
1923 bsi->bsi_id_listtail = &c_id->eid_next;
1925 #ifdef BACKSQL_ARBITRARY_KEY
1926 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1927 "added entry id=%s, keyval=%s dn=\"%s\"\n",
1928 c_id->eid_id.bv_val, c_id->eid_keyval.bv_val,
1930 #else /* ! BACKSQL_ARBITRARY_KEY */
1931 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1932 "added entry id=%ld, keyval=%ld dn=\"%s\"\n",
1933 c_id->eid_id, c_id->eid_keyval, row.cols[ 3 ] );
1934 #endif /* ! BACKSQL_ARBITRARY_KEY */
1936 /* count candidates, for unchecked limit */
1937 bsi->bsi_n_candidates--;
1938 if ( bsi->bsi_n_candidates == -1 ) {
1944 if ( !BER_BVISNULL( &pdn ) ) {
1945 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
1947 if ( !BER_BVISNULL( &ndn ) ) {
1948 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
1950 if ( c_id != NULL ) {
1954 backsql_FreeRow_x( &row, bsi->bsi_op->o_tmpmemctx );
1955 SQLFreeStmt( sth, SQL_DROP );
1957 Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates(): %d\n",
1958 n_candidates - bsi->bsi_n_candidates, 0, 0 );
1960 return ( bsi->bsi_n_candidates == -1 ? BACKSQL_AVL_STOP : BACKSQL_AVL_CONTINUE );
1964 backsql_search( Operation *op, SlapReply *rs )
1966 backsql_info *bi = (backsql_info *)op->o_bd->be_private;
1967 SQLHDBC dbh = SQL_NULL_HDBC;
1969 Entry user_entry = { 0 },
1971 int manageDSAit = get_manageDSAit( op );
1972 time_t stoptime = 0;
1973 backsql_srch_info bsi = { 0 };
1974 backsql_entryID *eid = NULL;
1975 struct berval nbase = BER_BVNULL;
1976 unsigned long lastid = 0;
1978 Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): "
1979 "base=\"%s\", filter=\"%s\", scope=%d,",
1980 op->o_req_ndn.bv_val,
1981 op->ors_filterstr.bv_val,
1983 Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, "
1984 "attributes to load: %s\n",
1987 op->ors_attrs == NULL ? "all" : "custom list" );
1989 if ( op->o_req_ndn.bv_len > BACKSQL_MAX_DN_LEN ) {
1990 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1991 "search base length (%ld) exceeds max length (%d)\n",
1992 op->o_req_ndn.bv_len, BACKSQL_MAX_DN_LEN, 0 );
1994 * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate
1995 * since it is impossible that such a long DN exists
1998 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1999 send_ldap_result( op, rs );
2003 sres = backsql_get_db_conn( op, &dbh );
2004 if ( sres != LDAP_SUCCESS ) {
2005 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2006 "could not get connection handle - exiting\n",
2009 rs->sr_text = sres == LDAP_OTHER ? "SQL-backend error" : NULL;
2010 send_ldap_result( op, rs );
2014 /* compute it anyway; root does not use it */
2015 stoptime = op->o_time + op->ors_tlimit;
2018 bsi.bsi_e = &base_entry;
2019 rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn,
2021 stoptime, op->ors_filter,
2022 dbh, op, rs, op->ors_attrs,
2023 ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) );
2024 switch ( rs->sr_err ) {
2029 if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
2030 dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) )
2032 rs->sr_err = LDAP_SUCCESS;
2034 rs->sr_matched = NULL;
2036 ber_bvarray_free( rs->sr_ref );
2042 /* an entry was created; free it */
2043 entry_clean( bsi.bsi_e );
2048 if ( !BER_BVISNULL( &base_entry.e_nname )
2049 && !access_allowed( op, &base_entry,
2050 slap_schema.si_ad_entry, NULL,
2051 ACL_DISCLOSE, NULL ) )
2053 rs->sr_err = LDAP_NO_SUCH_OBJECT;
2055 ber_bvarray_free( rs->sr_ref );
2058 rs->sr_matched = NULL;
2062 send_ldap_result( op, rs );
2065 ber_bvarray_free( rs->sr_ref );
2069 if ( !BER_BVISNULL( &base_entry.e_nname ) ) {
2070 entry_clean( &base_entry );
2075 /* NOTE: __NEW__ "search" access is required
2076 * on searchBase object */
2080 if ( get_assert( op ) &&
2081 ( test_filter( op, &base_entry, get_assertion( op ) )
2082 != LDAP_COMPARE_TRUE ) )
2084 rs->sr_err = LDAP_ASSERTION_FAILED;
2087 if ( ! access_allowed_mask( op, &base_entry,
2088 slap_schema.si_ad_entry,
2089 NULL, ACL_SEARCH, NULL, &mask ) )
2091 if ( rs->sr_err == LDAP_SUCCESS ) {
2092 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
2096 if ( rs->sr_err != LDAP_SUCCESS ) {
2097 if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
2098 rs->sr_err = LDAP_NO_SUCH_OBJECT;
2101 send_ldap_result( op, rs );
2108 bsi.bsi_n_candidates =
2109 ( op->ors_limit == NULL /* isroot == TRUE */ ? -2 :
2110 ( op->ors_limit->lms_s_unchecked == -1 ? -2 :
2111 ( op->ors_limit->lms_s_unchecked ) ) );
2113 /* If paged results are in effect, check the paging cookie */
2114 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
2115 PagedResultsState *ps = op->o_pagedresults_state;
2116 rs->sr_err = parse_paged_cookie( op, rs );
2117 if ( rs->sr_err != LDAP_SUCCESS ) {
2118 send_ldap_result( op, rs );
2123 switch ( bsi.bsi_scope ) {
2124 case LDAP_SCOPE_BASE:
2125 case BACKSQL_SCOPE_BASE_LIKE:
2127 * probably already found...
2129 bsi.bsi_id_list = &bsi.bsi_base_id;
2130 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
2133 case LDAP_SCOPE_SUBTREE:
2135 * if baseObject is defined, and if it is the root
2136 * of the search, add it to the candidate list
2138 if ( bi->sql_baseObject && BACKSQL_IS_BASEOBJECT_ID( &bsi.bsi_base_id.eid_id ) )
2140 bsi.bsi_id_list = &bsi.bsi_base_id;
2141 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
2148 * for each objectclass we try to construct query which gets IDs
2149 * of entries matching LDAP query filter and scope (or at least
2150 * candidates), and get the IDs. Do this in ID order for paging.
2152 avl_apply( bi->sql_oc_by_id, backsql_oc_get_candidates,
2153 &bsi, BACKSQL_AVL_STOP, AVL_INORDER );
2155 /* check for abandon */
2156 if ( op->o_abandon ) {
2157 eid = bsi.bsi_id_list;
2158 rs->sr_err = SLAPD_ABANDON;
2163 if ( op->ors_limit != NULL /* isroot == FALSE */
2164 && op->ors_limit->lms_s_unchecked != -1
2165 && bsi.bsi_n_candidates == -1 )
2167 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
2168 send_ldap_result( op, rs );
2173 * now we load candidate entries (only those attributes
2174 * mentioned in attrs and filter), test it against full filter
2175 * and then send to client; don't free entry_id if baseObject...
2177 for ( eid = bsi.bsi_id_list;
2179 eid = backsql_free_entryID( op,
2180 eid, eid == &bsi.bsi_base_id ? 0 : 1 ) )
2183 Attribute *a_hasSubordinate = NULL,
2184 *a_entryUUID = NULL,
2189 /* check for abandon */
2190 if ( op->o_abandon ) {
2191 rs->sr_err = SLAPD_ABANDON;
2195 /* check time limit */
2196 if ( op->ors_tlimit != SLAP_NO_LIMIT
2197 && slap_get_time() > stoptime )
2199 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
2200 rs->sr_ctrls = NULL;
2201 rs->sr_ref = rs->sr_v2ref;
2205 #ifdef BACKSQL_ARBITRARY_KEY
2206 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
2207 "for entry id=%s, oc_id=%ld, keyval=%s\n",
2208 eid->eid_id.bv_val, eid->eid_oc_id,
2209 eid->eid_keyval.bv_val );
2210 #else /* ! BACKSQL_ARBITRARY_KEY */
2211 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
2212 "for entry id=%ld, oc_id=%ld, keyval=%ld\n",
2213 eid->eid_id, eid->eid_oc_id, eid->eid_keyval );
2214 #endif /* ! BACKSQL_ARBITRARY_KEY */
2217 switch ( op->ors_scope ) {
2218 case LDAP_SCOPE_BASE:
2219 case BACKSQL_SCOPE_BASE_LIKE:
2220 if ( !dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
2225 case LDAP_SCOPE_ONE:
2227 struct berval rdn = eid->eid_ndn;
2229 rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
2230 if ( !dnIsOneLevelRDN( &rdn ) ) {
2236 case LDAP_SCOPE_SUBORDINATE:
2237 /* discard the baseObject entry */
2238 if ( dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
2242 case LDAP_SCOPE_SUBTREE:
2243 /* FIXME: this should never fail... */
2244 if ( !dnIsSuffix( &eid->eid_ndn, &op->o_req_ndn ) ) {
2250 if ( BACKSQL_IS_BASEOBJECT_ID( &eid->eid_id ) ) {
2251 /* don't recollect baseObject... */
2252 e = bi->sql_baseObject;
2254 } else if ( eid == &bsi.bsi_base_id ) {
2255 /* don't recollect searchBase object... */
2259 bsi.bsi_e = &user_entry;
2260 rc = backsql_id2entry( &bsi, eid );
2261 if ( rc != LDAP_SUCCESS ) {
2262 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2263 "error %d in backsql_id2entry() "
2264 "- skipping\n", rc, 0, 0 );
2270 if ( !manageDSAit &&
2271 op->ors_scope != LDAP_SCOPE_BASE &&
2272 op->ors_scope != BACKSQL_SCOPE_BASE_LIKE &&
2273 is_entry_referral( e ) )
2277 refs = get_entry_referrals( op, e );
2279 backsql_srch_info bsi2 = { 0 };
2280 Entry user_entry2 = { 0 };
2282 /* retry with the full entry... */
2283 bsi2.bsi_e = &user_entry2;
2284 rc = backsql_init_search( &bsi2,
2289 BACKSQL_ISF_GET_ENTRY );
2290 if ( rc == LDAP_SUCCESS ) {
2291 if ( is_entry_referral( &user_entry2 ) )
2293 refs = get_entry_referrals( op,
2296 rs->sr_err = LDAP_OTHER;
2298 backsql_entry_clean( op, &user_entry2 );
2300 if ( bsi2.bsi_attrs != NULL ) {
2301 op->o_tmpfree( bsi2.bsi_attrs,
2307 rs->sr_ref = referral_rewrite( refs,
2311 ber_bvarray_free( refs );
2315 rs->sr_err = LDAP_REFERRAL;
2318 rs->sr_text = "bad referral object";
2322 rs->sr_matched = user_entry.e_name.bv_val;
2323 send_search_reference( op, rs );
2325 ber_bvarray_free( rs->sr_ref );
2327 rs->sr_matched = NULL;
2328 rs->sr_entry = NULL;
2334 * We use this flag since we need to parse the filter
2335 * anyway; we should have used the frontend API function
2336 * filter_has_subordinates()
2338 if ( bsi.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) {
2339 rc = backsql_has_children( op, dbh, &e->e_nname );
2342 case LDAP_COMPARE_TRUE:
2343 case LDAP_COMPARE_FALSE:
2344 a_hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE );
2345 if ( a_hasSubordinate != NULL ) {
2346 for ( ap = &user_entry.e_attrs;
2348 ap = &(*ap)->a_next );
2350 *ap = a_hasSubordinate;
2356 Debug(LDAP_DEBUG_TRACE,
2357 "backsql_search(): "
2358 "has_children failed( %d)\n",
2365 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYUUID ) {
2366 a_entryUUID = backsql_operational_entryUUID( bi, eid );
2367 if ( a_entryUUID != NULL ) {
2369 ap = &user_entry.e_attrs;
2372 for ( ; *ap; ap = &(*ap)->a_next );
2378 #ifdef BACKSQL_SYNCPROV
2379 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYCSN ) {
2380 a_entryCSN = backsql_operational_entryCSN( op );
2381 if ( a_entryCSN != NULL ) {
2383 ap = &user_entry.e_attrs;
2386 for ( ; *ap; ap = &(*ap)->a_next );
2391 #endif /* BACKSQL_SYNCPROV */
2393 if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE )
2395 /* If paged results are in effect, see if the page limit was exceeded */
2396 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
2397 if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size )
2400 send_paged_response( op, rs, &lastid );
2403 lastid = SQL_TO_PAGECOOKIE( eid->eid_id, eid->eid_oc_id );
2405 rs->sr_attrs = op->ors_attrs;
2406 rs->sr_operational_attrs = NULL;
2408 rs->sr_flags = ( e == &user_entry ) ? REP_ENTRY_MODIFIABLE : 0;
2409 /* FIXME: need the whole entry (ITS#3480) */
2410 rs->sr_err = send_search_entry( op, rs );
2411 rs->sr_entry = NULL;
2412 rs->sr_attrs = NULL;
2413 rs->sr_operational_attrs = NULL;
2415 switch ( rs->sr_err ) {
2416 case LDAP_UNAVAILABLE:
2418 * FIXME: send_search_entry failed;
2421 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2422 "connection lost\n", 0, 0, 0 );
2425 case LDAP_SIZELIMIT_EXCEEDED:
2431 if ( e == &user_entry ) {
2432 backsql_entry_clean( op, &user_entry );
2439 if ( rs->sr_nentries > 0 ) {
2440 rs->sr_ref = rs->sr_v2ref;
2441 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
2445 rs->sr_err = bsi.bsi_status;
2449 if ( rs->sr_err != SLAPD_ABANDON ) {
2450 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
2451 send_paged_response( op, rs, NULL );
2453 send_ldap_result( op, rs );
2457 /* cleanup in case of abandon */
2458 for ( ; eid != NULL;
2459 eid = backsql_free_entryID( op,
2460 eid, eid == &bsi.bsi_base_id ? 0 : 1 ) )
2463 backsql_entry_clean( op, &base_entry );
2465 /* in case we got here accidentally */
2466 backsql_entry_clean( op, &user_entry );
2468 if ( rs->sr_v2ref ) {
2469 ber_bvarray_free( rs->sr_v2ref );
2470 rs->sr_v2ref = NULL;
2473 #ifdef BACKSQL_SYNCPROV
2475 Operation op2 = *op;
2476 SlapReply rs2 = { 0 };
2477 Entry *e = entry_alloc();
2478 slap_callback cb = { 0 };
2480 op2.o_tag = LDAP_REQ_ADD;
2481 op2.o_bd = select_backend( &op->o_bd->be_nsuffix[0], 0 );
2483 op2.o_callback = &cb;
2485 ber_dupbv( &e->e_name, op->o_bd->be_suffix );
2486 ber_dupbv( &e->e_nname, op->o_bd->be_nsuffix );
2488 cb.sc_response = slap_null_cb;
2490 op2.o_bd->be_add( &op2, &rs2 );
2492 if ( op2.ora_e == e )
2495 #endif /* BACKSQL_SYNCPROV */
2498 (void)backsql_free_entryID( op, &bsi.bsi_base_id, 0 );
2500 if ( bsi.bsi_attrs != NULL ) {
2501 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
2504 if ( !BER_BVISNULL( &nbase )
2505 && nbase.bv_val != op->o_req_ndn.bv_val )
2507 ch_free( nbase.bv_val );
2510 /* restore scope ... FIXME: this should be done before ANY
2511 * frontend call that uses op */
2512 if ( op->ors_scope == BACKSQL_SCOPE_BASE_LIKE ) {
2513 op->ors_scope = LDAP_SCOPE_BASE;
2516 Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n", 0, 0, 0 );
2521 /* return LDAP_SUCCESS IFF we can retrieve the specified entry.
2528 AttributeDescription *at,
2532 backsql_srch_info bsi = { 0 };
2533 SQLHDBC dbh = SQL_NULL_HDBC;
2535 SlapReply rs = { 0 };
2536 AttributeName anlist[ 2 ];
2540 rc = backsql_get_db_conn( op, &dbh );
2546 anlist[ 0 ].an_name = at->ad_cname;
2547 anlist[ 0 ].an_desc = at;
2548 BER_BVZERO( &anlist[ 1 ].an_name );
2551 bsi.bsi_e = entry_alloc();
2552 rc = backsql_init_search( &bsi,
2556 dbh, op, &rs, at ? anlist : NULL,
2557 BACKSQL_ISF_GET_ENTRY );
2559 if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
2560 (void)backsql_free_entryID( op, &bsi.bsi_base_id, 0 );
2563 if ( rc == LDAP_SUCCESS ) {
2565 #if 0 /* not supported at present */
2566 /* find attribute values */
2567 if ( is_entry_alias( bsi.bsi_e ) ) {
2568 Debug( LDAP_DEBUG_ACL,
2569 "<= backsql_entry_get: entry is an alias\n",
2571 rc = LDAP_ALIAS_PROBLEM;
2572 goto return_results;
2576 if ( is_entry_referral( bsi.bsi_e ) ) {
2577 Debug( LDAP_DEBUG_ACL,
2578 "<= backsql_entry_get: entry is a referral\n",
2581 goto return_results;
2584 if ( oc && !is_entry_objectclass( bsi.bsi_e, oc, 0 ) ) {
2585 Debug( LDAP_DEBUG_ACL,
2586 "<= backsql_entry_get: "
2587 "failed to find objectClass\n",
2589 rc = LDAP_NO_SUCH_ATTRIBUTE;
2590 goto return_results;
2597 if ( bsi.bsi_attrs != NULL ) {
2598 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
2601 if ( rc != LDAP_SUCCESS ) {
2603 entry_free( bsi.bsi_e );
2611 backsql_entry_clean(
2617 ctx = ldap_pvt_thread_pool_context();
2619 if ( ctx == NULL || ctx != op->o_tmpmemctx ) {
2620 if ( !BER_BVISNULL( &e->e_name ) ) {
2621 op->o_tmpfree( e->e_name.bv_val, op->o_tmpmemctx );
2622 BER_BVZERO( &e->e_name );
2625 if ( !BER_BVISNULL( &e->e_nname ) ) {
2626 op->o_tmpfree( e->e_nname.bv_val, op->o_tmpmemctx );
2627 BER_BVZERO( &e->e_nname );
2635 backsql_entry_release(
2640 backsql_entry_clean( op, e );
2648 /* This function is copied verbatim from back-bdb/search.c */
2650 parse_paged_cookie( Operation *op, SlapReply *rs )
2653 int rc = LDAP_SUCCESS;
2657 struct berval cookie = BER_BVNULL;
2658 PagedResultsState *ps = op->o_pagedresults_state;
2660 /* this function must be invoked only if the pagedResults
2661 * control has been detected, parsed and partially checked
2662 * by the frontend */
2663 assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
2665 /* look for the appropriate ctrl structure */
2666 for ( c = op->o_ctrls; c[0] != NULL; c++ ) {
2667 if ( strcmp( c[0]->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS ) == 0 )
2673 if ( c[0] == NULL ) {
2674 rs->sr_text = "missing pagedResults control";
2675 return LDAP_PROTOCOL_ERROR;
2678 /* Tested by frontend */
2679 assert( c[0]->ldctl_value.bv_len > 0 );
2681 /* Parse the control value
2682 * realSearchControlValue ::= SEQUENCE {
2683 * size INTEGER (0..maxInt),
2684 * -- requested page size from client
2685 * -- result set size estimate from server
2686 * cookie OCTET STRING
2689 ber = ber_init( &c[0]->ldctl_value );
2690 if ( ber == NULL ) {
2691 rs->sr_text = "internal error";
2695 tag = ber_scanf( ber, "{im}", &size, &cookie );
2697 /* Tested by frontend */
2698 assert( tag != LBER_ERROR );
2699 assert( size >= 0 );
2701 /* cookie decoding/checks deferred to backend... */
2702 if ( cookie.bv_len ) {
2703 PagedResultsCookie reqcookie;
2704 if( cookie.bv_len != sizeof( reqcookie ) ) {
2706 rs->sr_text = "paged results cookie is invalid";
2707 rc = LDAP_PROTOCOL_ERROR;
2711 AC_MEMCPY( &reqcookie, cookie.bv_val, sizeof( reqcookie ));
2713 if ( reqcookie > ps->ps_cookie ) {
2715 rs->sr_text = "paged results cookie is invalid";
2716 rc = LDAP_PROTOCOL_ERROR;
2719 } else if ( reqcookie < ps->ps_cookie ) {
2720 rs->sr_text = "paged results cookie is invalid or old";
2721 rc = LDAP_UNWILLING_TO_PERFORM;
2726 /* Initial request. Initialize state. */
2728 if ( op->o_conn->c_pagedresults_state.ps_cookie != 0 ) {
2729 /* There's another pagedResults control on the
2730 * same connection; reject new pagedResults controls
2731 * (allowed by RFC2696) */
2732 rs->sr_text = "paged results cookie unavailable; try later";
2733 rc = LDAP_UNWILLING_TO_PERFORM;
2742 (void)ber_free( ber, 1 );
2747 /* This function is copied nearly verbatim from back-bdb/search.c */
2749 send_paged_response(
2752 unsigned long *lastid )
2754 LDAPControl ctrl, *ctrls[2];
2755 BerElementBuffer berbuf;
2756 BerElement *ber = (BerElement *)&berbuf;
2757 PagedResultsCookie respcookie;
2758 struct berval cookie;
2760 Debug(LDAP_DEBUG_ARGS,
2761 "send_paged_response: lastid=0x%08lx nentries=%d\n",
2762 lastid ? *lastid : 0, rs->sr_nentries, NULL );
2764 BER_BVZERO( &ctrl.ldctl_value );
2768 ber_init2( ber, NULL, LBER_USE_DER );
2771 respcookie = ( PagedResultsCookie )(*lastid);
2772 cookie.bv_len = sizeof( respcookie );
2773 cookie.bv_val = (char *)&respcookie;
2776 respcookie = ( PagedResultsCookie )0;
2777 BER_BVSTR( &cookie, "" );
2780 op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
2781 op->o_conn->c_pagedresults_state.ps_count =
2782 ((PagedResultsState *)op->o_pagedresults_state)->ps_count +
2785 /* return size of 0 -- no estimate */
2786 ber_printf( ber, "{iO}", 0, &cookie );
2788 if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
2792 ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
2793 ctrls[0]->ldctl_iscritical = 0;
2795 rs->sr_ctrls = ctrls;
2796 rs->sr_err = LDAP_SUCCESS;
2797 send_ldap_result( op, rs );
2798 rs->sr_ctrls = NULL;
2801 (void) ber_free_buf( ber );