Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / bsd / openldap / dist / servers / slapd / back-sql / search.c
blob8294b077b822363d9b94768eb78583c6171bd912
1 /* $OpenLDAP: pkg/ldap/servers/slapd/back-sql/search.c,v 1.117.2.8 2008/02/11 23:26:48 kurt Exp $ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1999-2008 The OpenLDAP Foundation.
5 * Portions Copyright 1999 Dmitry Kovalev.
6 * Portions Copyright 2002 Pierangelo Masarati.
7 * Portions Copyright 2004 Mark Adamson.
8 * All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
12 * Public License.
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>.
18 /* ACKNOWLEDGEMENTS:
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.
24 #include "portable.h"
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include "ac/string.h"
29 #include "ac/ctype.h"
31 #include "lutil.h"
32 #include "slap.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(
58 Operation *op,
59 SlapReply *rs,
60 ID *lastid );
62 static int
63 backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad )
65 int n_attrs = 0;
66 AttributeName *an = NULL;
68 if ( bsi->bsi_attrs == NULL ) {
69 return 1;
73 * clear the list (retrieve all attrs)
75 if ( ad == NULL ) {
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;
79 return 1;
82 /* strip ';binary' */
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 ) ) {
98 return 1;
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 );
108 if ( an == NULL ) {
109 return -1;
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 );
116 bsi->bsi_attrs = an;
118 return 1;
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
131 backsql_init_search(
132 backsql_srch_info *bsi,
133 struct berval *nbase,
134 int scope,
135 time_t stoptime,
136 Filter *filter,
137 SQLHDBC dbh,
138 Operation *op,
139 SlapReply *rs,
140 AttributeName *attrs,
141 unsigned flags )
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;
152 bsi->bsi_dbh = dbh;
153 bsi->bsi_op = op;
154 bsi->bsi_rs = rs;
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;
165 } else {
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;
177 } else {
178 AttributeName *p;
179 int got_oc = 0;
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 ) {
188 /* handle "*" */
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;
197 break;
199 continue;
201 } else if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 ) {
202 /* handle "+" */
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;
211 break;
213 continue;
215 } else if ( BACKSQL_NCMP( &p->an_name, &NoAttrs ) == 0 ) {
216 /* ignore "1.1" */
217 continue;
219 } else if ( p->an_desc == slap_schema.si_ad_objectClass ) {
220 got_oc = 1;
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
230 * or so... */
231 backsql_attrlist_add( bsi, slap_schema.si_ad_objectClass );
235 if ( !BSQL_ISF_ALL_ATTRS( bsi ) && bi->sql_anlist ) {
236 AttributeName *p;
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 ) {
241 /* handle "*" */
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;
250 break;
252 continue;
254 } else if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 ) {
255 /* handle "+" */
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;
264 break;
266 continue;
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 );
292 int gotit = 0;
294 assert( op->o_bd->be_private != NULL );
296 rc = backsql_dn2id( op, rs, dbh, nbase, &bsi->bsi_base_id,
297 matched, 1 );
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 ) )
308 gotit = 1;
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 );
320 if ( erefs ) {
321 rc = rs->sr_err = LDAP_REFERRAL;
322 rs->sr_ref = referral_rewrite( erefs,
323 &bsi->bsi_e->e_nname,
324 &op->o_req_dn,
325 scope );
326 ber_bvarray_free( erefs );
328 } else {
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;
338 } else {
339 rs->sr_err = rc;
343 if ( gotit && BACKSQL_IS_GET_OC( flags ) ) {
344 bsi->bsi_base_id.eid_oc = backsql_id2oc( bi,
345 bsi->bsi_base_id.eid_oc_id );
346 if ( bsi->bsi_base_id.eid_oc == NULL ) {
347 /* error? */
348 backsql_free_entryID( &bsi->bsi_base_id, 1,
349 op->o_tmpmemctx );
350 rc = rs->sr_err = LDAP_OTHER;
355 bsi->bsi_status = rc;
357 switch ( rc ) {
358 case LDAP_SUCCESS:
359 case LDAP_REFERRAL:
360 break;
362 default:
363 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
364 bsi->bsi_op->o_tmpmemctx );
365 break;
368 return rc;
371 static int
372 backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op )
374 int res;
376 if ( !f ) {
377 return 0;
380 backsql_strfcat_x( &bsi->bsi_flt_where,
381 bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */ );
383 while ( 1 ) {
384 res = backsql_process_filter( bsi, f );
385 if ( res < 0 ) {
387 * TimesTen : If the query has no answers,
388 * don't bother to run the query.
390 return -1;
393 f = f->f_next;
394 if ( f == NULL ) {
395 break;
398 switch ( op ) {
399 case LDAP_FILTER_AND:
400 backsql_strfcat_x( &bsi->bsi_flt_where,
401 bsi->bsi_op->o_tmpmemctx, "l",
402 (ber_len_t)STRLENOF( " AND " ),
403 " AND " );
404 break;
406 case LDAP_FILTER_OR:
407 backsql_strfcat_x( &bsi->bsi_flt_where,
408 bsi->bsi_op->o_tmpmemctx, "l",
409 (ber_len_t)STRLENOF( " OR " ),
410 " OR " );
411 break;
415 backsql_strfcat_x( &bsi->bsi_flt_where,
416 bsi->bsi_op->o_tmpmemctx, "c", /* ( */ ')' );
418 return 1;
421 static int
422 backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f,
423 backsql_at_map_rec *at )
425 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
426 int i;
427 int casefold = 0;
429 if ( !f ) {
430 return 0;
433 /* always uppercase strings by now */
434 #ifdef BACKSQL_UPPERCASE_FILTER
435 if ( f->f_sub_desc->ad_type->sat_substr &&
436 SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
437 bi->sql_caseIgnoreMatch ) )
438 #endif /* BACKSQL_UPPERCASE_FILTER */
440 casefold = 1;
443 if ( f->f_sub_desc->ad_type->sat_substr &&
444 SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
445 bi->sql_telephoneNumberMatch ) )
448 struct berval bv;
449 ber_len_t i, s, a;
452 * to check for matching telephone numbers
453 * with intermixed chars, e.g. val='1234'
454 * use
456 * val LIKE '%1%2%3%4%'
459 BER_BVZERO( &bv );
460 if ( f->f_sub_initial.bv_val ) {
461 bv.bv_len += f->f_sub_initial.bv_len;
463 if ( f->f_sub_any != NULL ) {
464 for ( a = 0; f->f_sub_any[ a ].bv_val != NULL; a++ ) {
465 bv.bv_len += f->f_sub_any[ a ].bv_len;
468 if ( f->f_sub_final.bv_val ) {
469 bv.bv_len += f->f_sub_final.bv_len;
471 bv.bv_len = 2 * bv.bv_len - 1;
472 bv.bv_val = ch_malloc( bv.bv_len + 1 );
474 s = 0;
475 if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
476 bv.bv_val[ s ] = f->f_sub_initial.bv_val[ 0 ];
477 for ( i = 1; i < f->f_sub_initial.bv_len; i++ ) {
478 bv.bv_val[ s + 2 * i - 1 ] = '%';
479 bv.bv_val[ s + 2 * i ] = f->f_sub_initial.bv_val[ i ];
481 bv.bv_val[ s + 2 * i - 1 ] = '%';
482 s += 2 * i;
485 if ( f->f_sub_any != NULL ) {
486 for ( a = 0; !BER_BVISNULL( &f->f_sub_any[ a ] ); a++ ) {
487 bv.bv_val[ s ] = f->f_sub_any[ a ].bv_val[ 0 ];
488 for ( i = 1; i < f->f_sub_any[ a ].bv_len; i++ ) {
489 bv.bv_val[ s + 2 * i - 1 ] = '%';
490 bv.bv_val[ s + 2 * i ] = f->f_sub_any[ a ].bv_val[ i ];
492 bv.bv_val[ s + 2 * i - 1 ] = '%';
493 s += 2 * i;
497 if ( !BER_BVISNULL( &f->f_sub_final ) ) {
498 bv.bv_val[ s ] = f->f_sub_final.bv_val[ 0 ];
499 for ( i = 1; i < f->f_sub_final.bv_len; i++ ) {
500 bv.bv_val[ s + 2 * i - 1 ] = '%';
501 bv.bv_val[ s + 2 * i ] = f->f_sub_final.bv_val[ i ];
503 bv.bv_val[ s + 2 * i - 1 ] = '%';
504 s += 2 * i;
507 bv.bv_val[ s - 1 ] = '\0';
509 (void)backsql_process_filter_like( bsi, at, casefold, &bv );
510 ch_free( bv.bv_val );
512 return 1;
516 * When dealing with case-sensitive strings
517 * we may omit normalization; however, normalized
518 * SQL filters are more liberal.
521 backsql_strfcat_x( &bsi->bsi_flt_where,
522 bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */ );
524 /* TimesTen */
525 Debug( LDAP_DEBUG_TRACE, "backsql_process_sub_filter(%s):\n",
526 at->bam_ad->ad_cname.bv_val, 0, 0 );
527 Debug(LDAP_DEBUG_TRACE, " expr: '%s%s%s'\n", at->bam_sel_expr.bv_val,
528 at->bam_sel_expr_u.bv_val ? "' '" : "",
529 at->bam_sel_expr_u.bv_val ? at->bam_sel_expr_u.bv_val : "" );
530 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
532 * If a pre-upper-cased version of the column
533 * or a precompiled upper function exists, use it
535 backsql_strfcat_x( &bsi->bsi_flt_where,
536 bsi->bsi_op->o_tmpmemctx,
537 "bl",
538 &at->bam_sel_expr_u,
539 (ber_len_t)STRLENOF( " LIKE '" ),
540 " LIKE '" );
542 } else {
543 backsql_strfcat_x( &bsi->bsi_flt_where,
544 bsi->bsi_op->o_tmpmemctx,
545 "bl",
546 &at->bam_sel_expr,
547 (ber_len_t)STRLENOF( " LIKE '" ), " LIKE '" );
550 if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
551 ber_len_t start;
553 #ifdef BACKSQL_TRACE
554 Debug( LDAP_DEBUG_TRACE,
555 "==>backsql_process_sub_filter(%s): "
556 "sub_initial=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
557 f->f_sub_initial.bv_val, 0 );
558 #endif /* BACKSQL_TRACE */
560 start = bsi->bsi_flt_where.bb_val.bv_len;
561 backsql_strfcat_x( &bsi->bsi_flt_where,
562 bsi->bsi_op->o_tmpmemctx,
563 "b",
564 &f->f_sub_initial );
565 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
566 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
570 backsql_strfcat_x( &bsi->bsi_flt_where,
571 bsi->bsi_op->o_tmpmemctx,
572 "c", '%' );
574 if ( f->f_sub_any != NULL ) {
575 for ( i = 0; !BER_BVISNULL( &f->f_sub_any[ i ] ); i++ ) {
576 ber_len_t start;
578 #ifdef BACKSQL_TRACE
579 Debug( LDAP_DEBUG_TRACE,
580 "==>backsql_process_sub_filter(%s): "
581 "sub_any[%d]=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
582 i, f->f_sub_any[ i ].bv_val );
583 #endif /* BACKSQL_TRACE */
585 start = bsi->bsi_flt_where.bb_val.bv_len;
586 backsql_strfcat_x( &bsi->bsi_flt_where,
587 bsi->bsi_op->o_tmpmemctx,
588 "bc",
589 &f->f_sub_any[ i ],
590 '%' );
591 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
593 * Note: toupper('%') = '%'
595 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
600 if ( !BER_BVISNULL( &f->f_sub_final ) ) {
601 ber_len_t start;
603 #ifdef BACKSQL_TRACE
604 Debug( LDAP_DEBUG_TRACE,
605 "==>backsql_process_sub_filter(%s): "
606 "sub_final=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
607 f->f_sub_final.bv_val, 0 );
608 #endif /* BACKSQL_TRACE */
610 start = bsi->bsi_flt_where.bb_val.bv_len;
611 backsql_strfcat_x( &bsi->bsi_flt_where,
612 bsi->bsi_op->o_tmpmemctx,
613 "b",
614 &f->f_sub_final );
615 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
616 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
620 backsql_strfcat_x( &bsi->bsi_flt_where,
621 bsi->bsi_op->o_tmpmemctx,
622 "l",
623 (ber_len_t)STRLENOF( /* (' */ "')" ), /* (' */ "')" );
625 return 1;
628 static int
629 backsql_merge_from_tbls( backsql_srch_info *bsi, struct berval *from_tbls )
631 if ( BER_BVISNULL( from_tbls ) ) {
632 return LDAP_SUCCESS;
635 if ( !BER_BVISNULL( &bsi->bsi_from.bb_val ) ) {
636 char *start, *end;
637 struct berval tmp;
639 ber_dupbv_x( &tmp, from_tbls, bsi->bsi_op->o_tmpmemctx );
641 for ( start = tmp.bv_val, end = strchr( start, ',' ); start; ) {
642 if ( end ) {
643 end[0] = '\0';
646 if ( strstr( bsi->bsi_from.bb_val.bv_val, start) == NULL )
648 backsql_strfcat_x( &bsi->bsi_from,
649 bsi->bsi_op->o_tmpmemctx,
650 "cs", ',', start );
653 if ( end ) {
654 /* in case there are spaces after the comma... */
655 for ( start = &end[1]; isspace( start[0] ); start++ );
656 if ( start[0] ) {
657 end = strchr( start, ',' );
658 } else {
659 start = NULL;
661 } else {
662 start = NULL;
666 bsi->bsi_op->o_tmpfree( tmp.bv_val, bsi->bsi_op->o_tmpmemctx );
668 } else {
669 backsql_strfcat_x( &bsi->bsi_from,
670 bsi->bsi_op->o_tmpmemctx,
671 "b", from_tbls );
674 return LDAP_SUCCESS;
677 static int
678 backsql_process_filter( backsql_srch_info *bsi, Filter *f )
680 backsql_at_map_rec **vat = NULL;
681 AttributeDescription *ad = NULL;
682 unsigned i;
683 int done = 0;
684 int rc = 0;
686 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter()\n", 0, 0, 0 );
687 if ( f->f_choice == SLAPD_FILTER_COMPUTED ) {
688 struct berval flt;
689 char *msg = NULL;
691 switch ( f->f_result ) {
692 case LDAP_COMPARE_TRUE:
693 BER_BVSTR( &flt, "10=10" );
694 msg = "TRUE";
695 break;
697 case LDAP_COMPARE_FALSE:
698 BER_BVSTR( &flt, "11=0" );
699 msg = "FALSE";
700 break;
702 case SLAPD_COMPARE_UNDEFINED:
703 BER_BVSTR( &flt, "12=0" );
704 msg = "UNDEFINED";
705 break;
707 default:
708 rc = -1;
709 goto done;
712 Debug( LDAP_DEBUG_TRACE, "backsql_process_filter(): "
713 "filter computed (%s)\n", msg, 0, 0 );
714 backsql_strfcat_x( &bsi->bsi_flt_where,
715 bsi->bsi_op->o_tmpmemctx, "b", &flt );
716 rc = 1;
717 goto done;
720 switch( f->f_choice ) {
721 case LDAP_FILTER_OR:
722 rc = backsql_process_filter_list( bsi, f->f_or,
723 LDAP_FILTER_OR );
724 done = 1;
725 break;
727 case LDAP_FILTER_AND:
728 rc = backsql_process_filter_list( bsi, f->f_and,
729 LDAP_FILTER_AND );
730 done = 1;
731 break;
733 case LDAP_FILTER_NOT:
734 backsql_strfcat_x( &bsi->bsi_flt_where,
735 bsi->bsi_op->o_tmpmemctx,
736 "l",
737 (ber_len_t)STRLENOF( "NOT (" /* ) */ ),
738 "NOT (" /* ) */ );
739 rc = backsql_process_filter( bsi, f->f_not );
740 backsql_strfcat_x( &bsi->bsi_flt_where,
741 bsi->bsi_op->o_tmpmemctx,
742 "c", /* ( */ ')' );
743 done = 1;
744 break;
746 case LDAP_FILTER_PRESENT:
747 ad = f->f_desc;
748 break;
750 case LDAP_FILTER_EXT:
751 ad = f->f_mra->ma_desc;
752 if ( f->f_mr_dnattrs ) {
754 * if dn attrs filtering is requested, better return
755 * success and let test_filter() deal with candidate
756 * selection; otherwise we'd need to set conditions
757 * on the contents of the DN, e.g. "SELECT ... FROM
758 * ldap_entries AS attributeName WHERE attributeName.dn
759 * like '%attributeName=value%'"
761 backsql_strfcat_x( &bsi->bsi_flt_where,
762 bsi->bsi_op->o_tmpmemctx,
763 "l",
764 (ber_len_t)STRLENOF( "1=1" ), "1=1" );
765 bsi->bsi_status = LDAP_SUCCESS;
766 rc = 1;
767 goto done;
769 break;
771 default:
772 ad = f->f_av_desc;
773 break;
776 if ( rc == -1 ) {
777 goto done;
780 if ( done ) {
781 rc = 1;
782 goto done;
786 * Turn structuralObjectClass into objectClass
788 if ( ad == slap_schema.si_ad_objectClass
789 || ad == slap_schema.si_ad_structuralObjectClass )
792 * If the filter is LDAP_FILTER_PRESENT, then it's done;
793 * otherwise, let's see if we are lucky: filtering
794 * for "structural" objectclass or ancestor...
796 switch ( f->f_choice ) {
797 case LDAP_FILTER_EQUALITY:
799 ObjectClass *oc = oc_bvfind( &f->f_av_value );
801 if ( oc == NULL ) {
802 Debug( LDAP_DEBUG_TRACE,
803 "backsql_process_filter(): "
804 "unknown objectClass \"%s\" "
805 "in filter\n",
806 f->f_av_value.bv_val, 0, 0 );
807 bsi->bsi_status = LDAP_OTHER;
808 rc = -1;
809 goto done;
813 * "structural" objectClass inheritance:
814 * - a search for "person" will also return
815 * "inetOrgPerson"
816 * - a search for "top" will return everything
818 if ( is_object_subclass( oc, bsi->bsi_oc->bom_oc ) ) {
819 static struct berval ldap_entry_objclasses = BER_BVC( "ldap_entry_objclasses" );
821 backsql_merge_from_tbls( bsi, &ldap_entry_objclasses );
823 backsql_strfcat_x( &bsi->bsi_flt_where,
824 bsi->bsi_op->o_tmpmemctx,
825 "lbl",
826 (ber_len_t)STRLENOF( "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */ ),
827 "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */,
828 &bsi->bsi_oc->bom_oc->soc_cname,
829 (ber_len_t)STRLENOF( /* ((' */ "'))" ),
830 /* ((' */ "'))" );
831 bsi->bsi_status = LDAP_SUCCESS;
832 rc = 1;
833 goto done;
836 break;
839 case LDAP_FILTER_PRESENT:
840 backsql_strfcat_x( &bsi->bsi_flt_where,
841 bsi->bsi_op->o_tmpmemctx,
842 "l",
843 (ber_len_t)STRLENOF( "3=3" ), "3=3" );
844 bsi->bsi_status = LDAP_SUCCESS;
845 rc = 1;
846 goto done;
848 /* FIXME: LDAP_FILTER_EXT? */
850 default:
851 Debug( LDAP_DEBUG_TRACE,
852 "backsql_process_filter(): "
853 "illegal/unhandled filter "
854 "on objectClass attribute",
855 0, 0, 0 );
856 bsi->bsi_status = LDAP_OTHER;
857 rc = -1;
858 goto done;
861 } else if ( ad == slap_schema.si_ad_entryUUID ) {
862 unsigned long oc_id;
863 #ifdef BACKSQL_ARBITRARY_KEY
864 struct berval keyval;
865 #else /* ! BACKSQL_ARBITRARY_KEY */
866 unsigned long keyval;
867 char keyvalbuf[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
868 #endif /* ! BACKSQL_ARBITRARY_KEY */
870 switch ( f->f_choice ) {
871 case LDAP_FILTER_EQUALITY:
872 backsql_entryUUID_decode( &f->f_av_value, &oc_id, &keyval );
874 if ( oc_id != bsi->bsi_oc->bom_id ) {
875 bsi->bsi_status = LDAP_SUCCESS;
876 rc = -1;
877 goto done;
880 #ifdef BACKSQL_ARBITRARY_KEY
881 backsql_strfcat_x( &bsi->bsi_flt_where,
882 bsi->bsi_op->o_tmpmemctx,
883 "bcblbc",
884 &bsi->bsi_oc->bom_keytbl, '.',
885 &bsi->bsi_oc->bom_keycol,
886 STRLENOF( " LIKE '" ), " LIKE '",
887 &keyval, '\'' );
888 #else /* ! BACKSQL_ARBITRARY_KEY */
889 snprintf( keyvalbuf, sizeof( keyvalbuf ), "%lu", keyval );
890 backsql_strfcat_x( &bsi->bsi_flt_where,
891 bsi->bsi_op->o_tmpmemctx,
892 "bcbcs",
893 &bsi->bsi_oc->bom_keytbl, '.',
894 &bsi->bsi_oc->bom_keycol, '=', keyvalbuf );
895 #endif /* ! BACKSQL_ARBITRARY_KEY */
896 break;
898 case LDAP_FILTER_PRESENT:
899 backsql_strfcat_x( &bsi->bsi_flt_where,
900 bsi->bsi_op->o_tmpmemctx,
901 "l",
902 (ber_len_t)STRLENOF( "4=4" ), "4=4" );
903 break;
905 default:
906 rc = -1;
907 goto done;
910 bsi->bsi_flags |= BSQL_SF_FILTER_ENTRYUUID;
911 rc = 1;
912 goto done;
914 #ifdef BACKSQL_SYNCPROV
915 } else if ( ad == slap_schema.si_ad_entryCSN ) {
917 * support for syncrepl as producer...
919 #if 0
920 if ( !bsi->bsi_op->o_sync ) {
921 /* unsupported at present... */
922 bsi->bsi_status = LDAP_OTHER;
923 rc = -1;
924 goto done;
926 #endif
928 bsi->bsi_flags |= ( BSQL_SF_FILTER_ENTRYCSN | BSQL_SF_RETURN_ENTRYUUID);
930 /* if doing a syncrepl, try to return as much as possible,
931 * and always match the filter */
932 backsql_strfcat_x( &bsi->bsi_flt_where,
933 bsi->bsi_op->o_tmpmemctx,
934 "l",
935 (ber_len_t)STRLENOF( "5=5" ), "5=5" );
937 /* save for later use in operational attributes */
938 /* FIXME: saves only the first occurrence, because
939 * the filter during updates is written as
940 * "(&(entryCSN<={contextCSN})(entryCSN>={oldContextCSN})({filter}))"
941 * so we want our fake entryCSN to match the greatest
942 * value
944 if ( bsi->bsi_op->o_private == NULL ) {
945 bsi->bsi_op->o_private = &f->f_av_value;
947 bsi->bsi_status = LDAP_SUCCESS;
949 rc = 1;
950 goto done;
951 #endif /* BACKSQL_SYNCPROV */
953 } else if ( ad == slap_schema.si_ad_hasSubordinates || ad == NULL ) {
955 * FIXME: this is not robust; e.g. a filter
956 * '(!(hasSubordinates=TRUE))' fails because
957 * in SQL it would read 'NOT (1=1)' instead
958 * of no condition.
959 * Note however that hasSubordinates is boolean,
960 * so a more appropriate filter would be
961 * '(hasSubordinates=FALSE)'
963 * A more robust search for hasSubordinates
964 * would * require joining the ldap_entries table
965 * selecting if there are descendants of the
966 * candidate.
968 backsql_strfcat_x( &bsi->bsi_flt_where,
969 bsi->bsi_op->o_tmpmemctx,
970 "l",
971 (ber_len_t)STRLENOF( "6=6" ), "6=6" );
972 if ( ad == slap_schema.si_ad_hasSubordinates ) {
974 * instruct candidate selection algorithm
975 * and attribute list to try to detect
976 * if an entry has subordinates
978 bsi->bsi_flags |= BSQL_SF_FILTER_HASSUBORDINATE;
980 } else {
982 * clear attributes to fetch, to require ALL
983 * and try extended match on all attributes
985 backsql_attrlist_add( bsi, NULL );
987 rc = 1;
988 goto done;
992 * attribute inheritance:
994 if ( backsql_supad2at( bsi->bsi_oc, ad, &vat ) ) {
995 bsi->bsi_status = LDAP_OTHER;
996 rc = -1;
997 goto done;
1000 if ( vat == NULL ) {
1001 /* search anyway; other parts of the filter
1002 * may succeeed */
1003 backsql_strfcat_x( &bsi->bsi_flt_where,
1004 bsi->bsi_op->o_tmpmemctx,
1005 "l",
1006 (ber_len_t)STRLENOF( "7=7" ), "7=7" );
1007 bsi->bsi_status = LDAP_SUCCESS;
1008 rc = 1;
1009 goto done;
1012 /* if required, open extra level of parens */
1013 done = 0;
1014 if ( vat[0]->bam_next || vat[1] ) {
1015 backsql_strfcat_x( &bsi->bsi_flt_where,
1016 bsi->bsi_op->o_tmpmemctx,
1017 "c", '(' );
1018 done = 1;
1021 i = 0;
1022 next:;
1023 /* apply attr */
1024 if ( backsql_process_filter_attr( bsi, f, vat[i] ) == -1 ) {
1025 return -1;
1028 /* if more definitions of the same attr, apply */
1029 if ( vat[i]->bam_next ) {
1030 backsql_strfcat_x( &bsi->bsi_flt_where,
1031 bsi->bsi_op->o_tmpmemctx,
1032 "l",
1033 STRLENOF( " OR " ), " OR " );
1034 vat[i] = vat[i]->bam_next;
1035 goto next;
1038 /* if more descendants of the same attr, apply */
1039 i++;
1040 if ( vat[i] ) {
1041 backsql_strfcat_x( &bsi->bsi_flt_where,
1042 bsi->bsi_op->o_tmpmemctx,
1043 "l",
1044 STRLENOF( " OR " ), " OR " );
1045 goto next;
1048 /* if needed, close extra level of parens */
1049 if ( done ) {
1050 backsql_strfcat_x( &bsi->bsi_flt_where,
1051 bsi->bsi_op->o_tmpmemctx,
1052 "c", ')' );
1055 rc = 1;
1057 done:;
1058 if ( vat ) {
1059 ch_free( vat );
1062 Debug( LDAP_DEBUG_TRACE,
1063 "<==backsql_process_filter() %s\n",
1064 rc == 1 ? "succeeded" : "failed", 0, 0);
1066 return rc;
1069 static int
1070 backsql_process_filter_eq( backsql_srch_info *bsi, backsql_at_map_rec *at,
1071 int casefold, struct berval *filter_value )
1074 * maybe we should check type of at->sel_expr here somehow,
1075 * to know whether upper_func is applicable, but for now
1076 * upper_func stuff is made for Oracle, where UPPER is
1077 * safely applicable to NUMBER etc.
1079 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1080 ber_len_t start;
1082 backsql_strfcat_x( &bsi->bsi_flt_where,
1083 bsi->bsi_op->o_tmpmemctx,
1084 "cbl",
1085 '(', /* ) */
1086 &at->bam_sel_expr_u,
1087 (ber_len_t)STRLENOF( "='" ),
1088 "='" );
1090 start = bsi->bsi_flt_where.bb_val.bv_len;
1092 backsql_strfcat_x( &bsi->bsi_flt_where,
1093 bsi->bsi_op->o_tmpmemctx,
1094 "bl",
1095 filter_value,
1096 (ber_len_t)STRLENOF( /* (' */ "')" ),
1097 /* (' */ "')" );
1099 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1101 } else {
1102 backsql_strfcat_x( &bsi->bsi_flt_where,
1103 bsi->bsi_op->o_tmpmemctx,
1104 "cblbl",
1105 '(', /* ) */
1106 &at->bam_sel_expr,
1107 (ber_len_t)STRLENOF( "='" ), "='",
1108 filter_value,
1109 (ber_len_t)STRLENOF( /* (' */ "')" ),
1110 /* (' */ "')" );
1113 return 1;
1116 static int
1117 backsql_process_filter_like( backsql_srch_info *bsi, backsql_at_map_rec *at,
1118 int casefold, struct berval *filter_value )
1121 * maybe we should check type of at->sel_expr here somehow,
1122 * to know whether upper_func is applicable, but for now
1123 * upper_func stuff is made for Oracle, where UPPER is
1124 * safely applicable to NUMBER etc.
1126 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1127 ber_len_t start;
1129 backsql_strfcat_x( &bsi->bsi_flt_where,
1130 bsi->bsi_op->o_tmpmemctx,
1131 "cbl",
1132 '(', /* ) */
1133 &at->bam_sel_expr_u,
1134 (ber_len_t)STRLENOF( " LIKE '%" ),
1135 " LIKE '%" );
1137 start = bsi->bsi_flt_where.bb_val.bv_len;
1139 backsql_strfcat_x( &bsi->bsi_flt_where,
1140 bsi->bsi_op->o_tmpmemctx,
1141 "bl",
1142 filter_value,
1143 (ber_len_t)STRLENOF( /* (' */ "%')" ),
1144 /* (' */ "%')" );
1146 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1148 } else {
1149 backsql_strfcat_x( &bsi->bsi_flt_where,
1150 bsi->bsi_op->o_tmpmemctx,
1151 "cblbl",
1152 '(', /* ) */
1153 &at->bam_sel_expr,
1154 (ber_len_t)STRLENOF( " LIKE '%" ),
1155 " LIKE '%",
1156 filter_value,
1157 (ber_len_t)STRLENOF( /* (' */ "%')" ),
1158 /* (' */ "%')" );
1161 return 1;
1164 static int
1165 backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f, backsql_at_map_rec *at )
1167 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1168 int casefold = 0;
1169 struct berval *filter_value = NULL;
1170 MatchingRule *matching_rule = NULL;
1171 struct berval ordering = BER_BVC("<=");
1173 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter_attr(%s)\n",
1174 at->bam_ad->ad_cname.bv_val, 0, 0 );
1177 * need to add this attribute to list of attrs to load,
1178 * so that we can do test_filter() later
1180 backsql_attrlist_add( bsi, at->bam_ad );
1182 backsql_merge_from_tbls( bsi, &at->bam_from_tbls );
1184 if ( !BER_BVISNULL( &at->bam_join_where )
1185 && strstr( bsi->bsi_join_where.bb_val.bv_val,
1186 at->bam_join_where.bv_val ) == NULL )
1188 backsql_strfcat_x( &bsi->bsi_join_where,
1189 bsi->bsi_op->o_tmpmemctx,
1190 "lb",
1191 (ber_len_t)STRLENOF( " AND " ), " AND ",
1192 &at->bam_join_where );
1195 switch ( f->f_choice ) {
1196 case LDAP_FILTER_EQUALITY:
1197 filter_value = &f->f_av_value;
1198 matching_rule = at->bam_ad->ad_type->sat_equality;
1200 goto equality_match;
1202 /* fail over into next case */
1204 case LDAP_FILTER_EXT:
1205 filter_value = &f->f_mra->ma_value;
1206 matching_rule = f->f_mr_rule;
1208 equality_match:;
1209 /* always uppercase strings by now */
1210 #ifdef BACKSQL_UPPERCASE_FILTER
1211 if ( SLAP_MR_ASSOCIATED( matching_rule,
1212 bi->sql_caseIgnoreMatch ) )
1213 #endif /* BACKSQL_UPPERCASE_FILTER */
1215 casefold = 1;
1218 /* FIXME: directoryString filtering should use a similar
1219 * approach to deal with non-prettified values like
1220 * " A non prettified value ", by using a LIKE
1221 * filter with all whitespaces collapsed to a single '%' */
1222 if ( SLAP_MR_ASSOCIATED( matching_rule,
1223 bi->sql_telephoneNumberMatch ) )
1225 struct berval bv;
1226 ber_len_t i;
1229 * to check for matching telephone numbers
1230 * with intermized chars, e.g. val='1234'
1231 * use
1233 * val LIKE '%1%2%3%4%'
1236 bv.bv_len = 2 * filter_value->bv_len - 1;
1237 bv.bv_val = ch_malloc( bv.bv_len + 1 );
1239 bv.bv_val[ 0 ] = filter_value->bv_val[ 0 ];
1240 for ( i = 1; i < filter_value->bv_len; i++ ) {
1241 bv.bv_val[ 2 * i - 1 ] = '%';
1242 bv.bv_val[ 2 * i ] = filter_value->bv_val[ i ];
1244 bv.bv_val[ 2 * i - 1 ] = '\0';
1246 (void)backsql_process_filter_like( bsi, at, casefold, &bv );
1247 ch_free( bv.bv_val );
1249 break;
1252 /* NOTE: this is required by objectClass inheritance
1253 * and auxiliary objectClass use in filters for slightly
1254 * more efficient candidate selection. */
1255 /* FIXME: a bit too many specializations to deal with
1256 * very specific cases... */
1257 if ( at->bam_ad == slap_schema.si_ad_objectClass
1258 || at->bam_ad == slap_schema.si_ad_structuralObjectClass )
1260 backsql_strfcat_x( &bsi->bsi_flt_where,
1261 bsi->bsi_op->o_tmpmemctx,
1262 "lbl",
1263 (ber_len_t)STRLENOF( "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */ ),
1264 "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */,
1265 filter_value,
1266 (ber_len_t)STRLENOF( /* (' */ "')" ),
1267 /* (' */ "')" );
1268 break;
1272 * maybe we should check type of at->sel_expr here somehow,
1273 * to know whether upper_func is applicable, but for now
1274 * upper_func stuff is made for Oracle, where UPPER is
1275 * safely applicable to NUMBER etc.
1277 (void)backsql_process_filter_eq( bsi, at, casefold, filter_value );
1278 break;
1280 case LDAP_FILTER_GE:
1281 ordering.bv_val = ">=";
1283 /* fall thru to next case */
1285 case LDAP_FILTER_LE:
1286 filter_value = &f->f_av_value;
1288 /* always uppercase strings by now */
1289 #ifdef BACKSQL_UPPERCASE_FILTER
1290 if ( at->bam_ad->ad_type->sat_ordering &&
1291 SLAP_MR_ASSOCIATED( at->bam_ad->ad_type->sat_ordering,
1292 bi->sql_caseIgnoreMatch ) )
1293 #endif /* BACKSQL_UPPERCASE_FILTER */
1295 casefold = 1;
1299 * FIXME: should we uppercase the operands?
1301 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1302 ber_len_t start;
1304 backsql_strfcat_x( &bsi->bsi_flt_where,
1305 bsi->bsi_op->o_tmpmemctx,
1306 "cbbc",
1307 '(', /* ) */
1308 &at->bam_sel_expr_u,
1309 &ordering,
1310 '\'' );
1312 start = bsi->bsi_flt_where.bb_val.bv_len;
1314 backsql_strfcat_x( &bsi->bsi_flt_where,
1315 bsi->bsi_op->o_tmpmemctx,
1316 "bl",
1317 filter_value,
1318 (ber_len_t)STRLENOF( /* (' */ "')" ),
1319 /* (' */ "')" );
1321 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1323 } else {
1324 backsql_strfcat_x( &bsi->bsi_flt_where,
1325 bsi->bsi_op->o_tmpmemctx,
1326 "cbbcbl",
1327 '(' /* ) */ ,
1328 &at->bam_sel_expr,
1329 &ordering,
1330 '\'',
1331 &f->f_av_value,
1332 (ber_len_t)STRLENOF( /* (' */ "')" ),
1333 /* ( */ "')" );
1335 break;
1337 case LDAP_FILTER_PRESENT:
1338 backsql_strfcat_x( &bsi->bsi_flt_where,
1339 bsi->bsi_op->o_tmpmemctx,
1340 "lbl",
1341 (ber_len_t)STRLENOF( "NOT (" /* ) */),
1342 "NOT (", /* ) */
1343 &at->bam_sel_expr,
1344 (ber_len_t)STRLENOF( /* ( */ " IS NULL)" ),
1345 /* ( */ " IS NULL)" );
1346 break;
1348 case LDAP_FILTER_SUBSTRINGS:
1349 backsql_process_sub_filter( bsi, f, at );
1350 break;
1352 case LDAP_FILTER_APPROX:
1353 /* we do our best */
1356 * maybe we should check type of at->sel_expr here somehow,
1357 * to know whether upper_func is applicable, but for now
1358 * upper_func stuff is made for Oracle, where UPPER is
1359 * safely applicable to NUMBER etc.
1361 (void)backsql_process_filter_like( bsi, at, 1, &f->f_av_value );
1362 break;
1364 default:
1365 /* unhandled filter type; should not happen */
1366 assert( 0 );
1367 backsql_strfcat_x( &bsi->bsi_flt_where,
1368 bsi->bsi_op->o_tmpmemctx,
1369 "l",
1370 (ber_len_t)STRLENOF( "8=8" ), "8=8" );
1371 break;
1375 Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter_attr(%s)\n",
1376 at->bam_ad->ad_cname.bv_val, 0, 0 );
1378 return 1;
1381 static int
1382 backsql_srch_query( backsql_srch_info *bsi, struct berval *query )
1384 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1385 int rc;
1387 assert( query != NULL );
1388 BER_BVZERO( query );
1390 bsi->bsi_use_subtree_shortcut = 0;
1392 Debug( LDAP_DEBUG_TRACE, "==>backsql_srch_query()\n", 0, 0, 0 );
1393 BER_BVZERO( &bsi->bsi_sel.bb_val );
1394 BER_BVZERO( &bsi->bsi_sel.bb_val );
1395 bsi->bsi_sel.bb_len = 0;
1396 BER_BVZERO( &bsi->bsi_from.bb_val );
1397 bsi->bsi_from.bb_len = 0;
1398 BER_BVZERO( &bsi->bsi_join_where.bb_val );
1399 bsi->bsi_join_where.bb_len = 0;
1400 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1401 bsi->bsi_flt_where.bb_len = 0;
1403 backsql_strfcat_x( &bsi->bsi_sel,
1404 bsi->bsi_op->o_tmpmemctx,
1405 "lbcbc",
1406 (ber_len_t)STRLENOF( "SELECT DISTINCT ldap_entries.id," ),
1407 "SELECT DISTINCT ldap_entries.id,",
1408 &bsi->bsi_oc->bom_keytbl,
1409 '.',
1410 &bsi->bsi_oc->bom_keycol,
1411 ',' );
1413 if ( !BER_BVISNULL( &bi->sql_strcast_func ) ) {
1414 backsql_strfcat_x( &bsi->bsi_sel,
1415 bsi->bsi_op->o_tmpmemctx,
1416 "blbl",
1417 &bi->sql_strcast_func,
1418 (ber_len_t)STRLENOF( "('" /* ') */ ),
1419 "('" /* ') */ ,
1420 &bsi->bsi_oc->bom_oc->soc_cname,
1421 (ber_len_t)STRLENOF( /* (' */ "')" ),
1422 /* (' */ "')" );
1423 } else {
1424 backsql_strfcat_x( &bsi->bsi_sel,
1425 bsi->bsi_op->o_tmpmemctx,
1426 "cbc",
1427 '\'',
1428 &bsi->bsi_oc->bom_oc->soc_cname,
1429 '\'' );
1432 backsql_strfcat_x( &bsi->bsi_sel,
1433 bsi->bsi_op->o_tmpmemctx,
1434 "b",
1435 &bi->sql_dn_oc_aliasing );
1436 backsql_strfcat_x( &bsi->bsi_from,
1437 bsi->bsi_op->o_tmpmemctx,
1438 "lb",
1439 (ber_len_t)STRLENOF( " FROM ldap_entries," ),
1440 " FROM ldap_entries,",
1441 &bsi->bsi_oc->bom_keytbl );
1443 backsql_strfcat_x( &bsi->bsi_join_where,
1444 bsi->bsi_op->o_tmpmemctx,
1445 "lbcbl",
1446 (ber_len_t)STRLENOF( " WHERE " ), " WHERE ",
1447 &bsi->bsi_oc->bom_keytbl,
1448 '.',
1449 &bsi->bsi_oc->bom_keycol,
1450 (ber_len_t)STRLENOF( "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ),
1451 "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " );
1453 switch ( bsi->bsi_scope ) {
1454 case LDAP_SCOPE_BASE:
1455 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1456 backsql_strfcat_x( &bsi->bsi_join_where,
1457 bsi->bsi_op->o_tmpmemctx,
1458 "bl",
1459 &bi->sql_upper_func,
1460 (ber_len_t)STRLENOF( "(ldap_entries.dn)=?" ),
1461 "(ldap_entries.dn)=?" );
1462 } else {
1463 backsql_strfcat_x( &bsi->bsi_join_where,
1464 bsi->bsi_op->o_tmpmemctx,
1465 "l",
1466 (ber_len_t)STRLENOF( "ldap_entries.dn=?" ),
1467 "ldap_entries.dn=?" );
1469 break;
1471 case BACKSQL_SCOPE_BASE_LIKE:
1472 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1473 backsql_strfcat_x( &bsi->bsi_join_where,
1474 bsi->bsi_op->o_tmpmemctx,
1475 "bl",
1476 &bi->sql_upper_func,
1477 (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1478 "(ldap_entries.dn) LIKE ?" );
1479 } else {
1480 backsql_strfcat_x( &bsi->bsi_join_where,
1481 bsi->bsi_op->o_tmpmemctx,
1482 "l",
1483 (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1484 "ldap_entries.dn LIKE ?" );
1486 break;
1488 case LDAP_SCOPE_ONELEVEL:
1489 backsql_strfcat_x( &bsi->bsi_join_where,
1490 bsi->bsi_op->o_tmpmemctx,
1491 "l",
1492 (ber_len_t)STRLENOF( "ldap_entries.parent=?" ),
1493 "ldap_entries.parent=?" );
1494 break;
1496 case LDAP_SCOPE_SUBORDINATE:
1497 case LDAP_SCOPE_SUBTREE:
1498 if ( BACKSQL_USE_SUBTREE_SHORTCUT( bi ) ) {
1499 int i;
1500 BackendDB *bd = bsi->bsi_op->o_bd;
1502 assert( bd->be_nsuffix != NULL );
1504 for ( i = 0; !BER_BVISNULL( &bd->be_nsuffix[ i ] ); i++ )
1506 if ( dn_match( &bd->be_nsuffix[ i ],
1507 bsi->bsi_base_ndn ) )
1509 /* pass this to the candidate selection
1510 * routine so that the DN is not bound
1511 * to the select statement */
1512 bsi->bsi_use_subtree_shortcut = 1;
1513 break;
1518 if ( bsi->bsi_use_subtree_shortcut ) {
1519 /* Skip the base DN filter, as every entry will match it */
1520 backsql_strfcat_x( &bsi->bsi_join_where,
1521 bsi->bsi_op->o_tmpmemctx,
1522 "l",
1523 (ber_len_t)STRLENOF( "9=9"), "9=9");
1525 } else if ( !BER_BVISNULL( &bi->sql_subtree_cond ) ) {
1526 /* This should always be true... */
1527 backsql_strfcat_x( &bsi->bsi_join_where,
1528 bsi->bsi_op->o_tmpmemctx,
1529 "b",
1530 &bi->sql_subtree_cond );
1532 } else if ( BACKSQL_CANUPPERCASE( bi ) ) {
1533 backsql_strfcat_x( &bsi->bsi_join_where,
1534 bsi->bsi_op->o_tmpmemctx,
1535 "bl",
1536 &bi->sql_upper_func,
1537 (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1538 "(ldap_entries.dn) LIKE ?" );
1540 } else {
1541 backsql_strfcat_x( &bsi->bsi_join_where,
1542 bsi->bsi_op->o_tmpmemctx,
1543 "l",
1544 (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1545 "ldap_entries.dn LIKE ?" );
1548 break;
1550 default:
1551 assert( 0 );
1554 /* If paged results are in effect, ignore low ldap_entries.id numbers */
1555 if ( get_pagedresults(bsi->bsi_op) > SLAP_CONTROL_IGNORED ) {
1556 unsigned long lowid = 0;
1558 /* Pick up the previous ldap_entries.id if the previous page ended in this objectClass */
1559 if ( bsi->bsi_oc->bom_id == PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie ) )
1561 lowid = PAGECOOKIE_TO_SQL_ID( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie );
1564 if ( lowid ) {
1565 char lowidstring[48];
1566 int lowidlen;
1568 lowidlen = snprintf( lowidstring, sizeof( lowidstring ),
1569 " AND ldap_entries.id>%lu", lowid );
1570 backsql_strfcat_x( &bsi->bsi_join_where,
1571 bsi->bsi_op->o_tmpmemctx,
1572 "l",
1573 (ber_len_t)lowidlen,
1574 lowidstring );
1578 rc = backsql_process_filter( bsi, bsi->bsi_filter );
1579 if ( rc > 0 ) {
1580 struct berbuf bb = BB_NULL;
1582 backsql_strfcat_x( &bb,
1583 bsi->bsi_op->o_tmpmemctx,
1584 "bbblb",
1585 &bsi->bsi_sel.bb_val,
1586 &bsi->bsi_from.bb_val,
1587 &bsi->bsi_join_where.bb_val,
1588 (ber_len_t)STRLENOF( " AND " ), " AND ",
1589 &bsi->bsi_flt_where.bb_val );
1591 *query = bb.bb_val;
1593 } else if ( rc < 0 ) {
1595 * Indicates that there's no possible way the filter matches
1596 * anything. No need to issue the query
1598 free( query->bv_val );
1599 BER_BVZERO( query );
1602 bsi->bsi_op->o_tmpfree( bsi->bsi_sel.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1603 BER_BVZERO( &bsi->bsi_sel.bb_val );
1604 bsi->bsi_sel.bb_len = 0;
1605 bsi->bsi_op->o_tmpfree( bsi->bsi_from.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1606 BER_BVZERO( &bsi->bsi_from.bb_val );
1607 bsi->bsi_from.bb_len = 0;
1608 bsi->bsi_op->o_tmpfree( bsi->bsi_join_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1609 BER_BVZERO( &bsi->bsi_join_where.bb_val );
1610 bsi->bsi_join_where.bb_len = 0;
1611 bsi->bsi_op->o_tmpfree( bsi->bsi_flt_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1612 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1613 bsi->bsi_flt_where.bb_len = 0;
1615 Debug( LDAP_DEBUG_TRACE, "<==backsql_srch_query() returns %s\n",
1616 query->bv_val ? query->bv_val : "NULL", 0, 0 );
1618 return ( rc <= 0 ? 1 : 0 );
1621 static int
1622 backsql_oc_get_candidates( void *v_oc, void *v_bsi )
1624 backsql_oc_map_rec *oc = v_oc;
1625 backsql_srch_info *bsi = v_bsi;
1626 Operation *op = bsi->bsi_op;
1627 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1628 struct berval query;
1629 SQLHSTMT sth = SQL_NULL_HSTMT;
1630 RETCODE rc;
1631 int res;
1632 BACKSQL_ROW_NTS row;
1633 int i;
1634 int j;
1635 int n_candidates = bsi->bsi_n_candidates;
1638 * + 1 because we need room for '%';
1639 * + 1 because we need room for ',' for LDAP_SCOPE_SUBORDINATE;
1640 * this makes a subtree
1641 * search for a DN BACKSQL_MAX_DN_LEN long legal
1642 * if it returns that DN only
1644 char tmp_base_ndn[ BACKSQL_MAX_DN_LEN + 1 + 1 ];
1646 bsi->bsi_status = LDAP_SUCCESS;
1648 Debug( LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc=\"%s\"\n",
1649 BACKSQL_OC_NAME( oc ), 0, 0 );
1651 /* check for abandon */
1652 if ( op->o_abandon ) {
1653 bsi->bsi_status = SLAPD_ABANDON;
1654 return BACKSQL_AVL_STOP;
1657 /* If paged results have already completed this objectClass, skip it */
1658 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
1659 if ( oc->bom_id < PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)op->o_pagedresults_state)->ps_cookie ) )
1661 return BACKSQL_AVL_CONTINUE;
1665 if ( bsi->bsi_n_candidates == -1 ) {
1666 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1667 "unchecked limit has been overcome\n", 0, 0, 0 );
1668 /* should never get here */
1669 assert( 0 );
1670 bsi->bsi_status = LDAP_ADMINLIMIT_EXCEEDED;
1671 return BACKSQL_AVL_STOP;
1674 bsi->bsi_oc = oc;
1675 res = backsql_srch_query( bsi, &query );
1676 if ( res ) {
1677 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1678 "error while constructing query for objectclass \"%s\"\n",
1679 oc->bom_oc->soc_cname.bv_val, 0, 0 );
1681 * FIXME: need to separate errors from legally
1682 * impossible filters
1684 switch ( bsi->bsi_status ) {
1685 case LDAP_SUCCESS:
1686 case LDAP_UNDEFINED_TYPE:
1687 case LDAP_NO_SUCH_OBJECT:
1688 /* we are conservative... */
1689 default:
1690 bsi->bsi_status = LDAP_SUCCESS;
1691 /* try next */
1692 return BACKSQL_AVL_CONTINUE;
1694 case LDAP_ADMINLIMIT_EXCEEDED:
1695 case LDAP_OTHER:
1696 /* don't try any more */
1697 return BACKSQL_AVL_STOP;
1701 if ( BER_BVISNULL( &query ) ) {
1702 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1703 "could not construct query for objectclass \"%s\"\n",
1704 oc->bom_oc->soc_cname.bv_val, 0, 0 );
1705 bsi->bsi_status = LDAP_SUCCESS;
1706 return BACKSQL_AVL_CONTINUE;
1709 Debug( LDAP_DEBUG_TRACE, "Constructed query: %s\n",
1710 query.bv_val, 0, 0 );
1712 rc = backsql_Prepare( bsi->bsi_dbh, &sth, query.bv_val, 0 );
1713 bsi->bsi_op->o_tmpfree( query.bv_val, bsi->bsi_op->o_tmpmemctx );
1714 BER_BVZERO( &query );
1715 if ( rc != SQL_SUCCESS ) {
1716 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1717 "error preparing query\n", 0, 0, 0 );
1718 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1719 bsi->bsi_status = LDAP_OTHER;
1720 return BACKSQL_AVL_CONTINUE;
1723 Debug( LDAP_DEBUG_TRACE, "id: '%ld'\n", bsi->bsi_oc->bom_id, 0, 0 );
1725 rc = backsql_BindParamInt( sth, 1, SQL_PARAM_INPUT,
1726 &bsi->bsi_oc->bom_id );
1727 if ( rc != SQL_SUCCESS ) {
1728 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1729 "error binding objectclass id parameter\n", 0, 0, 0 );
1730 bsi->bsi_status = LDAP_OTHER;
1731 return BACKSQL_AVL_CONTINUE;
1734 switch ( bsi->bsi_scope ) {
1735 case LDAP_SCOPE_BASE:
1736 case BACKSQL_SCOPE_BASE_LIKE:
1738 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1739 * however this should be handled earlier
1741 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1742 bsi->bsi_status = LDAP_OTHER;
1743 return BACKSQL_AVL_CONTINUE;
1746 AC_MEMCPY( tmp_base_ndn, bsi->bsi_base_ndn->bv_val,
1747 bsi->bsi_base_ndn->bv_len + 1 );
1749 /* uppercase DN only if the stored DN can be uppercased
1750 * for comparison */
1751 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1752 ldap_pvt_str2upper( tmp_base_ndn );
1755 Debug( LDAP_DEBUG_TRACE, "(base)dn: \"%s\"\n",
1756 tmp_base_ndn, 0, 0 );
1758 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1759 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1760 if ( rc != SQL_SUCCESS ) {
1761 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1762 "error binding base_ndn parameter\n", 0, 0, 0 );
1763 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh,
1764 sth, rc );
1765 bsi->bsi_status = LDAP_OTHER;
1766 return BACKSQL_AVL_CONTINUE;
1768 break;
1770 case LDAP_SCOPE_SUBORDINATE:
1771 case LDAP_SCOPE_SUBTREE:
1773 /* if short-cutting the search base,
1774 * don't bind any parameter */
1775 if ( bsi->bsi_use_subtree_shortcut ) {
1776 break;
1780 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1781 * however this should be handled earlier
1783 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1784 bsi->bsi_status = LDAP_OTHER;
1785 return BACKSQL_AVL_CONTINUE;
1789 * Sets the parameters for the SQL built earlier
1790 * NOTE that all the databases could actually use
1791 * the TimesTen version, which would be cleaner
1792 * and would also eliminate the need for the
1793 * subtree_cond line in the configuration file.
1794 * For now, I'm leaving it the way it is,
1795 * so non-TimesTen databases use the original code.
1796 * But at some point this should get cleaned up.
1798 * If "dn" is being used, do a suffix search.
1799 * If "dn_ru" is being used, do a prefix search.
1801 if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
1802 tmp_base_ndn[ 0 ] = '\0';
1804 for ( i = 0, j = bsi->bsi_base_ndn->bv_len - 1;
1805 j >= 0; i++, j--) {
1806 tmp_base_ndn[ i ] = bsi->bsi_base_ndn->bv_val[ j ];
1809 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1810 tmp_base_ndn[ i++ ] = ',';
1813 tmp_base_ndn[ i ] = '%';
1814 tmp_base_ndn[ i + 1 ] = '\0';
1816 } else {
1817 i = 0;
1819 tmp_base_ndn[ i++ ] = '%';
1821 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1822 tmp_base_ndn[ i++ ] = ',';
1825 AC_MEMCPY( &tmp_base_ndn[ i ], bsi->bsi_base_ndn->bv_val,
1826 bsi->bsi_base_ndn->bv_len + 1 );
1829 /* uppercase DN only if the stored DN can be uppercased
1830 * for comparison */
1831 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1832 ldap_pvt_str2upper( tmp_base_ndn );
1835 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1836 Debug( LDAP_DEBUG_TRACE, "(children)dn: \"%s\"\n",
1837 tmp_base_ndn, 0, 0 );
1838 } else {
1839 Debug( LDAP_DEBUG_TRACE, "(sub)dn: \"%s\"\n",
1840 tmp_base_ndn, 0, 0 );
1843 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1844 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1845 if ( rc != SQL_SUCCESS ) {
1846 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1847 "error binding base_ndn parameter (2)\n",
1848 0, 0, 0 );
1849 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh,
1850 sth, rc );
1851 bsi->bsi_status = LDAP_OTHER;
1852 return BACKSQL_AVL_CONTINUE;
1854 break;
1857 case LDAP_SCOPE_ONELEVEL:
1858 assert( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) );
1860 #ifdef BACKSQL_ARBITRARY_KEY
1861 Debug( LDAP_DEBUG_TRACE, "(one)id: \"%s\"\n",
1862 bsi->bsi_base_id.eid_id.bv_val, 0, 0 );
1863 #else /* ! BACKSQL_ARBITRARY_KEY */
1864 Debug( LDAP_DEBUG_TRACE, "(one)id: '%lu'\n",
1865 bsi->bsi_base_id.eid_id, 0, 0 );
1866 #endif /* ! BACKSQL_ARBITRARY_KEY */
1867 rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT,
1868 &bsi->bsi_base_id.eid_id );
1869 if ( rc != SQL_SUCCESS ) {
1870 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1871 "error binding base id parameter\n", 0, 0, 0 );
1872 bsi->bsi_status = LDAP_OTHER;
1873 return BACKSQL_AVL_CONTINUE;
1875 break;
1878 rc = SQLExecute( sth );
1879 if ( !BACKSQL_SUCCESS( rc ) ) {
1880 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1881 "error executing query\n", 0, 0, 0 );
1882 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1883 SQLFreeStmt( sth, SQL_DROP );
1884 bsi->bsi_status = LDAP_OTHER;
1885 return BACKSQL_AVL_CONTINUE;
1888 backsql_BindRowAsStrings_x( sth, &row, bsi->bsi_op->o_tmpmemctx );
1889 rc = SQLFetch( sth );
1890 for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
1891 struct berval dn, pdn, ndn;
1892 backsql_entryID *c_id = NULL;
1893 int ret;
1895 ber_str2bv( row.cols[ 3 ], 0, 0, &dn );
1897 if ( backsql_api_odbc2dn( bsi->bsi_op, bsi->bsi_rs, &dn ) ) {
1898 continue;
1901 ret = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
1902 if ( dn.bv_val != row.cols[ 3 ] ) {
1903 free( dn.bv_val );
1906 if ( ret != LDAP_SUCCESS ) {
1907 continue;
1910 if ( bi->sql_baseObject && dn_match( &ndn, &bi->sql_baseObject->e_nname ) ) {
1911 goto cleanup;
1914 c_id = (backsql_entryID *)op->o_tmpcalloc( 1,
1915 sizeof( backsql_entryID ), op->o_tmpmemctx );
1916 #ifdef BACKSQL_ARBITRARY_KEY
1917 ber_str2bv_x( row.cols[ 0 ], 0, 1, &c_id->eid_id,
1918 op->o_tmpmemctx );
1919 ber_str2bv_x( row.cols[ 1 ], 0, 1, &c_id->eid_keyval,
1920 op->o_tmpmemctx );
1921 #else /* ! BACKSQL_ARBITRARY_KEY */
1922 if ( lutil_atoulx( &c_id->eid_id, row.cols[ 0 ], 0 ) != 0 ) {
1923 goto cleanup;
1925 if ( lutil_atoulx( &c_id->eid_keyval, row.cols[ 1 ], 0 ) != 0 ) {
1926 goto cleanup;
1928 #endif /* ! BACKSQL_ARBITRARY_KEY */
1929 c_id->eid_oc = bsi->bsi_oc;
1930 c_id->eid_oc_id = bsi->bsi_oc->bom_id;
1932 c_id->eid_dn = pdn;
1933 c_id->eid_ndn = ndn;
1935 /* append at end of list ... */
1936 c_id->eid_next = NULL;
1937 *bsi->bsi_id_listtail = c_id;
1938 bsi->bsi_id_listtail = &c_id->eid_next;
1940 #ifdef BACKSQL_ARBITRARY_KEY
1941 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1942 "added entry id=%s, keyval=%s dn=\"%s\"\n",
1943 c_id->eid_id.bv_val, c_id->eid_keyval.bv_val,
1944 row.cols[ 3 ] );
1945 #else /* ! BACKSQL_ARBITRARY_KEY */
1946 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1947 "added entry id=%ld, keyval=%ld dn=\"%s\"\n",
1948 c_id->eid_id, c_id->eid_keyval, row.cols[ 3 ] );
1949 #endif /* ! BACKSQL_ARBITRARY_KEY */
1951 /* count candidates, for unchecked limit */
1952 bsi->bsi_n_candidates--;
1953 if ( bsi->bsi_n_candidates == -1 ) {
1954 break;
1956 continue;
1958 cleanup:;
1959 if ( !BER_BVISNULL( &pdn ) ) {
1960 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
1962 if ( !BER_BVISNULL( &ndn ) ) {
1963 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
1965 if ( c_id != NULL ) {
1966 ch_free( c_id );
1969 backsql_FreeRow_x( &row, bsi->bsi_op->o_tmpmemctx );
1970 SQLFreeStmt( sth, SQL_DROP );
1972 Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates(): %d\n",
1973 n_candidates - bsi->bsi_n_candidates, 0, 0 );
1975 return ( bsi->bsi_n_candidates == -1 ? BACKSQL_AVL_STOP : BACKSQL_AVL_CONTINUE );
1979 backsql_search( Operation *op, SlapReply *rs )
1981 backsql_info *bi = (backsql_info *)op->o_bd->be_private;
1982 SQLHDBC dbh = SQL_NULL_HDBC;
1983 int sres;
1984 Entry user_entry = { 0 },
1985 base_entry = { 0 };
1986 int manageDSAit = get_manageDSAit( op );
1987 time_t stoptime = 0;
1988 backsql_srch_info bsi = { 0 };
1989 backsql_entryID *eid = NULL;
1990 struct berval nbase = BER_BVNULL;
1991 unsigned long lastid = 0;
1993 Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): "
1994 "base=\"%s\", filter=\"%s\", scope=%d,",
1995 op->o_req_ndn.bv_val,
1996 op->ors_filterstr.bv_val,
1997 op->ors_scope );
1998 Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, "
1999 "attributes to load: %s\n",
2000 op->ors_deref,
2001 op->ors_attrsonly,
2002 op->ors_attrs == NULL ? "all" : "custom list" );
2004 if ( op->o_req_ndn.bv_len > BACKSQL_MAX_DN_LEN ) {
2005 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2006 "search base length (%ld) exceeds max length (%d)\n",
2007 op->o_req_ndn.bv_len, BACKSQL_MAX_DN_LEN, 0 );
2009 * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate
2010 * since it is impossible that such a long DN exists
2011 * in the backend
2013 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
2014 send_ldap_result( op, rs );
2015 return 1;
2018 sres = backsql_get_db_conn( op, &dbh );
2019 if ( sres != LDAP_SUCCESS ) {
2020 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2021 "could not get connection handle - exiting\n",
2022 0, 0, 0 );
2023 rs->sr_err = sres;
2024 rs->sr_text = sres == LDAP_OTHER ? "SQL-backend error" : NULL;
2025 send_ldap_result( op, rs );
2026 return 1;
2029 /* compute it anyway; root does not use it */
2030 stoptime = op->o_time + op->ors_tlimit;
2032 /* init search */
2033 bsi.bsi_e = &base_entry;
2034 rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn,
2035 op->ors_scope,
2036 stoptime, op->ors_filter,
2037 dbh, op, rs, op->ors_attrs,
2038 ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) );
2039 switch ( rs->sr_err ) {
2040 case LDAP_SUCCESS:
2041 break;
2043 case LDAP_REFERRAL:
2044 if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
2045 dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) )
2047 rs->sr_err = LDAP_SUCCESS;
2048 rs->sr_text = NULL;
2049 rs->sr_matched = NULL;
2050 if ( rs->sr_ref ) {
2051 ber_bvarray_free( rs->sr_ref );
2052 rs->sr_ref = NULL;
2054 break;
2057 /* an entry was created; free it */
2058 entry_clean( bsi.bsi_e );
2060 /* fall thru */
2062 default:
2063 if ( !BER_BVISNULL( &base_entry.e_nname )
2064 && !access_allowed( op, &base_entry,
2065 slap_schema.si_ad_entry, NULL,
2066 ACL_DISCLOSE, NULL ) )
2068 rs->sr_err = LDAP_NO_SUCH_OBJECT;
2069 if ( rs->sr_ref ) {
2070 ber_bvarray_free( rs->sr_ref );
2071 rs->sr_ref = NULL;
2073 rs->sr_matched = NULL;
2074 rs->sr_text = NULL;
2077 send_ldap_result( op, rs );
2079 if ( rs->sr_ref ) {
2080 ber_bvarray_free( rs->sr_ref );
2081 rs->sr_ref = NULL;
2084 if ( !BER_BVISNULL( &base_entry.e_nname ) ) {
2085 entry_clean( &base_entry );
2088 goto done;
2090 /* NOTE: __NEW__ "search" access is required
2091 * on searchBase object */
2093 slap_mask_t mask;
2095 if ( get_assert( op ) &&
2096 ( test_filter( op, &base_entry, get_assertion( op ) )
2097 != LDAP_COMPARE_TRUE ) )
2099 rs->sr_err = LDAP_ASSERTION_FAILED;
2102 if ( ! access_allowed_mask( op, &base_entry,
2103 slap_schema.si_ad_entry,
2104 NULL, ACL_SEARCH, NULL, &mask ) )
2106 if ( rs->sr_err == LDAP_SUCCESS ) {
2107 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
2111 if ( rs->sr_err != LDAP_SUCCESS ) {
2112 if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
2113 rs->sr_err = LDAP_NO_SUCH_OBJECT;
2114 rs->sr_text = NULL;
2116 send_ldap_result( op, rs );
2117 goto done;
2121 bsi.bsi_e = NULL;
2123 bsi.bsi_n_candidates =
2124 ( op->ors_limit == NULL /* isroot == TRUE */ ? -2 :
2125 ( op->ors_limit->lms_s_unchecked == -1 ? -2 :
2126 ( op->ors_limit->lms_s_unchecked ) ) );
2128 /* If paged results are in effect, check the paging cookie */
2129 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
2130 rs->sr_err = parse_paged_cookie( op, rs );
2131 if ( rs->sr_err != LDAP_SUCCESS ) {
2132 send_ldap_result( op, rs );
2133 goto done;
2137 switch ( bsi.bsi_scope ) {
2138 case LDAP_SCOPE_BASE:
2139 case BACKSQL_SCOPE_BASE_LIKE:
2141 * probably already found...
2143 bsi.bsi_id_list = &bsi.bsi_base_id;
2144 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
2145 break;
2147 case LDAP_SCOPE_SUBTREE:
2149 * if baseObject is defined, and if it is the root
2150 * of the search, add it to the candidate list
2152 if ( bi->sql_baseObject && BACKSQL_IS_BASEOBJECT_ID( &bsi.bsi_base_id.eid_id ) )
2154 bsi.bsi_id_list = &bsi.bsi_base_id;
2155 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
2158 /* FALLTHRU */
2159 default:
2162 * for each objectclass we try to construct query which gets IDs
2163 * of entries matching LDAP query filter and scope (or at least
2164 * candidates), and get the IDs. Do this in ID order for paging.
2166 avl_apply( bi->sql_oc_by_id, backsql_oc_get_candidates,
2167 &bsi, BACKSQL_AVL_STOP, AVL_INORDER );
2169 /* check for abandon */
2170 if ( op->o_abandon ) {
2171 eid = bsi.bsi_id_list;
2172 rs->sr_err = SLAPD_ABANDON;
2173 goto send_results;
2177 if ( op->ors_limit != NULL /* isroot == FALSE */
2178 && op->ors_limit->lms_s_unchecked != -1
2179 && bsi.bsi_n_candidates == -1 )
2181 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
2182 send_ldap_result( op, rs );
2183 goto done;
2187 * now we load candidate entries (only those attributes
2188 * mentioned in attrs and filter), test it against full filter
2189 * and then send to client; don't free entry_id if baseObject...
2191 for ( eid = bsi.bsi_id_list;
2192 eid != NULL;
2193 eid = backsql_free_entryID(
2194 eid, eid == &bsi.bsi_base_id ? 0 : 1, op->o_tmpmemctx ) )
2196 int rc;
2197 Attribute *a_hasSubordinate = NULL,
2198 *a_entryUUID = NULL,
2199 *a_entryCSN = NULL,
2200 **ap = NULL;
2201 Entry *e = NULL;
2203 /* check for abandon */
2204 if ( op->o_abandon ) {
2205 rs->sr_err = SLAPD_ABANDON;
2206 goto send_results;
2209 /* check time limit */
2210 if ( op->ors_tlimit != SLAP_NO_LIMIT
2211 && slap_get_time() > stoptime )
2213 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
2214 rs->sr_ctrls = NULL;
2215 rs->sr_ref = rs->sr_v2ref;
2216 goto send_results;
2219 #ifdef BACKSQL_ARBITRARY_KEY
2220 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
2221 "for entry id=%s, oc_id=%ld, keyval=%s\n",
2222 eid->eid_id.bv_val, eid->eid_oc_id,
2223 eid->eid_keyval.bv_val );
2224 #else /* ! BACKSQL_ARBITRARY_KEY */
2225 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
2226 "for entry id=%ld, oc_id=%ld, keyval=%ld\n",
2227 eid->eid_id, eid->eid_oc_id, eid->eid_keyval );
2228 #endif /* ! BACKSQL_ARBITRARY_KEY */
2230 /* check scope */
2231 switch ( op->ors_scope ) {
2232 case LDAP_SCOPE_BASE:
2233 case BACKSQL_SCOPE_BASE_LIKE:
2234 if ( !dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
2235 goto next_entry2;
2237 break;
2239 case LDAP_SCOPE_ONE:
2241 struct berval rdn = eid->eid_ndn;
2243 rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
2244 if ( !dnIsOneLevelRDN( &rdn ) ) {
2245 goto next_entry2;
2247 /* fall thru */
2250 case LDAP_SCOPE_SUBORDINATE:
2251 /* discard the baseObject entry */
2252 if ( dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
2253 goto next_entry2;
2255 /* FALLTHRU */
2256 case LDAP_SCOPE_SUBTREE:
2257 /* FIXME: this should never fail... */
2258 if ( !dnIsSuffix( &eid->eid_ndn, &op->o_req_ndn ) ) {
2259 goto next_entry2;
2261 break;
2264 if ( BACKSQL_IS_BASEOBJECT_ID( &eid->eid_id ) ) {
2265 /* don't recollect baseObject... */
2266 e = bi->sql_baseObject;
2268 } else if ( eid == &bsi.bsi_base_id ) {
2269 /* don't recollect searchBase object... */
2270 e = &base_entry;
2272 } else {
2273 bsi.bsi_e = &user_entry;
2274 rc = backsql_id2entry( &bsi, eid );
2275 if ( rc != LDAP_SUCCESS ) {
2276 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2277 "error %d in backsql_id2entry() "
2278 "- skipping\n", rc, 0, 0 );
2279 continue;
2281 e = &user_entry;
2284 if ( !manageDSAit &&
2285 op->ors_scope != LDAP_SCOPE_BASE &&
2286 op->ors_scope != BACKSQL_SCOPE_BASE_LIKE &&
2287 is_entry_referral( e ) )
2289 BerVarray refs;
2291 refs = get_entry_referrals( op, e );
2292 if ( !refs ) {
2293 backsql_srch_info bsi2 = { 0 };
2294 Entry user_entry2 = { 0 };
2296 /* retry with the full entry... */
2297 bsi2.bsi_e = &user_entry2;
2298 rc = backsql_init_search( &bsi2,
2299 &e->e_nname,
2300 LDAP_SCOPE_BASE,
2301 (time_t)(-1), NULL,
2302 dbh, op, rs, NULL,
2303 BACKSQL_ISF_GET_ENTRY );
2304 if ( rc == LDAP_SUCCESS ) {
2305 if ( is_entry_referral( &user_entry2 ) )
2307 refs = get_entry_referrals( op,
2308 &user_entry2 );
2309 } else {
2310 rs->sr_err = LDAP_OTHER;
2312 backsql_entry_clean( op, &user_entry2 );
2314 if ( bsi2.bsi_attrs != NULL ) {
2315 op->o_tmpfree( bsi2.bsi_attrs,
2316 op->o_tmpmemctx );
2320 if ( refs ) {
2321 rs->sr_ref = referral_rewrite( refs,
2322 &e->e_name,
2323 &op->o_req_dn,
2324 op->ors_scope );
2325 ber_bvarray_free( refs );
2328 if ( rs->sr_ref ) {
2329 rs->sr_err = LDAP_REFERRAL;
2331 } else {
2332 rs->sr_text = "bad referral object";
2335 rs->sr_entry = e;
2336 rs->sr_matched = user_entry.e_name.bv_val;
2337 send_search_reference( op, rs );
2339 ber_bvarray_free( rs->sr_ref );
2340 rs->sr_ref = NULL;
2341 rs->sr_matched = NULL;
2342 rs->sr_entry = NULL;
2343 if ( rs->sr_err == LDAP_REFERRAL ) {
2344 rs->sr_err = LDAP_SUCCESS;
2347 goto next_entry;
2351 * We use this flag since we need to parse the filter
2352 * anyway; we should have used the frontend API function
2353 * filter_has_subordinates()
2355 if ( bsi.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) {
2356 rc = backsql_has_children( op, dbh, &e->e_nname );
2358 switch ( rc ) {
2359 case LDAP_COMPARE_TRUE:
2360 case LDAP_COMPARE_FALSE:
2361 a_hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE );
2362 if ( a_hasSubordinate != NULL ) {
2363 for ( ap = &user_entry.e_attrs;
2364 *ap;
2365 ap = &(*ap)->a_next );
2367 *ap = a_hasSubordinate;
2369 rc = 0;
2370 break;
2372 default:
2373 Debug(LDAP_DEBUG_TRACE,
2374 "backsql_search(): "
2375 "has_children failed( %d)\n",
2376 rc, 0, 0 );
2377 rc = 1;
2378 goto next_entry;
2382 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYUUID ) {
2383 a_entryUUID = backsql_operational_entryUUID( bi, eid );
2384 if ( a_entryUUID != NULL ) {
2385 if ( ap == NULL ) {
2386 ap = &user_entry.e_attrs;
2389 for ( ; *ap; ap = &(*ap)->a_next );
2391 *ap = a_entryUUID;
2395 #ifdef BACKSQL_SYNCPROV
2396 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYCSN ) {
2397 a_entryCSN = backsql_operational_entryCSN( op );
2398 if ( a_entryCSN != NULL ) {
2399 if ( ap == NULL ) {
2400 ap = &user_entry.e_attrs;
2403 for ( ; *ap; ap = &(*ap)->a_next );
2405 *ap = a_entryCSN;
2408 #endif /* BACKSQL_SYNCPROV */
2410 if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE )
2412 /* If paged results are in effect, see if the page limit was exceeded */
2413 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
2414 if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size )
2416 e = NULL;
2417 send_paged_response( op, rs, &lastid );
2418 goto done;
2420 lastid = SQL_TO_PAGECOOKIE( eid->eid_id, eid->eid_oc_id );
2422 rs->sr_attrs = op->ors_attrs;
2423 rs->sr_operational_attrs = NULL;
2424 rs->sr_entry = e;
2425 e->e_private = (void *)eid;
2426 rs->sr_flags = ( e == &user_entry ) ? REP_ENTRY_MODIFIABLE : 0;
2427 /* FIXME: need the whole entry (ITS#3480) */
2428 rs->sr_err = send_search_entry( op, rs );
2429 e->e_private = NULL;
2430 rs->sr_entry = NULL;
2431 rs->sr_attrs = NULL;
2432 rs->sr_operational_attrs = NULL;
2434 switch ( rs->sr_err ) {
2435 case LDAP_UNAVAILABLE:
2437 * FIXME: send_search_entry failed;
2438 * better stop
2440 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2441 "connection lost\n", 0, 0, 0 );
2442 goto end_of_search;
2444 case LDAP_SIZELIMIT_EXCEEDED:
2445 goto send_results;
2449 next_entry:;
2450 if ( e == &user_entry ) {
2451 backsql_entry_clean( op, &user_entry );
2454 next_entry2:;
2457 end_of_search:;
2458 if ( rs->sr_nentries > 0 ) {
2459 rs->sr_ref = rs->sr_v2ref;
2460 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
2461 : LDAP_REFERRAL;
2463 } else {
2464 rs->sr_err = bsi.bsi_status;
2467 send_results:;
2468 if ( rs->sr_err != SLAPD_ABANDON ) {
2469 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
2470 send_paged_response( op, rs, NULL );
2471 } else {
2472 send_ldap_result( op, rs );
2476 /* cleanup in case of abandon */
2477 for ( ; eid != NULL;
2478 eid = backsql_free_entryID(
2479 eid, eid == &bsi.bsi_base_id ? 0 : 1, op->o_tmpmemctx ) )
2482 backsql_entry_clean( op, &base_entry );
2484 /* in case we got here accidentally */
2485 backsql_entry_clean( op, &user_entry );
2487 if ( rs->sr_v2ref ) {
2488 ber_bvarray_free( rs->sr_v2ref );
2489 rs->sr_v2ref = NULL;
2492 #ifdef BACKSQL_SYNCPROV
2493 if ( op->o_sync ) {
2494 Operation op2 = *op;
2495 SlapReply rs2 = { 0 };
2496 Entry *e = entry_alloc();
2497 slap_callback cb = { 0 };
2499 op2.o_tag = LDAP_REQ_ADD;
2500 op2.o_bd = select_backend( &op->o_bd->be_nsuffix[0], 0 );
2501 op2.ora_e = e;
2502 op2.o_callback = &cb;
2504 ber_dupbv( &e->e_name, op->o_bd->be_suffix );
2505 ber_dupbv( &e->e_nname, op->o_bd->be_nsuffix );
2507 cb.sc_response = slap_null_cb;
2509 op2.o_bd->be_add( &op2, &rs2 );
2511 if ( op2.ora_e == e )
2512 entry_free( e );
2514 #endif /* BACKSQL_SYNCPROV */
2516 done:;
2517 (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
2519 if ( bsi.bsi_attrs != NULL ) {
2520 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
2523 if ( !BER_BVISNULL( &nbase )
2524 && nbase.bv_val != op->o_req_ndn.bv_val )
2526 ch_free( nbase.bv_val );
2529 /* restore scope ... FIXME: this should be done before ANY
2530 * frontend call that uses op */
2531 if ( op->ors_scope == BACKSQL_SCOPE_BASE_LIKE ) {
2532 op->ors_scope = LDAP_SCOPE_BASE;
2535 Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n", 0, 0, 0 );
2537 return rs->sr_err;
2540 /* return LDAP_SUCCESS IFF we can retrieve the specified entry.
2543 backsql_entry_get(
2544 Operation *op,
2545 struct berval *ndn,
2546 ObjectClass *oc,
2547 AttributeDescription *at,
2548 int rw,
2549 Entry **ent )
2551 backsql_srch_info bsi = { 0 };
2552 SQLHDBC dbh = SQL_NULL_HDBC;
2553 int rc;
2554 SlapReply rs = { 0 };
2555 AttributeName anlist[ 2 ];
2557 *ent = NULL;
2559 rc = backsql_get_db_conn( op, &dbh );
2560 if ( rc != LDAP_SUCCESS ) {
2561 return rc;
2564 if ( at ) {
2565 anlist[ 0 ].an_name = at->ad_cname;
2566 anlist[ 0 ].an_desc = at;
2567 BER_BVZERO( &anlist[ 1 ].an_name );
2570 bsi.bsi_e = entry_alloc();
2571 rc = backsql_init_search( &bsi,
2572 ndn,
2573 LDAP_SCOPE_BASE,
2574 (time_t)(-1), NULL,
2575 dbh, op, &rs, at ? anlist : NULL,
2576 BACKSQL_ISF_GET_ENTRY );
2578 if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
2579 (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
2582 if ( rc == LDAP_SUCCESS ) {
2584 #if 0 /* not supported at present */
2585 /* find attribute values */
2586 if ( is_entry_alias( bsi.bsi_e ) ) {
2587 Debug( LDAP_DEBUG_ACL,
2588 "<= backsql_entry_get: entry is an alias\n",
2589 0, 0, 0 );
2590 rc = LDAP_ALIAS_PROBLEM;
2591 goto return_results;
2593 #endif
2595 if ( is_entry_referral( bsi.bsi_e ) ) {
2596 Debug( LDAP_DEBUG_ACL,
2597 "<= backsql_entry_get: entry is a referral\n",
2598 0, 0, 0 );
2599 rc = LDAP_REFERRAL;
2600 goto return_results;
2603 if ( oc && !is_entry_objectclass( bsi.bsi_e, oc, 0 ) ) {
2604 Debug( LDAP_DEBUG_ACL,
2605 "<= backsql_entry_get: "
2606 "failed to find objectClass\n",
2607 0, 0, 0 );
2608 rc = LDAP_NO_SUCH_ATTRIBUTE;
2609 goto return_results;
2612 *ent = bsi.bsi_e;
2615 return_results:;
2616 if ( bsi.bsi_attrs != NULL ) {
2617 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
2620 if ( rc != LDAP_SUCCESS ) {
2621 if ( bsi.bsi_e ) {
2622 entry_free( bsi.bsi_e );
2626 return rc;
2629 void
2630 backsql_entry_clean(
2631 Operation *op,
2632 Entry *e )
2634 void *ctx;
2636 ctx = ldap_pvt_thread_pool_context();
2638 if ( ctx == NULL || ctx != op->o_tmpmemctx ) {
2639 if ( !BER_BVISNULL( &e->e_name ) ) {
2640 op->o_tmpfree( e->e_name.bv_val, op->o_tmpmemctx );
2641 BER_BVZERO( &e->e_name );
2644 if ( !BER_BVISNULL( &e->e_nname ) ) {
2645 op->o_tmpfree( e->e_nname.bv_val, op->o_tmpmemctx );
2646 BER_BVZERO( &e->e_nname );
2650 entry_clean( e );
2654 backsql_entry_release(
2655 Operation *op,
2656 Entry *e,
2657 int rw )
2659 backsql_entry_clean( op, e );
2661 entry_free( e );
2663 return 0;
2667 /* This function is copied verbatim from back-bdb/search.c */
2668 static int
2669 parse_paged_cookie( Operation *op, SlapReply *rs )
2671 int rc = LDAP_SUCCESS;
2672 PagedResultsState *ps = op->o_pagedresults_state;
2674 /* this function must be invoked only if the pagedResults
2675 * control has been detected, parsed and partially checked
2676 * by the frontend */
2677 assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
2679 /* cookie decoding/checks deferred to backend... */
2680 if ( ps->ps_cookieval.bv_len ) {
2681 PagedResultsCookie reqcookie;
2682 if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) {
2683 /* bad cookie */
2684 rs->sr_text = "paged results cookie is invalid";
2685 rc = LDAP_PROTOCOL_ERROR;
2686 goto done;
2689 AC_MEMCPY( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie ));
2691 if ( reqcookie > ps->ps_cookie ) {
2692 /* bad cookie */
2693 rs->sr_text = "paged results cookie is invalid";
2694 rc = LDAP_PROTOCOL_ERROR;
2695 goto done;
2697 } else if ( reqcookie < ps->ps_cookie ) {
2698 rs->sr_text = "paged results cookie is invalid or old";
2699 rc = LDAP_UNWILLING_TO_PERFORM;
2700 goto done;
2703 } else {
2704 /* Initial request. Initialize state. */
2705 ps->ps_cookie = 0;
2706 ps->ps_count = 0;
2709 done:;
2711 return rc;
2714 /* This function is copied nearly verbatim from back-bdb/search.c */
2715 static void
2716 send_paged_response(
2717 Operation *op,
2718 SlapReply *rs,
2719 unsigned long *lastid )
2721 LDAPControl ctrl, *ctrls[2];
2722 BerElementBuffer berbuf;
2723 BerElement *ber = (BerElement *)&berbuf;
2724 PagedResultsCookie respcookie;
2725 struct berval cookie;
2727 Debug(LDAP_DEBUG_ARGS,
2728 "send_paged_response: lastid=0x%08lx nentries=%d\n",
2729 lastid ? *lastid : 0, rs->sr_nentries, NULL );
2731 BER_BVZERO( &ctrl.ldctl_value );
2732 ctrls[0] = &ctrl;
2733 ctrls[1] = NULL;
2735 ber_init2( ber, NULL, LBER_USE_DER );
2737 if ( lastid ) {
2738 respcookie = ( PagedResultsCookie )(*lastid);
2739 cookie.bv_len = sizeof( respcookie );
2740 cookie.bv_val = (char *)&respcookie;
2742 } else {
2743 respcookie = ( PagedResultsCookie )0;
2744 BER_BVSTR( &cookie, "" );
2747 op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
2748 op->o_conn->c_pagedresults_state.ps_count =
2749 ((PagedResultsState *)op->o_pagedresults_state)->ps_count +
2750 rs->sr_nentries;
2752 /* return size of 0 -- no estimate */
2753 ber_printf( ber, "{iO}", 0, &cookie );
2755 if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
2756 goto done;
2759 ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
2760 ctrls[0]->ldctl_iscritical = 0;
2762 rs->sr_ctrls = ctrls;
2763 rs->sr_err = LDAP_SUCCESS;
2764 send_ldap_result( op, rs );
2765 rs->sr_ctrls = NULL;
2767 done:
2768 (void) ber_free_buf( ber );