1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3 *
4 * Copyright 1999-2021 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.
9 *
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.
13 *
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>.
17 */
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.
22 */
23
24 #include "portable.h"
25
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include "ac/string.h"
29 #include "ac/ctype.h"
30
31 #include "lutil.h"
32 #include "slap.h"
33 #include "proto-sql.h"
34
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 );
44
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
52 /* NOTE: not supported when BACKSQL_ARBITRARY_KEY is defined */
53 #ifndef BACKSQL_ARBITRARY_KEY
54 #define SQL_TO_PAGECOOKIE(id, oc) (((id) << 6 ) | ((oc) & 0x3F))
55 #define PAGECOOKIE_TO_SQL_ID(pc) ((pc) >> 6)
56 #define PAGECOOKIE_TO_SQL_OC(pc) ((pc) & 0x3F)
57
58 static int parse_paged_cookie( Operation *op, SlapReply *rs );
59
60 static void send_paged_response(
61 Operation *op,
62 SlapReply *rs,
63 ID *lastid );
64 #endif /* ! BACKSQL_ARBITRARY_KEY */
65
66 static int
backsql_attrlist_add(backsql_srch_info * bsi,AttributeDescription * ad)67 backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad )
68 {
69 int n_attrs = 0;
70 AttributeName *an = NULL;
71
72 if ( bsi->bsi_attrs == NULL ) {
73 return 1;
74 }
75
76 /*
77 * clear the list (retrieve all attrs)
78 */
79 if ( ad == NULL ) {
80 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs, bsi->bsi_op->o_tmpmemctx );
81 bsi->bsi_attrs = NULL;
82 bsi->bsi_flags |= BSQL_SF_ALL_ATTRS;
83 return 1;
84 }
85
86 /* strip ';binary' */
87 if ( slap_ad_is_binary( ad ) ) {
88 ad = ad->ad_type->sat_ad;
89 }
90
91 for ( ; !BER_BVISNULL( &bsi->bsi_attrs[ n_attrs ].an_name ); n_attrs++ ) {
92 an = &bsi->bsi_attrs[ n_attrs ];
93
94 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
95 "attribute \"%s\" is in list\n",
96 an->an_name.bv_val );
97 /*
98 * We can live with strcmp because the attribute
99 * list has been normalized before calling be_search
100 */
101 if ( !BACKSQL_NCMP( &an->an_name, &ad->ad_cname ) ) {
102 return 1;
103 }
104 }
105
106 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
107 "adding \"%s\" to list\n", ad->ad_cname.bv_val );
108
109 an = (AttributeName *)bsi->bsi_op->o_tmprealloc( bsi->bsi_attrs,
110 sizeof( AttributeName ) * ( n_attrs + 2 ),
111 bsi->bsi_op->o_tmpmemctx );
112 if ( an == NULL ) {
113 return -1;
114 }
115
116 an[ n_attrs ].an_name = ad->ad_cname;
117 an[ n_attrs ].an_desc = ad;
118 BER_BVZERO( &an[ n_attrs + 1 ].an_name );
119
120 bsi->bsi_attrs = an;
121
122 return 1;
123 }
124
125 /*
126 * Initializes the search structure.
127 *
128 * If get_base_id != 0, the field bsi_base_id is filled
129 * with the entryID of bsi_base_ndn; it must be freed
130 * by backsql_free_entryID() when no longer required.
131 *
132 * NOTE: base must be normalized
133 */
134 int
backsql_init_search(backsql_srch_info * bsi,struct berval * nbase,int scope,time_t stoptime,Filter * filter,SQLHDBC dbh,Operation * op,SlapReply * rs,AttributeName * attrs,unsigned flags)135 backsql_init_search(
136 backsql_srch_info *bsi,
137 struct berval *nbase,
138 int scope,
139 time_t stoptime,
140 Filter *filter,
141 SQLHDBC dbh,
142 Operation *op,
143 SlapReply *rs,
144 AttributeName *attrs,
145 unsigned flags )
146 {
147 backsql_info *bi = (backsql_info *)op->o_bd->be_private;
148 int rc = LDAP_SUCCESS;
149
150 bsi->bsi_base_ndn = nbase;
151 bsi->bsi_use_subtree_shortcut = 0;
152 BER_BVZERO( &bsi->bsi_base_id.eid_dn );
153 BER_BVZERO( &bsi->bsi_base_id.eid_ndn );
154 bsi->bsi_scope = scope;
155 bsi->bsi_filter = filter;
156 bsi->bsi_dbh = dbh;
157 bsi->bsi_op = op;
158 bsi->bsi_rs = rs;
159 bsi->bsi_flags = BSQL_SF_NONE;
160
161 bsi->bsi_attrs = NULL;
162
163 if ( BACKSQL_FETCH_ALL_ATTRS( bi ) ) {
164 /*
165 * if requested, simply try to fetch all attributes
166 */
167 bsi->bsi_flags |= BSQL_SF_ALL_ATTRS;
168
169 } else {
170 if ( BACKSQL_FETCH_ALL_USERATTRS( bi ) ) {
171 bsi->bsi_flags |= BSQL_SF_ALL_USER;
172
173 } else if ( BACKSQL_FETCH_ALL_OPATTRS( bi ) ) {
174 bsi->bsi_flags |= BSQL_SF_ALL_OPER;
175 }
176
177 if ( attrs == NULL ) {
178 /* NULL means all user attributes */
179 bsi->bsi_flags |= BSQL_SF_ALL_USER;
180
181 } else {
182 AttributeName *p;
183 int got_oc = 0;
184
185 bsi->bsi_attrs = (AttributeName *)bsi->bsi_op->o_tmpalloc(
186 sizeof( AttributeName ),
187 bsi->bsi_op->o_tmpmemctx );
188 BER_BVZERO( &bsi->bsi_attrs[ 0 ].an_name );
189
190 for ( p = attrs; !BER_BVISNULL( &p->an_name ); p++ ) {
191 if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_user_attrs ) == 0 ) {
192 /* handle "*" */
193 bsi->bsi_flags |= BSQL_SF_ALL_USER;
194
195 /* if all attrs are requested, there's
196 * no need to continue */
197 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
198 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
199 bsi->bsi_op->o_tmpmemctx );
200 bsi->bsi_attrs = NULL;
201 break;
202 }
203 continue;
204
205 } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_operational_attrs ) == 0 ) {
206 /* handle "+" */
207 bsi->bsi_flags |= BSQL_SF_ALL_OPER;
208
209 /* if all attrs are requested, there's
210 * no need to continue */
211 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
212 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
213 bsi->bsi_op->o_tmpmemctx );
214 bsi->bsi_attrs = NULL;
215 break;
216 }
217 continue;
218
219 } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_no_attrs ) == 0 ) {
220 /* ignore "1.1" */
221 continue;
222
223 } else if ( p->an_desc == slap_schema.si_ad_objectClass ) {
224 got_oc = 1;
225 }
226
227 backsql_attrlist_add( bsi, p->an_desc );
228 }
229
230 if ( got_oc == 0 && !( bsi->bsi_flags & BSQL_SF_ALL_USER ) ) {
231 /* add objectClass if not present,
232 * because it is required to understand
233 * if an entry is a referral, an alias
234 * or so... */
235 backsql_attrlist_add( bsi, slap_schema.si_ad_objectClass );
236 }
237 }
238
239 if ( !BSQL_ISF_ALL_ATTRS( bsi ) && bi->sql_anlist ) {
240 AttributeName *p;
241
242 /* use hints if available */
243 for ( p = bi->sql_anlist; !BER_BVISNULL( &p->an_name ); p++ ) {
244 if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_user_attrs ) == 0 ) {
245 /* handle "*" */
246 bsi->bsi_flags |= BSQL_SF_ALL_USER;
247
248 /* if all attrs are requested, there's
249 * no need to continue */
250 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
251 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
252 bsi->bsi_op->o_tmpmemctx );
253 bsi->bsi_attrs = NULL;
254 break;
255 }
256 continue;
257
258 } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_operational_attrs ) == 0 ) {
259 /* handle "+" */
260 bsi->bsi_flags |= BSQL_SF_ALL_OPER;
261
262 /* if all attrs are requested, there's
263 * no need to continue */
264 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
265 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
266 bsi->bsi_op->o_tmpmemctx );
267 bsi->bsi_attrs = NULL;
268 break;
269 }
270 continue;
271 }
272
273 backsql_attrlist_add( bsi, p->an_desc );
274 }
275
276 }
277 }
278
279 bsi->bsi_id_list = NULL;
280 bsi->bsi_id_listtail = &bsi->bsi_id_list;
281 bsi->bsi_n_candidates = 0;
282 bsi->bsi_stoptime = stoptime;
283 BER_BVZERO( &bsi->bsi_sel.bb_val );
284 bsi->bsi_sel.bb_len = 0;
285 BER_BVZERO( &bsi->bsi_from.bb_val );
286 bsi->bsi_from.bb_len = 0;
287 BER_BVZERO( &bsi->bsi_join_where.bb_val );
288 bsi->bsi_join_where.bb_len = 0;
289 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
290 bsi->bsi_flt_where.bb_len = 0;
291 bsi->bsi_filter_oc = NULL;
292
293 if ( BACKSQL_IS_GET_ID( flags ) ) {
294 int matched = BACKSQL_IS_MATCHED( flags );
295 int getentry = BACKSQL_IS_GET_ENTRY( flags );
296 int gotit = 0;
297
298 assert( op->o_bd->be_private != NULL );
299
300 rc = backsql_dn2id( op, rs, dbh, nbase, &bsi->bsi_base_id,
301 matched, 1 );
302
303 /* the entry is collected either if requested for by getentry
304 * or if get noSuchObject and requested to climb the tree,
305 * so that a matchedDN or a referral can be returned */
306 if ( ( rc == LDAP_NO_SUCH_OBJECT && matched ) || getentry ) {
307 if ( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) ) {
308 assert( bsi->bsi_e != NULL );
309
310 if ( dn_match( nbase, &bsi->bsi_base_id.eid_ndn ) )
311 {
312 gotit = 1;
313 }
314
315 /*
316 * let's see if it is a referral and, in case, get it
317 */
318 backsql_attrlist_add( bsi, slap_schema.si_ad_ref );
319 rc = backsql_id2entry( bsi, &bsi->bsi_base_id );
320 if ( rc == LDAP_SUCCESS ) {
321 if ( is_entry_referral( bsi->bsi_e ) )
322 {
323 BerVarray erefs = get_entry_referrals( op, bsi->bsi_e );
324 if ( erefs ) {
325 rc = rs->sr_err = LDAP_REFERRAL;
326 rs->sr_ref = referral_rewrite( erefs,
327 &bsi->bsi_e->e_nname,
328 &op->o_req_dn,
329 scope );
330 ber_bvarray_free( erefs );
331
332 } else {
333 rc = rs->sr_err = LDAP_OTHER;
334 rs->sr_text = "bad referral object";
335 }
336
337 } else if ( !gotit ) {
338 rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
339 }
340 }
341
342 } else {
343 rs->sr_err = rc;
344 }
345 }
346
347 if ( gotit && BACKSQL_IS_GET_OC( flags ) ) {
348 bsi->bsi_base_id.eid_oc = backsql_id2oc( bi,
349 bsi->bsi_base_id.eid_oc_id );
350 if ( bsi->bsi_base_id.eid_oc == NULL ) {
351 /* error? */
352 backsql_free_entryID( &bsi->bsi_base_id, 1,
353 op->o_tmpmemctx );
354 rc = rs->sr_err = LDAP_OTHER;
355 }
356 }
357 }
358
359 bsi->bsi_status = rc;
360
361 switch ( rc ) {
362 case LDAP_SUCCESS:
363 case LDAP_REFERRAL:
364 break;
365
366 default:
367 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
368 bsi->bsi_op->o_tmpmemctx );
369 break;
370 }
371
372 return rc;
373 }
374
375 static int
backsql_process_filter_list(backsql_srch_info * bsi,Filter * f,int op)376 backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op )
377 {
378 int res;
379
380 if ( !f ) {
381 return 0;
382 }
383
384 backsql_strfcat_x( &bsi->bsi_flt_where,
385 bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */ );
386
387 while ( 1 ) {
388 res = backsql_process_filter( bsi, f );
389 if ( res < 0 ) {
390 /*
391 * TimesTen : If the query has no answers,
392 * don't bother to run the query.
393 */
394 return -1;
395 }
396
397 f = f->f_next;
398 if ( f == NULL ) {
399 break;
400 }
401
402 switch ( op ) {
403 case LDAP_FILTER_AND:
404 backsql_strfcat_x( &bsi->bsi_flt_where,
405 bsi->bsi_op->o_tmpmemctx, "l",
406 (ber_len_t)STRLENOF( " AND " ),
407 " AND " );
408 break;
409
410 case LDAP_FILTER_OR:
411 backsql_strfcat_x( &bsi->bsi_flt_where,
412 bsi->bsi_op->o_tmpmemctx, "l",
413 (ber_len_t)STRLENOF( " OR " ),
414 " OR " );
415 break;
416 }
417 }
418
419 backsql_strfcat_x( &bsi->bsi_flt_where,
420 bsi->bsi_op->o_tmpmemctx, "c", /* ( */ ')' );
421
422 return 1;
423 }
424
425 static int
backsql_process_sub_filter(backsql_srch_info * bsi,Filter * f,backsql_at_map_rec * at)426 backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f,
427 backsql_at_map_rec *at )
428 {
429 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
430 int i;
431 int casefold = 0;
432
433 if ( !f ) {
434 return 0;
435 }
436
437 /* always uppercase strings by now */
438 #ifdef BACKSQL_UPPERCASE_FILTER
439 if ( f->f_sub_desc->ad_type->sat_substr &&
440 SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
441 bi->sql_caseIgnoreMatch ) )
442 #endif /* BACKSQL_UPPERCASE_FILTER */
443 {
444 casefold = 1;
445 }
446
447 if ( f->f_sub_desc->ad_type->sat_substr &&
448 SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
449 bi->sql_telephoneNumberMatch ) )
450 {
451
452 struct berval bv;
453 ber_len_t i, s, a;
454
455 /*
456 * to check for matching telephone numbers
457 * with intermixed chars, e.g. val='1234'
458 * use
459 *
460 * val LIKE '%1%2%3%4%'
461 */
462
463 BER_BVZERO( &bv );
464 if ( f->f_sub_initial.bv_val ) {
465 bv.bv_len += f->f_sub_initial.bv_len;
466 }
467 if ( f->f_sub_any != NULL ) {
468 for ( a = 0; f->f_sub_any[ a ].bv_val != NULL; a++ ) {
469 bv.bv_len += f->f_sub_any[ a ].bv_len;
470 }
471 }
472 if ( f->f_sub_final.bv_val ) {
473 bv.bv_len += f->f_sub_final.bv_len;
474 }
475 bv.bv_len = 2 * bv.bv_len - 1;
476 bv.bv_val = ch_malloc( bv.bv_len + 1 );
477
478 s = 0;
479 if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
480 bv.bv_val[ s ] = f->f_sub_initial.bv_val[ 0 ];
481 for ( i = 1; i < f->f_sub_initial.bv_len; i++ ) {
482 bv.bv_val[ s + 2 * i - 1 ] = '%';
483 bv.bv_val[ s + 2 * i ] = f->f_sub_initial.bv_val[ i ];
484 }
485 bv.bv_val[ s + 2 * i - 1 ] = '%';
486 s += 2 * i;
487 }
488
489 if ( f->f_sub_any != NULL ) {
490 for ( a = 0; !BER_BVISNULL( &f->f_sub_any[ a ] ); a++ ) {
491 bv.bv_val[ s ] = f->f_sub_any[ a ].bv_val[ 0 ];
492 for ( i = 1; i < f->f_sub_any[ a ].bv_len; i++ ) {
493 bv.bv_val[ s + 2 * i - 1 ] = '%';
494 bv.bv_val[ s + 2 * i ] = f->f_sub_any[ a ].bv_val[ i ];
495 }
496 bv.bv_val[ s + 2 * i - 1 ] = '%';
497 s += 2 * i;
498 }
499 }
500
501 if ( !BER_BVISNULL( &f->f_sub_final ) ) {
502 bv.bv_val[ s ] = f->f_sub_final.bv_val[ 0 ];
503 for ( i = 1; i < f->f_sub_final.bv_len; i++ ) {
504 bv.bv_val[ s + 2 * i - 1 ] = '%';
505 bv.bv_val[ s + 2 * i ] = f->f_sub_final.bv_val[ i ];
506 }
507 bv.bv_val[ s + 2 * i - 1 ] = '%';
508 s += 2 * i;
509 }
510
511 bv.bv_val[ s - 1 ] = '\0';
512
513 (void)backsql_process_filter_like( bsi, at, casefold, &bv );
514 ch_free( bv.bv_val );
515
516 return 1;
517 }
518
519 /*
520 * When dealing with case-sensitive strings
521 * we may omit normalization; however, normalized
522 * SQL filters are more liberal.
523 */
524
525 backsql_strfcat_x( &bsi->bsi_flt_where,
526 bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */ );
527
528 /* TimesTen */
529 Debug( LDAP_DEBUG_TRACE, "backsql_process_sub_filter(%s):\n",
530 at->bam_ad->ad_cname.bv_val );
531 Debug(LDAP_DEBUG_TRACE, " expr: '%s%s%s'\n", at->bam_sel_expr.bv_val,
532 at->bam_sel_expr_u.bv_val ? "' '" : "",
533 at->bam_sel_expr_u.bv_val ? at->bam_sel_expr_u.bv_val : "" );
534 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
535 /*
536 * If a pre-upper-cased version of the column
537 * or a precompiled upper function exists, use it
538 */
539 backsql_strfcat_x( &bsi->bsi_flt_where,
540 bsi->bsi_op->o_tmpmemctx,
541 "bl",
542 &at->bam_sel_expr_u,
543 (ber_len_t)STRLENOF( " LIKE '" ),
544 " LIKE '" );
545
546 } else {
547 backsql_strfcat_x( &bsi->bsi_flt_where,
548 bsi->bsi_op->o_tmpmemctx,
549 "bl",
550 &at->bam_sel_expr,
551 (ber_len_t)STRLENOF( " LIKE '" ), " LIKE '" );
552 }
553
554 if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
555 ber_len_t start;
556
557 #ifdef BACKSQL_TRACE
558 Debug( LDAP_DEBUG_TRACE,
559 "==>backsql_process_sub_filter(%s): "
560 "sub_initial=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
561 f->f_sub_initial.bv_val );
562 #endif /* BACKSQL_TRACE */
563
564 start = bsi->bsi_flt_where.bb_val.bv_len;
565 backsql_strfcat_x( &bsi->bsi_flt_where,
566 bsi->bsi_op->o_tmpmemctx,
567 "b",
568 &f->f_sub_initial );
569 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
570 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
571 }
572 }
573
574 backsql_strfcat_x( &bsi->bsi_flt_where,
575 bsi->bsi_op->o_tmpmemctx,
576 "c", '%' );
577
578 if ( f->f_sub_any != NULL ) {
579 for ( i = 0; !BER_BVISNULL( &f->f_sub_any[ i ] ); i++ ) {
580 ber_len_t start;
581
582 #ifdef BACKSQL_TRACE
583 Debug( LDAP_DEBUG_TRACE,
584 "==>backsql_process_sub_filter(%s): "
585 "sub_any[%d]=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
586 i, f->f_sub_any[ i ].bv_val );
587 #endif /* BACKSQL_TRACE */
588
589 start = bsi->bsi_flt_where.bb_val.bv_len;
590 backsql_strfcat_x( &bsi->bsi_flt_where,
591 bsi->bsi_op->o_tmpmemctx,
592 "bc",
593 &f->f_sub_any[ i ],
594 '%' );
595 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
596 /*
597 * Note: toupper('%') = '%'
598 */
599 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
600 }
601 }
602 }
603
604 if ( !BER_BVISNULL( &f->f_sub_final ) ) {
605 ber_len_t start;
606
607 #ifdef BACKSQL_TRACE
608 Debug( LDAP_DEBUG_TRACE,
609 "==>backsql_process_sub_filter(%s): "
610 "sub_final=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
611 f->f_sub_final.bv_val );
612 #endif /* BACKSQL_TRACE */
613
614 start = bsi->bsi_flt_where.bb_val.bv_len;
615 backsql_strfcat_x( &bsi->bsi_flt_where,
616 bsi->bsi_op->o_tmpmemctx,
617 "b",
618 &f->f_sub_final );
619 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
620 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
621 }
622 }
623
624 backsql_strfcat_x( &bsi->bsi_flt_where,
625 bsi->bsi_op->o_tmpmemctx,
626 "l",
627 (ber_len_t)STRLENOF( /* (' */ "')" ), /* (' */ "')" );
628
629 return 1;
630 }
631
632 static int
backsql_merge_from_tbls(backsql_srch_info * bsi,struct berval * from_tbls)633 backsql_merge_from_tbls( backsql_srch_info *bsi, struct berval *from_tbls )
634 {
635 if ( BER_BVISNULL( from_tbls ) ) {
636 return LDAP_SUCCESS;
637 }
638
639 if ( !BER_BVISNULL( &bsi->bsi_from.bb_val ) ) {
640 char *start, *end;
641 struct berval tmp;
642
643 ber_dupbv_x( &tmp, from_tbls, bsi->bsi_op->o_tmpmemctx );
644
645 for ( start = tmp.bv_val, end = strchr( start, ',' ); start; ) {
646 if ( end ) {
647 end[0] = '\0';
648 }
649
650 if ( strstr( bsi->bsi_from.bb_val.bv_val, start) == NULL )
651 {
652 backsql_strfcat_x( &bsi->bsi_from,
653 bsi->bsi_op->o_tmpmemctx,
654 "cs", ',', start );
655 }
656
657 if ( end ) {
658 /* in case there are spaces after the comma... */
659 for ( start = &end[1]; isspace( start[0] ); start++ );
660 if ( start[0] ) {
661 end = strchr( start, ',' );
662 } else {
663 start = NULL;
664 }
665 } else {
666 start = NULL;
667 }
668 }
669
670 bsi->bsi_op->o_tmpfree( tmp.bv_val, bsi->bsi_op->o_tmpmemctx );
671
672 } else {
673 backsql_strfcat_x( &bsi->bsi_from,
674 bsi->bsi_op->o_tmpmemctx,
675 "b", from_tbls );
676 }
677
678 return LDAP_SUCCESS;
679 }
680
681 static int
backsql_process_filter(backsql_srch_info * bsi,Filter * f)682 backsql_process_filter( backsql_srch_info *bsi, Filter *f )
683 {
684 backsql_at_map_rec **vat = NULL;
685 AttributeDescription *ad = NULL;
686 unsigned i;
687 int done = 0;
688 int rc = 0;
689
690 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter()\n" );
691 if ( f->f_choice == SLAPD_FILTER_COMPUTED ) {
692 struct berval flt;
693 char *msg = NULL;
694
695 switch ( f->f_result ) {
696 case LDAP_COMPARE_TRUE:
697 BER_BVSTR( &flt, "10=10" );
698 msg = "TRUE";
699 break;
700
701 case LDAP_COMPARE_FALSE:
702 BER_BVSTR( &flt, "11=0" );
703 msg = "FALSE";
704 break;
705
706 case SLAPD_COMPARE_UNDEFINED:
707 BER_BVSTR( &flt, "12=0" );
708 msg = "UNDEFINED";
709 break;
710
711 default:
712 rc = -1;
713 goto done;
714 }
715
716 Debug( LDAP_DEBUG_TRACE, "backsql_process_filter(): "
717 "filter computed (%s)\n", msg );
718 backsql_strfcat_x( &bsi->bsi_flt_where,
719 bsi->bsi_op->o_tmpmemctx, "b", &flt );
720 rc = 1;
721 goto done;
722 }
723
724 if ( f->f_choice & SLAPD_FILTER_UNDEFINED ) {
725 backsql_strfcat_x( &bsi->bsi_flt_where,
726 bsi->bsi_op->o_tmpmemctx,
727 "l",
728 (ber_len_t)STRLENOF( "1=0" ), "1=0" );
729 done = 1;
730 rc = 1;
731 goto done;
732 }
733
734 switch( f->f_choice ) {
735 case LDAP_FILTER_OR:
736 rc = backsql_process_filter_list( bsi, f->f_or,
737 LDAP_FILTER_OR );
738 done = 1;
739 break;
740
741 case LDAP_FILTER_AND:
742 rc = backsql_process_filter_list( bsi, f->f_and,
743 LDAP_FILTER_AND );
744 done = 1;
745 break;
746
747 case LDAP_FILTER_NOT:
748 backsql_strfcat_x( &bsi->bsi_flt_where,
749 bsi->bsi_op->o_tmpmemctx,
750 "l",
751 (ber_len_t)STRLENOF( "NOT (" /* ) */ ),
752 "NOT (" /* ) */ );
753 rc = backsql_process_filter( bsi, f->f_not );
754 backsql_strfcat_x( &bsi->bsi_flt_where,
755 bsi->bsi_op->o_tmpmemctx,
756 "c", /* ( */ ')' );
757 done = 1;
758 break;
759
760 case LDAP_FILTER_PRESENT:
761 ad = f->f_desc;
762 break;
763
764 case LDAP_FILTER_EXT:
765 ad = f->f_mra->ma_desc;
766 if ( f->f_mr_dnattrs ) {
767 /*
768 * if dn attrs filtering is requested, better return
769 * success and let test_filter() deal with candidate
770 * selection; otherwise we'd need to set conditions
771 * on the contents of the DN, e.g. "SELECT ... FROM
772 * ldap_entries AS attributeName WHERE attributeName.dn
773 * like '%attributeName=value%'"
774 */
775 backsql_strfcat_x( &bsi->bsi_flt_where,
776 bsi->bsi_op->o_tmpmemctx,
777 "l",
778 (ber_len_t)STRLENOF( "1=1" ), "1=1" );
779 bsi->bsi_status = LDAP_SUCCESS;
780 rc = 1;
781 goto done;
782 }
783 break;
784
785 default:
786 ad = f->f_av_desc;
787 break;
788 }
789
790 if ( rc == -1 ) {
791 goto done;
792 }
793
794 if ( done ) {
795 rc = 1;
796 goto done;
797 }
798
799 /*
800 * Turn structuralObjectClass into objectClass
801 */
802 if ( ad == slap_schema.si_ad_objectClass
803 || ad == slap_schema.si_ad_structuralObjectClass )
804 {
805 /*
806 * If the filter is LDAP_FILTER_PRESENT, then it's done;
807 * otherwise, let's see if we are lucky: filtering
808 * for "structural" objectclass or ancestor...
809 */
810 switch ( f->f_choice ) {
811 case LDAP_FILTER_EQUALITY:
812 {
813 ObjectClass *oc = oc_bvfind( &f->f_av_value );
814
815 if ( oc == NULL ) {
816 Debug( LDAP_DEBUG_TRACE,
817 "backsql_process_filter(): "
818 "unknown objectClass \"%s\" "
819 "in filter\n",
820 f->f_av_value.bv_val );
821 bsi->bsi_status = LDAP_OTHER;
822 rc = -1;
823 goto done;
824 }
825
826 /*
827 * "structural" objectClass inheritance:
828 * - a search for "person" will also return
829 * "inetOrgPerson"
830 * - a search for "top" will return everything
831 */
832 if ( is_object_subclass( oc, bsi->bsi_oc->bom_oc ) ) {
833 static struct berval ldap_entry_objclasses = BER_BVC( "ldap_entry_objclasses" );
834
835 backsql_merge_from_tbls( bsi, &ldap_entry_objclasses );
836
837 backsql_strfcat_x( &bsi->bsi_flt_where,
838 bsi->bsi_op->o_tmpmemctx,
839 "lbl",
840 (ber_len_t)STRLENOF( "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */ ),
841 "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */,
842 &bsi->bsi_oc->bom_oc->soc_cname,
843 (ber_len_t)STRLENOF( /* ((' */ "'))" ),
844 /* ((' */ "'))" );
845 bsi->bsi_status = LDAP_SUCCESS;
846 rc = 1;
847 goto done;
848 }
849
850 break;
851 }
852
853 case LDAP_FILTER_PRESENT:
854 backsql_strfcat_x( &bsi->bsi_flt_where,
855 bsi->bsi_op->o_tmpmemctx,
856 "l",
857 (ber_len_t)STRLENOF( "3=3" ), "3=3" );
858 bsi->bsi_status = LDAP_SUCCESS;
859 rc = 1;
860 goto done;
861
862 /* FIXME: LDAP_FILTER_EXT? */
863
864 default:
865 Debug( LDAP_DEBUG_TRACE,
866 "backsql_process_filter(): "
867 "illegal/unhandled filter "
868 "on objectClass attribute" );
869 bsi->bsi_status = LDAP_OTHER;
870 rc = -1;
871 goto done;
872 }
873
874 } else if ( ad == slap_schema.si_ad_entryUUID ) {
875 unsigned long oc_id;
876 #ifdef BACKSQL_ARBITRARY_KEY
877 struct berval keyval;
878 #else /* ! BACKSQL_ARBITRARY_KEY */
879 unsigned long keyval;
880 char keyvalbuf[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
881 #endif /* ! BACKSQL_ARBITRARY_KEY */
882
883 switch ( f->f_choice ) {
884 case LDAP_FILTER_EQUALITY:
885 backsql_entryUUID_decode( &f->f_av_value, &oc_id, &keyval );
886
887 if ( oc_id != bsi->bsi_oc->bom_id ) {
888 bsi->bsi_status = LDAP_SUCCESS;
889 rc = -1;
890 goto done;
891 }
892
893 #ifdef BACKSQL_ARBITRARY_KEY
894 backsql_strfcat_x( &bsi->bsi_flt_where,
895 bsi->bsi_op->o_tmpmemctx,
896 "bcblbc",
897 &bsi->bsi_oc->bom_keytbl, '.',
898 &bsi->bsi_oc->bom_keycol,
899 STRLENOF( " LIKE '" ), " LIKE '",
900 &keyval, '\'' );
901 #else /* ! BACKSQL_ARBITRARY_KEY */
902 snprintf( keyvalbuf, sizeof( keyvalbuf ), "%lu", keyval );
903 backsql_strfcat_x( &bsi->bsi_flt_where,
904 bsi->bsi_op->o_tmpmemctx,
905 "bcbcs",
906 &bsi->bsi_oc->bom_keytbl, '.',
907 &bsi->bsi_oc->bom_keycol, '=', keyvalbuf );
908 #endif /* ! BACKSQL_ARBITRARY_KEY */
909 break;
910
911 case LDAP_FILTER_PRESENT:
912 backsql_strfcat_x( &bsi->bsi_flt_where,
913 bsi->bsi_op->o_tmpmemctx,
914 "l",
915 (ber_len_t)STRLENOF( "4=4" ), "4=4" );
916 break;
917
918 default:
919 rc = -1;
920 goto done;
921 }
922
923 bsi->bsi_flags |= BSQL_SF_FILTER_ENTRYUUID;
924 rc = 1;
925 goto done;
926
927 #ifdef BACKSQL_SYNCPROV
928 } else if ( ad == slap_schema.si_ad_entryCSN ) {
929 /*
930 * support for syncrepl as provider...
931 */
932 #if 0
933 if ( !bsi->bsi_op->o_sync ) {
934 /* unsupported at present... */
935 bsi->bsi_status = LDAP_OTHER;
936 rc = -1;
937 goto done;
938 }
939 #endif
940
941 bsi->bsi_flags |= ( BSQL_SF_FILTER_ENTRYCSN | BSQL_SF_RETURN_ENTRYUUID);
942
943 /* if doing a syncrepl, try to return as much as possible,
944 * and always match the filter */
945 backsql_strfcat_x( &bsi->bsi_flt_where,
946 bsi->bsi_op->o_tmpmemctx,
947 "l",
948 (ber_len_t)STRLENOF( "5=5" ), "5=5" );
949
950 /* save for later use in operational attributes */
951 /* FIXME: saves only the first occurrence, because
952 * the filter during updates is written as
953 * "(&(entryCSN<={contextCSN})(entryCSN>={oldContextCSN})({filter}))"
954 * so we want our fake entryCSN to match the greatest
955 * value
956 */
957 if ( bsi->bsi_op->o_private == NULL ) {
958 bsi->bsi_op->o_private = &f->f_av_value;
959 }
960 bsi->bsi_status = LDAP_SUCCESS;
961
962 rc = 1;
963 goto done;
964 #endif /* BACKSQL_SYNCPROV */
965
966 } else if ( ad == slap_schema.si_ad_hasSubordinates || ad == NULL ) {
967 /*
968 * FIXME: this is not robust; e.g. a filter
969 * '(!(hasSubordinates=TRUE))' fails because
970 * in SQL it would read 'NOT (1=1)' instead
971 * of no condition.
972 * Note however that hasSubordinates is boolean,
973 * so a more appropriate filter would be
974 * '(hasSubordinates=FALSE)'
975 *
976 * A more robust search for hasSubordinates
977 * would * require joining the ldap_entries table
978 * selecting if there are descendants of the
979 * candidate.
980 */
981 backsql_strfcat_x( &bsi->bsi_flt_where,
982 bsi->bsi_op->o_tmpmemctx,
983 "l",
984 (ber_len_t)STRLENOF( "6=6" ), "6=6" );
985 if ( ad == slap_schema.si_ad_hasSubordinates ) {
986 /*
987 * instruct candidate selection algorithm
988 * and attribute list to try to detect
989 * if an entry has subordinates
990 */
991 bsi->bsi_flags |= BSQL_SF_FILTER_HASSUBORDINATE;
992
993 } else {
994 /*
995 * clear attributes to fetch, to require ALL
996 * and try extended match on all attributes
997 */
998 backsql_attrlist_add( bsi, NULL );
999 }
1000 rc = 1;
1001 goto done;
1002 }
1003
1004 /*
1005 * attribute inheritance:
1006 */
1007 if ( backsql_supad2at( bsi->bsi_oc, ad, &vat ) ) {
1008 bsi->bsi_status = LDAP_OTHER;
1009 rc = -1;
1010 goto done;
1011 }
1012
1013 if ( vat == NULL ) {
1014 /* search anyway; other parts of the filter
1015 * may succeed */
1016 backsql_strfcat_x( &bsi->bsi_flt_where,
1017 bsi->bsi_op->o_tmpmemctx,
1018 "l",
1019 (ber_len_t)STRLENOF( "7=7" ), "7=7" );
1020 bsi->bsi_status = LDAP_SUCCESS;
1021 rc = 1;
1022 goto done;
1023 }
1024
1025 /* if required, open extra level of parens */
1026 done = 0;
1027 if ( vat[0]->bam_next || vat[1] ) {
1028 backsql_strfcat_x( &bsi->bsi_flt_where,
1029 bsi->bsi_op->o_tmpmemctx,
1030 "c", '(' );
1031 done = 1;
1032 }
1033
1034 i = 0;
1035 next:;
1036 /* apply attr */
1037 if ( backsql_process_filter_attr( bsi, f, vat[i] ) == -1 ) {
1038 return -1;
1039 }
1040
1041 /* if more definitions of the same attr, apply */
1042 if ( vat[i]->bam_next ) {
1043 backsql_strfcat_x( &bsi->bsi_flt_where,
1044 bsi->bsi_op->o_tmpmemctx,
1045 "l",
1046 STRLENOF( " OR " ), " OR " );
1047 vat[i] = vat[i]->bam_next;
1048 goto next;
1049 }
1050
1051 /* if more descendants of the same attr, apply */
1052 i++;
1053 if ( vat[i] ) {
1054 backsql_strfcat_x( &bsi->bsi_flt_where,
1055 bsi->bsi_op->o_tmpmemctx,
1056 "l",
1057 STRLENOF( " OR " ), " OR " );
1058 goto next;
1059 }
1060
1061 /* if needed, close extra level of parens */
1062 if ( done ) {
1063 backsql_strfcat_x( &bsi->bsi_flt_where,
1064 bsi->bsi_op->o_tmpmemctx,
1065 "c", ')' );
1066 }
1067
1068 rc = 1;
1069
1070 done:;
1071 if ( vat ) {
1072 ch_free( vat );
1073 }
1074
1075 Debug( LDAP_DEBUG_TRACE,
1076 "<==backsql_process_filter() %s\n",
1077 rc == 1 ? "succeeded" : "failed" );
1078
1079 return rc;
1080 }
1081
1082 static int
backsql_process_filter_eq(backsql_srch_info * bsi,backsql_at_map_rec * at,int casefold,struct berval * filter_value)1083 backsql_process_filter_eq( backsql_srch_info *bsi, backsql_at_map_rec *at,
1084 int casefold, struct berval *filter_value )
1085 {
1086 /*
1087 * maybe we should check type of at->sel_expr here somehow,
1088 * to know whether upper_func is applicable, but for now
1089 * upper_func stuff is made for Oracle, where UPPER is
1090 * safely applicable to NUMBER etc.
1091 */
1092 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1093 ber_len_t start;
1094
1095 backsql_strfcat_x( &bsi->bsi_flt_where,
1096 bsi->bsi_op->o_tmpmemctx,
1097 "cbl",
1098 '(', /* ) */
1099 &at->bam_sel_expr_u,
1100 (ber_len_t)STRLENOF( "='" ),
1101 "='" );
1102
1103 start = bsi->bsi_flt_where.bb_val.bv_len;
1104
1105 backsql_strfcat_x( &bsi->bsi_flt_where,
1106 bsi->bsi_op->o_tmpmemctx,
1107 "bl",
1108 filter_value,
1109 (ber_len_t)STRLENOF( /* (' */ "')" ),
1110 /* (' */ "')" );
1111
1112 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1113
1114 } else {
1115 backsql_strfcat_x( &bsi->bsi_flt_where,
1116 bsi->bsi_op->o_tmpmemctx,
1117 "cblbl",
1118 '(', /* ) */
1119 &at->bam_sel_expr,
1120 (ber_len_t)STRLENOF( "='" ), "='",
1121 filter_value,
1122 (ber_len_t)STRLENOF( /* (' */ "')" ),
1123 /* (' */ "')" );
1124 }
1125
1126 return 1;
1127 }
1128
1129 static int
backsql_process_filter_like(backsql_srch_info * bsi,backsql_at_map_rec * at,int casefold,struct berval * filter_value)1130 backsql_process_filter_like( backsql_srch_info *bsi, backsql_at_map_rec *at,
1131 int casefold, struct berval *filter_value )
1132 {
1133 /*
1134 * maybe we should check type of at->sel_expr here somehow,
1135 * to know whether upper_func is applicable, but for now
1136 * upper_func stuff is made for Oracle, where UPPER is
1137 * safely applicable to NUMBER etc.
1138 */
1139 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1140 ber_len_t start;
1141
1142 backsql_strfcat_x( &bsi->bsi_flt_where,
1143 bsi->bsi_op->o_tmpmemctx,
1144 "cbl",
1145 '(', /* ) */
1146 &at->bam_sel_expr_u,
1147 (ber_len_t)STRLENOF( " LIKE '%" ),
1148 " LIKE '%" );
1149
1150 start = bsi->bsi_flt_where.bb_val.bv_len;
1151
1152 backsql_strfcat_x( &bsi->bsi_flt_where,
1153 bsi->bsi_op->o_tmpmemctx,
1154 "bl",
1155 filter_value,
1156 (ber_len_t)STRLENOF( /* (' */ "%')" ),
1157 /* (' */ "%')" );
1158
1159 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1160
1161 } else {
1162 backsql_strfcat_x( &bsi->bsi_flt_where,
1163 bsi->bsi_op->o_tmpmemctx,
1164 "cblbl",
1165 '(', /* ) */
1166 &at->bam_sel_expr,
1167 (ber_len_t)STRLENOF( " LIKE '%" ),
1168 " LIKE '%",
1169 filter_value,
1170 (ber_len_t)STRLENOF( /* (' */ "%')" ),
1171 /* (' */ "%')" );
1172 }
1173
1174 return 1;
1175 }
1176
1177 static int
backsql_process_filter_attr(backsql_srch_info * bsi,Filter * f,backsql_at_map_rec * at)1178 backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f, backsql_at_map_rec *at )
1179 {
1180 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1181 int casefold = 0;
1182 struct berval *filter_value = NULL;
1183 MatchingRule *matching_rule = NULL;
1184 struct berval ordering = BER_BVC("<=");
1185
1186 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter_attr(%s)\n",
1187 at->bam_ad->ad_cname.bv_val );
1188
1189 /*
1190 * need to add this attribute to list of attrs to load,
1191 * so that we can do test_filter() later
1192 */
1193 backsql_attrlist_add( bsi, at->bam_ad );
1194
1195 backsql_merge_from_tbls( bsi, &at->bam_from_tbls );
1196
1197 if ( !BER_BVISNULL( &at->bam_join_where )
1198 && strstr( bsi->bsi_join_where.bb_val.bv_val,
1199 at->bam_join_where.bv_val ) == NULL )
1200 {
1201 backsql_strfcat_x( &bsi->bsi_join_where,
1202 bsi->bsi_op->o_tmpmemctx,
1203 "lb",
1204 (ber_len_t)STRLENOF( " AND " ), " AND ",
1205 &at->bam_join_where );
1206 }
1207
1208 if ( f->f_choice & SLAPD_FILTER_UNDEFINED ) {
1209 backsql_strfcat_x( &bsi->bsi_flt_where,
1210 bsi->bsi_op->o_tmpmemctx,
1211 "l",
1212 (ber_len_t)STRLENOF( "1=0" ), "1=0" );
1213 return 1;
1214 }
1215
1216 switch ( f->f_choice ) {
1217 case LDAP_FILTER_EQUALITY:
1218 filter_value = &f->f_av_value;
1219 matching_rule = at->bam_ad->ad_type->sat_equality;
1220
1221 goto equality_match;
1222
1223 /* fail over into next case */
1224
1225 case LDAP_FILTER_EXT:
1226 filter_value = &f->f_mra->ma_value;
1227 matching_rule = f->f_mr_rule;
1228
1229 equality_match:;
1230 /* always uppercase strings by now */
1231 #ifdef BACKSQL_UPPERCASE_FILTER
1232 if ( SLAP_MR_ASSOCIATED( matching_rule,
1233 bi->sql_caseIgnoreMatch ) )
1234 #endif /* BACKSQL_UPPERCASE_FILTER */
1235 {
1236 casefold = 1;
1237 }
1238
1239 /* FIXME: directoryString filtering should use a similar
1240 * approach to deal with non-prettified values like
1241 * " A non prettified value ", by using a LIKE
1242 * filter with all whitespaces collapsed to a single '%' */
1243 if ( SLAP_MR_ASSOCIATED( matching_rule,
1244 bi->sql_telephoneNumberMatch ) )
1245 {
1246 struct berval bv;
1247 ber_len_t i;
1248
1249 /*
1250 * to check for matching telephone numbers
1251 * with intermized chars, e.g. val='1234'
1252 * use
1253 *
1254 * val LIKE '%1%2%3%4%'
1255 */
1256
1257 bv.bv_len = 2 * filter_value->bv_len - 1;
1258 bv.bv_val = ch_malloc( bv.bv_len + 1 );
1259
1260 bv.bv_val[ 0 ] = filter_value->bv_val[ 0 ];
1261 for ( i = 1; i < filter_value->bv_len; i++ ) {
1262 bv.bv_val[ 2 * i - 1 ] = '%';
1263 bv.bv_val[ 2 * i ] = filter_value->bv_val[ i ];
1264 }
1265 bv.bv_val[ 2 * i - 1 ] = '\0';
1266
1267 (void)backsql_process_filter_like( bsi, at, casefold, &bv );
1268 ch_free( bv.bv_val );
1269
1270 break;
1271 }
1272
1273 /* NOTE: this is required by objectClass inheritance
1274 * and auxiliary objectClass use in filters for slightly
1275 * more efficient candidate selection. */
1276 /* FIXME: a bit too many specializations to deal with
1277 * very specific cases... */
1278 if ( at->bam_ad == slap_schema.si_ad_objectClass
1279 || at->bam_ad == slap_schema.si_ad_structuralObjectClass )
1280 {
1281 backsql_strfcat_x( &bsi->bsi_flt_where,
1282 bsi->bsi_op->o_tmpmemctx,
1283 "lbl",
1284 (ber_len_t)STRLENOF( "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */ ),
1285 "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */,
1286 filter_value,
1287 (ber_len_t)STRLENOF( /* (' */ "')" ),
1288 /* (' */ "')" );
1289 break;
1290 }
1291
1292 /*
1293 * maybe we should check type of at->sel_expr here somehow,
1294 * to know whether upper_func is applicable, but for now
1295 * upper_func stuff is made for Oracle, where UPPER is
1296 * safely applicable to NUMBER etc.
1297 */
1298 (void)backsql_process_filter_eq( bsi, at, casefold, filter_value );
1299 break;
1300
1301 case LDAP_FILTER_GE:
1302 ordering.bv_val = ">=";
1303
1304 /* fall thru to next case */
1305
1306 case LDAP_FILTER_LE:
1307 filter_value = &f->f_av_value;
1308
1309 /* always uppercase strings by now */
1310 #ifdef BACKSQL_UPPERCASE_FILTER
1311 if ( at->bam_ad->ad_type->sat_ordering &&
1312 SLAP_MR_ASSOCIATED( at->bam_ad->ad_type->sat_ordering,
1313 bi->sql_caseIgnoreMatch ) )
1314 #endif /* BACKSQL_UPPERCASE_FILTER */
1315 {
1316 casefold = 1;
1317 }
1318
1319 /*
1320 * FIXME: should we uppercase the operands?
1321 */
1322 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1323 ber_len_t start;
1324
1325 backsql_strfcat_x( &bsi->bsi_flt_where,
1326 bsi->bsi_op->o_tmpmemctx,
1327 "cbbc",
1328 '(', /* ) */
1329 &at->bam_sel_expr_u,
1330 &ordering,
1331 '\'' );
1332
1333 start = bsi->bsi_flt_where.bb_val.bv_len;
1334
1335 backsql_strfcat_x( &bsi->bsi_flt_where,
1336 bsi->bsi_op->o_tmpmemctx,
1337 "bl",
1338 filter_value,
1339 (ber_len_t)STRLENOF( /* (' */ "')" ),
1340 /* (' */ "')" );
1341
1342 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1343
1344 } else {
1345 backsql_strfcat_x( &bsi->bsi_flt_where,
1346 bsi->bsi_op->o_tmpmemctx,
1347 "cbbcbl",
1348 '(' /* ) */ ,
1349 &at->bam_sel_expr,
1350 &ordering,
1351 '\'',
1352 &f->f_av_value,
1353 (ber_len_t)STRLENOF( /* (' */ "')" ),
1354 /* ( */ "')" );
1355 }
1356 break;
1357
1358 case LDAP_FILTER_PRESENT:
1359 backsql_strfcat_x( &bsi->bsi_flt_where,
1360 bsi->bsi_op->o_tmpmemctx,
1361 "lbl",
1362 (ber_len_t)STRLENOF( "NOT (" /* ) */),
1363 "NOT (", /* ) */
1364 &at->bam_sel_expr,
1365 (ber_len_t)STRLENOF( /* ( */ " IS NULL)" ),
1366 /* ( */ " IS NULL)" );
1367 break;
1368
1369 case LDAP_FILTER_SUBSTRINGS:
1370 backsql_process_sub_filter( bsi, f, at );
1371 break;
1372
1373 case LDAP_FILTER_APPROX:
1374 /* we do our best */
1375
1376 /*
1377 * maybe we should check type of at->sel_expr here somehow,
1378 * to know whether upper_func is applicable, but for now
1379 * upper_func stuff is made for Oracle, where UPPER is
1380 * safely applicable to NUMBER etc.
1381 */
1382 (void)backsql_process_filter_like( bsi, at, 1, &f->f_av_value );
1383 break;
1384
1385 default:
1386 /* unhandled filter type; should not happen */
1387 assert( 0 );
1388 backsql_strfcat_x( &bsi->bsi_flt_where,
1389 bsi->bsi_op->o_tmpmemctx,
1390 "l",
1391 (ber_len_t)STRLENOF( "8=8" ), "8=8" );
1392 break;
1393
1394 }
1395
1396 Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter_attr(%s)\n",
1397 at->bam_ad->ad_cname.bv_val );
1398
1399 return 1;
1400 }
1401
1402 static int
backsql_srch_query(backsql_srch_info * bsi,struct berval * query)1403 backsql_srch_query( backsql_srch_info *bsi, struct berval *query )
1404 {
1405 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1406 int rc;
1407
1408 assert( query != NULL );
1409 BER_BVZERO( query );
1410
1411 bsi->bsi_use_subtree_shortcut = 0;
1412
1413 Debug( LDAP_DEBUG_TRACE, "==>backsql_srch_query()\n" );
1414 BER_BVZERO( &bsi->bsi_sel.bb_val );
1415 BER_BVZERO( &bsi->bsi_sel.bb_val );
1416 bsi->bsi_sel.bb_len = 0;
1417 BER_BVZERO( &bsi->bsi_from.bb_val );
1418 bsi->bsi_from.bb_len = 0;
1419 BER_BVZERO( &bsi->bsi_join_where.bb_val );
1420 bsi->bsi_join_where.bb_len = 0;
1421 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1422 bsi->bsi_flt_where.bb_len = 0;
1423
1424 backsql_strfcat_x( &bsi->bsi_sel,
1425 bsi->bsi_op->o_tmpmemctx,
1426 "lbcbc",
1427 (ber_len_t)STRLENOF( "SELECT DISTINCT ldap_entries.id," ),
1428 "SELECT DISTINCT ldap_entries.id,",
1429 &bsi->bsi_oc->bom_keytbl,
1430 '.',
1431 &bsi->bsi_oc->bom_keycol,
1432 ',' );
1433
1434 if ( !BER_BVISNULL( &bi->sql_strcast_func ) ) {
1435 backsql_strfcat_x( &bsi->bsi_sel,
1436 bsi->bsi_op->o_tmpmemctx,
1437 "blbl",
1438 &bi->sql_strcast_func,
1439 (ber_len_t)STRLENOF( "('" /* ') */ ),
1440 "('" /* ') */ ,
1441 &bsi->bsi_oc->bom_oc->soc_cname,
1442 (ber_len_t)STRLENOF( /* (' */ "')" ),
1443 /* (' */ "')" );
1444 } else {
1445 backsql_strfcat_x( &bsi->bsi_sel,
1446 bsi->bsi_op->o_tmpmemctx,
1447 "cbc",
1448 '\'',
1449 &bsi->bsi_oc->bom_oc->soc_cname,
1450 '\'' );
1451 }
1452
1453 backsql_strfcat_x( &bsi->bsi_sel,
1454 bsi->bsi_op->o_tmpmemctx,
1455 "b",
1456 &bi->sql_dn_oc_aliasing );
1457 backsql_strfcat_x( &bsi->bsi_from,
1458 bsi->bsi_op->o_tmpmemctx,
1459 "lb",
1460 (ber_len_t)STRLENOF( " FROM ldap_entries," ),
1461 " FROM ldap_entries,",
1462 &bsi->bsi_oc->bom_keytbl );
1463
1464 backsql_strfcat_x( &bsi->bsi_join_where,
1465 bsi->bsi_op->o_tmpmemctx,
1466 "lbcbl",
1467 (ber_len_t)STRLENOF( " WHERE " ), " WHERE ",
1468 &bsi->bsi_oc->bom_keytbl,
1469 '.',
1470 &bsi->bsi_oc->bom_keycol,
1471 (ber_len_t)STRLENOF( "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ),
1472 "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " );
1473
1474 switch ( bsi->bsi_scope ) {
1475 case LDAP_SCOPE_BASE:
1476 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1477 backsql_strfcat_x( &bsi->bsi_join_where,
1478 bsi->bsi_op->o_tmpmemctx,
1479 "bl",
1480 &bi->sql_upper_func,
1481 (ber_len_t)STRLENOF( "(ldap_entries.dn)=?" ),
1482 "(ldap_entries.dn)=?" );
1483 } else {
1484 backsql_strfcat_x( &bsi->bsi_join_where,
1485 bsi->bsi_op->o_tmpmemctx,
1486 "l",
1487 (ber_len_t)STRLENOF( "ldap_entries.dn=?" ),
1488 "ldap_entries.dn=?" );
1489 }
1490 break;
1491
1492 case BACKSQL_SCOPE_BASE_LIKE:
1493 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1494 backsql_strfcat_x( &bsi->bsi_join_where,
1495 bsi->bsi_op->o_tmpmemctx,
1496 "bl",
1497 &bi->sql_upper_func,
1498 (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1499 "(ldap_entries.dn) LIKE ?" );
1500 } else {
1501 backsql_strfcat_x( &bsi->bsi_join_where,
1502 bsi->bsi_op->o_tmpmemctx,
1503 "l",
1504 (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1505 "ldap_entries.dn LIKE ?" );
1506 }
1507 break;
1508
1509 case LDAP_SCOPE_ONELEVEL:
1510 backsql_strfcat_x( &bsi->bsi_join_where,
1511 bsi->bsi_op->o_tmpmemctx,
1512 "l",
1513 (ber_len_t)STRLENOF( "ldap_entries.parent=?" ),
1514 "ldap_entries.parent=?" );
1515 break;
1516
1517 case LDAP_SCOPE_SUBORDINATE:
1518 case LDAP_SCOPE_SUBTREE:
1519 if ( BACKSQL_USE_SUBTREE_SHORTCUT( bi ) ) {
1520 int i;
1521 BackendDB *bd = bsi->bsi_op->o_bd;
1522
1523 assert( bd->be_nsuffix != NULL );
1524
1525 for ( i = 0; !BER_BVISNULL( &bd->be_nsuffix[ i ] ); i++ )
1526 {
1527 if ( dn_match( &bd->be_nsuffix[ i ],
1528 bsi->bsi_base_ndn ) )
1529 {
1530 /* pass this to the candidate selection
1531 * routine so that the DN is not bound
1532 * to the select statement */
1533 bsi->bsi_use_subtree_shortcut = 1;
1534 break;
1535 }
1536 }
1537 }
1538
1539 if ( bsi->bsi_use_subtree_shortcut ) {
1540 /* Skip the base DN filter, as every entry will match it */
1541 backsql_strfcat_x( &bsi->bsi_join_where,
1542 bsi->bsi_op->o_tmpmemctx,
1543 "l",
1544 (ber_len_t)STRLENOF( "9=9"), "9=9");
1545
1546 } else if ( !BER_BVISNULL( &bi->sql_subtree_cond ) ) {
1547 /* This should always be true... */
1548 backsql_strfcat_x( &bsi->bsi_join_where,
1549 bsi->bsi_op->o_tmpmemctx,
1550 "b",
1551 &bi->sql_subtree_cond );
1552
1553 } else if ( BACKSQL_CANUPPERCASE( bi ) ) {
1554 backsql_strfcat_x( &bsi->bsi_join_where,
1555 bsi->bsi_op->o_tmpmemctx,
1556 "bl",
1557 &bi->sql_upper_func,
1558 (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1559 "(ldap_entries.dn) LIKE ?" );
1560
1561 } else {
1562 backsql_strfcat_x( &bsi->bsi_join_where,
1563 bsi->bsi_op->o_tmpmemctx,
1564 "l",
1565 (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1566 "ldap_entries.dn LIKE ?" );
1567 }
1568
1569 break;
1570
1571 default:
1572 assert( 0 );
1573 }
1574
1575 #ifndef BACKSQL_ARBITRARY_KEY
1576 /* If paged results are in effect, ignore low ldap_entries.id numbers */
1577 if ( get_pagedresults(bsi->bsi_op) > SLAP_CONTROL_IGNORED ) {
1578 unsigned long lowid = 0;
1579
1580 /* Pick up the previous ldap_entries.id if the previous page ended in this objectClass */
1581 if ( bsi->bsi_oc->bom_id == PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie ) )
1582 {
1583 lowid = PAGECOOKIE_TO_SQL_ID( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie );
1584 }
1585
1586 if ( lowid ) {
1587 char lowidstring[48];
1588 int lowidlen;
1589
1590 lowidlen = snprintf( lowidstring, sizeof( lowidstring ),
1591 " AND ldap_entries.id>%lu", lowid );
1592 backsql_strfcat_x( &bsi->bsi_join_where,
1593 bsi->bsi_op->o_tmpmemctx,
1594 "l",
1595 (ber_len_t)lowidlen,
1596 lowidstring );
1597 }
1598 }
1599 #endif /* ! BACKSQL_ARBITRARY_KEY */
1600
1601 rc = backsql_process_filter( bsi, bsi->bsi_filter );
1602 if ( rc > 0 ) {
1603 struct berbuf bb = BB_NULL;
1604
1605 backsql_strfcat_x( &bb,
1606 bsi->bsi_op->o_tmpmemctx,
1607 "bbblb",
1608 &bsi->bsi_sel.bb_val,
1609 &bsi->bsi_from.bb_val,
1610 &bsi->bsi_join_where.bb_val,
1611 (ber_len_t)STRLENOF( " AND " ), " AND ",
1612 &bsi->bsi_flt_where.bb_val );
1613
1614 *query = bb.bb_val;
1615
1616 } else if ( rc < 0 ) {
1617 /*
1618 * Indicates that there's no possible way the filter matches
1619 * anything. No need to issue the query
1620 */
1621 free( query->bv_val );
1622 BER_BVZERO( query );
1623 }
1624
1625 bsi->bsi_op->o_tmpfree( bsi->bsi_sel.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1626 BER_BVZERO( &bsi->bsi_sel.bb_val );
1627 bsi->bsi_sel.bb_len = 0;
1628 bsi->bsi_op->o_tmpfree( bsi->bsi_from.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1629 BER_BVZERO( &bsi->bsi_from.bb_val );
1630 bsi->bsi_from.bb_len = 0;
1631 bsi->bsi_op->o_tmpfree( bsi->bsi_join_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1632 BER_BVZERO( &bsi->bsi_join_where.bb_val );
1633 bsi->bsi_join_where.bb_len = 0;
1634 bsi->bsi_op->o_tmpfree( bsi->bsi_flt_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1635 BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1636 bsi->bsi_flt_where.bb_len = 0;
1637
1638 Debug( LDAP_DEBUG_TRACE, "<==backsql_srch_query() returns %s\n",
1639 query->bv_val ? query->bv_val : "NULL" );
1640
1641 return ( rc <= 0 ? 1 : 0 );
1642 }
1643
1644 static int
backsql_oc_get_candidates(void * v_oc,void * v_bsi)1645 backsql_oc_get_candidates( void *v_oc, void *v_bsi )
1646 {
1647 backsql_oc_map_rec *oc = v_oc;
1648 backsql_srch_info *bsi = v_bsi;
1649 Operation *op = bsi->bsi_op;
1650 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1651 struct berval query;
1652 SQLHSTMT sth = SQL_NULL_HSTMT;
1653 RETCODE rc;
1654 int res;
1655 BACKSQL_ROW_NTS row;
1656 int i;
1657 int j;
1658 int n_candidates = bsi->bsi_n_candidates;
1659
1660 /*
1661 * + 1 because we need room for '%';
1662 * + 1 because we need room for ',' for LDAP_SCOPE_SUBORDINATE;
1663 * this makes a subtree
1664 * search for a DN BACKSQL_MAX_DN_LEN long legal
1665 * if it returns that DN only
1666 */
1667 char tmp_base_ndn[ BACKSQL_MAX_DN_LEN + 1 + 1 ];
1668
1669 bsi->bsi_status = LDAP_SUCCESS;
1670
1671 Debug( LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc=\"%s\"\n",
1672 BACKSQL_OC_NAME( oc ) );
1673
1674 /* check for abandon */
1675 if ( op->o_abandon ) {
1676 bsi->bsi_status = SLAPD_ABANDON;
1677 return BACKSQL_AVL_STOP;
1678 }
1679
1680 #ifndef BACKSQL_ARBITRARY_KEY
1681 /* If paged results have already completed this objectClass, skip it */
1682 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
1683 if ( oc->bom_id < PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)op->o_pagedresults_state)->ps_cookie ) )
1684 {
1685 return BACKSQL_AVL_CONTINUE;
1686 }
1687 }
1688 #endif /* ! BACKSQL_ARBITRARY_KEY */
1689
1690 if ( bsi->bsi_n_candidates == -1 ) {
1691 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1692 "unchecked limit has been overcome\n" );
1693 /* should never get here */
1694 assert( 0 );
1695 bsi->bsi_status = LDAP_ADMINLIMIT_EXCEEDED;
1696 return BACKSQL_AVL_STOP;
1697 }
1698
1699 bsi->bsi_oc = oc;
1700 res = backsql_srch_query( bsi, &query );
1701 if ( res ) {
1702 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1703 "error while constructing query for objectclass \"%s\"\n",
1704 oc->bom_oc->soc_cname.bv_val );
1705 /*
1706 * FIXME: need to separate errors from legally
1707 * impossible filters
1708 */
1709 switch ( bsi->bsi_status ) {
1710 case LDAP_SUCCESS:
1711 case LDAP_UNDEFINED_TYPE:
1712 case LDAP_NO_SUCH_OBJECT:
1713 /* we are conservative... */
1714 default:
1715 bsi->bsi_status = LDAP_SUCCESS;
1716 /* try next */
1717 return BACKSQL_AVL_CONTINUE;
1718
1719 case LDAP_ADMINLIMIT_EXCEEDED:
1720 case LDAP_OTHER:
1721 /* don't try any more */
1722 return BACKSQL_AVL_STOP;
1723 }
1724 }
1725
1726 if ( BER_BVISNULL( &query ) ) {
1727 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1728 "could not construct query for objectclass \"%s\"\n",
1729 oc->bom_oc->soc_cname.bv_val );
1730 bsi->bsi_status = LDAP_SUCCESS;
1731 return BACKSQL_AVL_CONTINUE;
1732 }
1733
1734 Debug( LDAP_DEBUG_TRACE, "Constructed query: %s\n",
1735 query.bv_val );
1736
1737 rc = backsql_Prepare( bsi->bsi_dbh, &sth, query.bv_val, 0 );
1738 bsi->bsi_op->o_tmpfree( query.bv_val, bsi->bsi_op->o_tmpmemctx );
1739 BER_BVZERO( &query );
1740 if ( rc != SQL_SUCCESS ) {
1741 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1742 "error preparing query\n" );
1743 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1744 bsi->bsi_status = LDAP_OTHER;
1745 return BACKSQL_AVL_CONTINUE;
1746 }
1747
1748 Debug( LDAP_DEBUG_TRACE, "id: '" BACKSQL_IDNUMFMT "'\n",
1749 bsi->bsi_oc->bom_id );
1750
1751 rc = backsql_BindParamNumID( sth, 1, SQL_PARAM_INPUT,
1752 &bsi->bsi_oc->bom_id );
1753 if ( rc != SQL_SUCCESS ) {
1754 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1755 "error binding objectclass id parameter\n" );
1756 bsi->bsi_status = LDAP_OTHER;
1757 return BACKSQL_AVL_CONTINUE;
1758 }
1759
1760 switch ( bsi->bsi_scope ) {
1761 case LDAP_SCOPE_BASE:
1762 case BACKSQL_SCOPE_BASE_LIKE:
1763 /*
1764 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1765 * however this should be handled earlier
1766 */
1767 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1768 bsi->bsi_status = LDAP_OTHER;
1769 return BACKSQL_AVL_CONTINUE;
1770 }
1771
1772 AC_MEMCPY( tmp_base_ndn, bsi->bsi_base_ndn->bv_val,
1773 bsi->bsi_base_ndn->bv_len + 1 );
1774
1775 /* uppercase DN only if the stored DN can be uppercased
1776 * for comparison */
1777 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1778 ldap_pvt_str2upper( tmp_base_ndn );
1779 }
1780
1781 Debug( LDAP_DEBUG_TRACE, "(base)dn: \"%s\"\n",
1782 tmp_base_ndn );
1783
1784 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1785 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1786 if ( rc != SQL_SUCCESS ) {
1787 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1788 "error binding base_ndn parameter\n" );
1789 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh,
1790 sth, rc );
1791 bsi->bsi_status = LDAP_OTHER;
1792 return BACKSQL_AVL_CONTINUE;
1793 }
1794 break;
1795
1796 case LDAP_SCOPE_SUBORDINATE:
1797 case LDAP_SCOPE_SUBTREE:
1798 {
1799 /* if short-cutting the search base,
1800 * don't bind any parameter */
1801 if ( bsi->bsi_use_subtree_shortcut ) {
1802 break;
1803 }
1804
1805 /*
1806 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1807 * however this should be handled earlier
1808 */
1809 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1810 bsi->bsi_status = LDAP_OTHER;
1811 return BACKSQL_AVL_CONTINUE;
1812 }
1813
1814 /*
1815 * Sets the parameters for the SQL built earlier
1816 * NOTE that all the databases could actually use
1817 * the TimesTen version, which would be cleaner
1818 * and would also eliminate the need for the
1819 * subtree_cond line in the configuration file.
1820 * For now, I'm leaving it the way it is,
1821 * so non-TimesTen databases use the original code.
1822 * But at some point this should get cleaned up.
1823 *
1824 * If "dn" is being used, do a suffix search.
1825 * If "dn_ru" is being used, do a prefix search.
1826 */
1827 if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
1828 tmp_base_ndn[ 0 ] = '\0';
1829
1830 for ( i = 0, j = bsi->bsi_base_ndn->bv_len - 1;
1831 j >= 0; i++, j--) {
1832 tmp_base_ndn[ i ] = bsi->bsi_base_ndn->bv_val[ j ];
1833 }
1834
1835 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1836 tmp_base_ndn[ i++ ] = ',';
1837 }
1838
1839 tmp_base_ndn[ i ] = '%';
1840 tmp_base_ndn[ i + 1 ] = '\0';
1841
1842 } else {
1843 i = 0;
1844
1845 tmp_base_ndn[ i++ ] = '%';
1846
1847 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1848 tmp_base_ndn[ i++ ] = ',';
1849 }
1850
1851 AC_MEMCPY( &tmp_base_ndn[ i ], bsi->bsi_base_ndn->bv_val,
1852 bsi->bsi_base_ndn->bv_len + 1 );
1853 }
1854
1855 /* uppercase DN only if the stored DN can be uppercased
1856 * for comparison */
1857 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1858 ldap_pvt_str2upper( tmp_base_ndn );
1859 }
1860
1861 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1862 Debug( LDAP_DEBUG_TRACE, "(children)dn: \"%s\"\n",
1863 tmp_base_ndn );
1864 } else {
1865 Debug( LDAP_DEBUG_TRACE, "(sub)dn: \"%s\"\n",
1866 tmp_base_ndn );
1867 }
1868
1869 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1870 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1871 if ( rc != SQL_SUCCESS ) {
1872 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1873 "error binding base_ndn parameter (2)\n" );
1874 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh,
1875 sth, rc );
1876 bsi->bsi_status = LDAP_OTHER;
1877 return BACKSQL_AVL_CONTINUE;
1878 }
1879 break;
1880 }
1881
1882 case LDAP_SCOPE_ONELEVEL:
1883 assert( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) );
1884
1885 Debug( LDAP_DEBUG_TRACE, "(one)id=" BACKSQL_IDFMT "\n",
1886 BACKSQL_IDARG(bsi->bsi_base_id.eid_id) );
1887 rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT,
1888 &bsi->bsi_base_id.eid_id );
1889 if ( rc != SQL_SUCCESS ) {
1890 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1891 "error binding base id parameter\n" );
1892 bsi->bsi_status = LDAP_OTHER;
1893 return BACKSQL_AVL_CONTINUE;
1894 }
1895 break;
1896 }
1897
1898 rc = SQLExecute( sth );
1899 if ( !BACKSQL_SUCCESS( rc ) ) {
1900 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1901 "error executing query\n" );
1902 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1903 SQLFreeStmt( sth, SQL_DROP );
1904 bsi->bsi_status = LDAP_OTHER;
1905 return BACKSQL_AVL_CONTINUE;
1906 }
1907
1908 backsql_BindRowAsStrings_x( sth, &row, bsi->bsi_op->o_tmpmemctx );
1909 rc = SQLFetch( sth );
1910 for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
1911 struct berval dn, pdn, ndn;
1912 backsql_entryID *c_id = NULL;
1913 int ret;
1914
1915 ber_str2bv( row.cols[ 3 ], 0, 0, &dn );
1916
1917 if ( backsql_api_odbc2dn( bsi->bsi_op, bsi->bsi_rs, &dn ) ) {
1918 continue;
1919 }
1920
1921 ret = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
1922 if ( dn.bv_val != row.cols[ 3 ] ) {
1923 free( dn.bv_val );
1924 }
1925
1926 if ( ret != LDAP_SUCCESS ) {
1927 continue;
1928 }
1929
1930 if ( bi->sql_baseObject && dn_match( &ndn, &bi->sql_baseObject->e_nname ) ) {
1931 goto cleanup;
1932 }
1933
1934 c_id = (backsql_entryID *)op->o_tmpcalloc( 1,
1935 sizeof( backsql_entryID ), op->o_tmpmemctx );
1936 #ifdef BACKSQL_ARBITRARY_KEY
1937 ber_str2bv_x( row.cols[ 0 ], 0, 1, &c_id->eid_id,
1938 op->o_tmpmemctx );
1939 ber_str2bv_x( row.cols[ 1 ], 0, 1, &c_id->eid_keyval,
1940 op->o_tmpmemctx );
1941 #else /* ! BACKSQL_ARBITRARY_KEY */
1942 if ( BACKSQL_STR2ID( &c_id->eid_id, row.cols[ 0 ], 0 ) != 0 ) {
1943 goto cleanup;
1944 }
1945 if ( BACKSQL_STR2ID( &c_id->eid_keyval, row.cols[ 1 ], 0 ) != 0 ) {
1946 goto cleanup;
1947 }
1948 #endif /* ! BACKSQL_ARBITRARY_KEY */
1949 c_id->eid_oc = bsi->bsi_oc;
1950 c_id->eid_oc_id = bsi->bsi_oc->bom_id;
1951
1952 c_id->eid_dn = pdn;
1953 c_id->eid_ndn = ndn;
1954
1955 /* append at end of list ... */
1956 c_id->eid_next = NULL;
1957 *bsi->bsi_id_listtail = c_id;
1958 bsi->bsi_id_listtail = &c_id->eid_next;
1959
1960 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1961 "added entry id=" BACKSQL_IDFMT " keyval=" BACKSQL_IDFMT " dn=\"%s\"\n",
1962 BACKSQL_IDARG(c_id->eid_id),
1963 BACKSQL_IDARG(c_id->eid_keyval),
1964 row.cols[ 3 ] );
1965
1966 /* count candidates, for unchecked limit */
1967 bsi->bsi_n_candidates--;
1968 if ( bsi->bsi_n_candidates == -1 ) {
1969 break;
1970 }
1971 continue;
1972
1973 cleanup:;
1974 if ( !BER_BVISNULL( &pdn ) ) {
1975 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
1976 }
1977 if ( !BER_BVISNULL( &ndn ) ) {
1978 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
1979 }
1980 if ( c_id != NULL ) {
1981 ch_free( c_id );
1982 }
1983 }
1984 backsql_FreeRow_x( &row, bsi->bsi_op->o_tmpmemctx );
1985 SQLFreeStmt( sth, SQL_DROP );
1986
1987 Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates(): %d\n",
1988 n_candidates - bsi->bsi_n_candidates );
1989
1990 return ( bsi->bsi_n_candidates == -1 ? BACKSQL_AVL_STOP : BACKSQL_AVL_CONTINUE );
1991 }
1992
1993 int
backsql_search(Operation * op,SlapReply * rs)1994 backsql_search( Operation *op, SlapReply *rs )
1995 {
1996 backsql_info *bi = (backsql_info *)op->o_bd->be_private;
1997 SQLHDBC dbh = SQL_NULL_HDBC;
1998 int sres;
1999 Entry user_entry = { 0 },
2000 base_entry = { 0 };
2001 int manageDSAit = get_manageDSAit( op );
2002 time_t stoptime = 0;
2003 backsql_srch_info bsi = { 0 };
2004 backsql_entryID *eid = NULL;
2005 struct berval nbase = BER_BVNULL;
2006 #ifndef BACKSQL_ARBITRARY_KEY
2007 ID lastid = 0;
2008 #endif /* ! BACKSQL_ARBITRARY_KEY */
2009
2010 Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): "
2011 "base=\"%s\", filter=\"%s\", scope=%d,",
2012 op->o_req_ndn.bv_val,
2013 op->ors_filterstr.bv_val,
2014 op->ors_scope );
2015 Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, "
2016 "attributes to load: %s\n",
2017 op->ors_deref,
2018 op->ors_attrsonly,
2019 op->ors_attrs == NULL ? "all" : "custom list" );
2020
2021 if ( op->o_req_ndn.bv_len > BACKSQL_MAX_DN_LEN ) {
2022 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2023 "search base length (%ld) exceeds max length (%d)\n",
2024 op->o_req_ndn.bv_len, BACKSQL_MAX_DN_LEN );
2025 /*
2026 * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate
2027 * since it is impossible that such a long DN exists
2028 * in the backend
2029 */
2030 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
2031 send_ldap_result( op, rs );
2032 return 1;
2033 }
2034
2035 sres = backsql_get_db_conn( op, &dbh );
2036 if ( sres != LDAP_SUCCESS ) {
2037 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2038 "could not get connection handle - exiting\n" );
2039 rs->sr_err = sres;
2040 rs->sr_text = sres == LDAP_OTHER ? "SQL-backend error" : NULL;
2041 send_ldap_result( op, rs );
2042 return 1;
2043 }
2044
2045 /* compute it anyway; root does not use it */
2046 stoptime = op->o_time + op->ors_tlimit;
2047
2048 /* init search */
2049 bsi.bsi_e = &base_entry;
2050 rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn,
2051 op->ors_scope,
2052 stoptime, op->ors_filter,
2053 dbh, op, rs, op->ors_attrs,
2054 ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) );
2055 switch ( rs->sr_err ) {
2056 case LDAP_SUCCESS:
2057 break;
2058
2059 case LDAP_REFERRAL:
2060 if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
2061 dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) )
2062 {
2063 rs->sr_err = LDAP_SUCCESS;
2064 rs->sr_text = NULL;
2065 rs->sr_matched = NULL;
2066 if ( rs->sr_ref ) {
2067 ber_bvarray_free( rs->sr_ref );
2068 rs->sr_ref = NULL;
2069 }
2070 break;
2071 }
2072
2073 /* an entry was created; free it */
2074 entry_clean( bsi.bsi_e );
2075
2076 /* fall thru */
2077
2078 default:
2079 if ( !BER_BVISNULL( &base_entry.e_nname )
2080 && !access_allowed( op, &base_entry,
2081 slap_schema.si_ad_entry, NULL,
2082 ACL_DISCLOSE, NULL ) )
2083 {
2084 rs->sr_err = LDAP_NO_SUCH_OBJECT;
2085 if ( rs->sr_ref ) {
2086 ber_bvarray_free( rs->sr_ref );
2087 rs->sr_ref = NULL;
2088 }
2089 rs->sr_matched = NULL;
2090 rs->sr_text = NULL;
2091 }
2092
2093 send_ldap_result( op, rs );
2094
2095 if ( rs->sr_ref ) {
2096 ber_bvarray_free( rs->sr_ref );
2097 rs->sr_ref = NULL;
2098 }
2099
2100 if ( !BER_BVISNULL( &base_entry.e_nname ) ) {
2101 entry_clean( &base_entry );
2102 }
2103
2104 goto done;
2105 }
2106 /* NOTE: __NEW__ "search" access is required
2107 * on searchBase object */
2108 {
2109 slap_mask_t mask;
2110
2111 if ( get_assert( op ) &&
2112 ( test_filter( op, &base_entry, get_assertion( op ) )
2113 != LDAP_COMPARE_TRUE ) )
2114 {
2115 rs->sr_err = LDAP_ASSERTION_FAILED;
2116
2117 }
2118 if ( ! access_allowed_mask( op, &base_entry,
2119 slap_schema.si_ad_entry,
2120 NULL, ACL_SEARCH, NULL, &mask ) )
2121 {
2122 if ( rs->sr_err == LDAP_SUCCESS ) {
2123 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
2124 }
2125 }
2126
2127 if ( rs->sr_err != LDAP_SUCCESS ) {
2128 if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
2129 rs->sr_err = LDAP_NO_SUCH_OBJECT;
2130 rs->sr_text = NULL;
2131 }
2132 send_ldap_result( op, rs );
2133 goto done;
2134 }
2135 }
2136
2137 bsi.bsi_e = NULL;
2138
2139 bsi.bsi_n_candidates =
2140 ( op->ors_limit == NULL /* isroot == TRUE */ ? -2 :
2141 ( op->ors_limit->lms_s_unchecked == -1 ? -2 :
2142 ( op->ors_limit->lms_s_unchecked ) ) );
2143
2144 #ifndef BACKSQL_ARBITRARY_KEY
2145 /* If paged results are in effect, check the paging cookie */
2146 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
2147 rs->sr_err = parse_paged_cookie( op, rs );
2148 if ( rs->sr_err != LDAP_SUCCESS ) {
2149 send_ldap_result( op, rs );
2150 goto done;
2151 }
2152 }
2153 #endif /* ! BACKSQL_ARBITRARY_KEY */
2154
2155 switch ( bsi.bsi_scope ) {
2156 case LDAP_SCOPE_BASE:
2157 case BACKSQL_SCOPE_BASE_LIKE:
2158 /*
2159 * probably already found...
2160 */
2161 bsi.bsi_id_list = &bsi.bsi_base_id;
2162 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
2163 break;
2164
2165 case LDAP_SCOPE_SUBTREE:
2166 /*
2167 * if baseObject is defined, and if it is the root
2168 * of the search, add it to the candidate list
2169 */
2170 if ( bi->sql_baseObject && BACKSQL_IS_BASEOBJECT_ID( &bsi.bsi_base_id.eid_id ) )
2171 {
2172 bsi.bsi_id_list = &bsi.bsi_base_id;
2173 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
2174 }
2175
2176 /* FALLTHRU */
2177 default:
2178
2179 /*
2180 * for each objectclass we try to construct query which gets IDs
2181 * of entries matching LDAP query filter and scope (or at least
2182 * candidates), and get the IDs. Do this in ID order for paging.
2183 */
2184 ldap_avl_apply( bi->sql_oc_by_id, backsql_oc_get_candidates,
2185 &bsi, BACKSQL_AVL_STOP, AVL_INORDER );
2186
2187 /* check for abandon */
2188 if ( op->o_abandon ) {
2189 eid = bsi.bsi_id_list;
2190 rs->sr_err = SLAPD_ABANDON;
2191 goto send_results;
2192 }
2193 }
2194
2195 if ( op->ors_limit != NULL /* isroot == FALSE */
2196 && op->ors_limit->lms_s_unchecked != -1
2197 && bsi.bsi_n_candidates == -1 )
2198 {
2199 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
2200 send_ldap_result( op, rs );
2201 goto done;
2202 }
2203
2204 /*
2205 * now we load candidate entries (only those attributes
2206 * mentioned in attrs and filter), test it against full filter
2207 * and then send to client; don't free entry_id if baseObject...
2208 */
2209 for ( eid = bsi.bsi_id_list;
2210 eid != NULL;
2211 eid = backsql_free_entryID(
2212 eid, eid == &bsi.bsi_base_id ? 0 : 1, op->o_tmpmemctx ) )
2213 {
2214 int rc;
2215 Attribute *a_hasSubordinate = NULL,
2216 *a_entryUUID = NULL,
2217 *a_entryCSN = NULL,
2218 **ap = NULL;
2219 Entry *e = NULL;
2220
2221 /* check for abandon */
2222 if ( op->o_abandon ) {
2223 rs->sr_err = SLAPD_ABANDON;
2224 goto send_results;
2225 }
2226
2227 /* check time limit */
2228 if ( op->ors_tlimit != SLAP_NO_LIMIT
2229 && slap_get_time() > stoptime )
2230 {
2231 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
2232 rs->sr_ctrls = NULL;
2233 rs->sr_ref = rs->sr_v2ref;
2234 goto send_results;
2235 }
2236
2237 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
2238 "for entry id=" BACKSQL_IDFMT " oc_id=" BACKSQL_IDNUMFMT ", keyval=" BACKSQL_IDFMT "\n",
2239 BACKSQL_IDARG(eid->eid_id),
2240 eid->eid_oc_id,
2241 BACKSQL_IDARG(eid->eid_keyval) );
2242
2243 /* check scope */
2244 switch ( op->ors_scope ) {
2245 case LDAP_SCOPE_BASE:
2246 case BACKSQL_SCOPE_BASE_LIKE:
2247 if ( !dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
2248 goto next_entry2;
2249 }
2250 break;
2251
2252 case LDAP_SCOPE_ONE:
2253 {
2254 struct berval rdn = eid->eid_ndn;
2255
2256 rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
2257 if ( !dnIsOneLevelRDN( &rdn ) ) {
2258 goto next_entry2;
2259 }
2260 /* fall thru */
2261 }
2262
2263 case LDAP_SCOPE_SUBORDINATE:
2264 /* discard the baseObject entry */
2265 if ( dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
2266 goto next_entry2;
2267 }
2268 /* FALLTHRU */
2269 case LDAP_SCOPE_SUBTREE:
2270 /* FIXME: this should never fail... */
2271 if ( !dnIsSuffix( &eid->eid_ndn, &op->o_req_ndn ) ) {
2272 goto next_entry2;
2273 }
2274 break;
2275 }
2276
2277 if ( BACKSQL_IS_BASEOBJECT_ID( &eid->eid_id ) ) {
2278 /* don't recollect baseObject... */
2279 e = bi->sql_baseObject;
2280
2281 } else if ( eid == &bsi.bsi_base_id ) {
2282 /* don't recollect searchBase object... */
2283 e = &base_entry;
2284
2285 } else {
2286 bsi.bsi_e = &user_entry;
2287 rc = backsql_id2entry( &bsi, eid );
2288 if ( rc != LDAP_SUCCESS ) {
2289 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2290 "error %d in backsql_id2entry() "
2291 "- skipping\n", rc );
2292 continue;
2293 }
2294 e = &user_entry;
2295 }
2296
2297 if ( !manageDSAit &&
2298 op->ors_scope != LDAP_SCOPE_BASE &&
2299 op->ors_scope != BACKSQL_SCOPE_BASE_LIKE &&
2300 is_entry_referral( e ) )
2301 {
2302 BerVarray refs;
2303
2304 refs = get_entry_referrals( op, e );
2305 if ( !refs ) {
2306 backsql_srch_info bsi2 = { 0 };
2307 Entry user_entry2 = { 0 };
2308
2309 /* retry with the full entry... */
2310 bsi2.bsi_e = &user_entry2;
2311 rc = backsql_init_search( &bsi2,
2312 &e->e_nname,
2313 LDAP_SCOPE_BASE,
2314 (time_t)(-1), NULL,
2315 dbh, op, rs, NULL,
2316 BACKSQL_ISF_GET_ENTRY );
2317 if ( rc == LDAP_SUCCESS ) {
2318 if ( is_entry_referral( &user_entry2 ) )
2319 {
2320 refs = get_entry_referrals( op,
2321 &user_entry2 );
2322 } else {
2323 rs->sr_err = LDAP_OTHER;
2324 }
2325 backsql_entry_clean( op, &user_entry2 );
2326 }
2327 if ( bsi2.bsi_attrs != NULL ) {
2328 op->o_tmpfree( bsi2.bsi_attrs,
2329 op->o_tmpmemctx );
2330 }
2331 }
2332
2333 if ( refs ) {
2334 rs->sr_ref = referral_rewrite( refs,
2335 &e->e_name,
2336 &op->o_req_dn,
2337 op->ors_scope );
2338 ber_bvarray_free( refs );
2339 }
2340
2341 if ( rs->sr_ref ) {
2342 rs->sr_err = LDAP_REFERRAL;
2343
2344 } else {
2345 rs->sr_text = "bad referral object";
2346 }
2347
2348 rs->sr_entry = e;
2349 rs->sr_matched = user_entry.e_name.bv_val;
2350 send_search_reference( op, rs );
2351
2352 ber_bvarray_free( rs->sr_ref );
2353 rs->sr_ref = NULL;
2354 rs->sr_matched = NULL;
2355 rs->sr_entry = NULL;
2356 if ( rs->sr_err == LDAP_REFERRAL ) {
2357 rs->sr_err = LDAP_SUCCESS;
2358 }
2359
2360 goto next_entry;
2361 }
2362
2363 /*
2364 * We use this flag since we need to parse the filter
2365 * anyway; we should have used the frontend API function
2366 * filter_has_subordinates()
2367 */
2368 if ( bsi.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) {
2369 rc = backsql_has_children( op, dbh, &e->e_nname );
2370
2371 switch ( rc ) {
2372 case LDAP_COMPARE_TRUE:
2373 case LDAP_COMPARE_FALSE:
2374 a_hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE );
2375 if ( a_hasSubordinate != NULL ) {
2376 for ( ap = &user_entry.e_attrs;
2377 *ap;
2378 ap = &(*ap)->a_next );
2379
2380 *ap = a_hasSubordinate;
2381 }
2382 rc = 0;
2383 break;
2384
2385 default:
2386 Debug(LDAP_DEBUG_TRACE,
2387 "backsql_search(): "
2388 "has_children failed( %d)\n",
2389 rc );
2390 rc = 1;
2391 goto next_entry;
2392 }
2393 }
2394
2395 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYUUID ) {
2396 a_entryUUID = backsql_operational_entryUUID( bi, eid );
2397 if ( a_entryUUID != NULL ) {
2398 if ( ap == NULL ) {
2399 ap = &user_entry.e_attrs;
2400 }
2401
2402 for ( ; *ap; ap = &(*ap)->a_next );
2403
2404 *ap = a_entryUUID;
2405 }
2406 }
2407
2408 #ifdef BACKSQL_SYNCPROV
2409 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYCSN ) {
2410 a_entryCSN = backsql_operational_entryCSN( op );
2411 if ( a_entryCSN != NULL ) {
2412 if ( ap == NULL ) {
2413 ap = &user_entry.e_attrs;
2414 }
2415
2416 for ( ; *ap; ap = &(*ap)->a_next );
2417
2418 *ap = a_entryCSN;
2419 }
2420 }
2421 #endif /* BACKSQL_SYNCPROV */
2422
2423 if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE )
2424 {
2425 #ifndef BACKSQL_ARBITRARY_KEY
2426 /* If paged results are in effect, see if the page limit was exceeded */
2427 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
2428 if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size )
2429 {
2430 e = NULL;
2431 send_paged_response( op, rs, &lastid );
2432 goto done;
2433 }
2434 lastid = SQL_TO_PAGECOOKIE( eid->eid_id, eid->eid_oc_id );
2435 }
2436 #endif /* ! BACKSQL_ARBITRARY_KEY */
2437 rs->sr_attrs = op->ors_attrs;
2438 rs->sr_operational_attrs = NULL;
2439 rs->sr_entry = e;
2440 e->e_private = (void *)eid;
2441 rs->sr_flags = ( e == &user_entry ) ? REP_ENTRY_MODIFIABLE : 0;
2442 /* FIXME: need the whole entry (ITS#3480) */
2443 rs->sr_err = send_search_entry( op, rs );
2444 e->e_private = NULL;
2445 rs->sr_entry = NULL;
2446 rs->sr_attrs = NULL;
2447 rs->sr_operational_attrs = NULL;
2448
2449 switch ( rs->sr_err ) {
2450 case LDAP_UNAVAILABLE:
2451 /*
2452 * FIXME: send_search_entry failed;
2453 * better stop
2454 */
2455 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2456 "connection lost\n" );
2457 goto end_of_search;
2458
2459 case LDAP_SIZELIMIT_EXCEEDED:
2460 case LDAP_BUSY:
2461 goto send_results;
2462 }
2463 }
2464
2465 next_entry:;
2466 if ( e == &user_entry ) {
2467 backsql_entry_clean( op, &user_entry );
2468 }
2469
2470 next_entry2:;
2471 }
2472
2473 end_of_search:;
2474 if ( rs->sr_nentries > 0 ) {
2475 rs->sr_ref = rs->sr_v2ref;
2476 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
2477 : LDAP_REFERRAL;
2478
2479 } else {
2480 rs->sr_err = bsi.bsi_status;
2481 }
2482
2483 send_results:;
2484 if ( rs->sr_err != SLAPD_ABANDON ) {
2485 #ifndef BACKSQL_ARBITRARY_KEY
2486 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
2487 send_paged_response( op, rs, NULL );
2488 } else
2489 #endif /* ! BACKSQL_ARBITRARY_KEY */
2490 {
2491 send_ldap_result( op, rs );
2492 }
2493 }
2494
2495 /* cleanup in case of abandon */
2496 for ( ; eid != NULL;
2497 eid = backsql_free_entryID(
2498 eid, eid == &bsi.bsi_base_id ? 0 : 1, op->o_tmpmemctx ) )
2499 ;
2500
2501 backsql_entry_clean( op, &base_entry );
2502
2503 /* in case we got here accidentally */
2504 backsql_entry_clean( op, &user_entry );
2505
2506 if ( rs->sr_v2ref ) {
2507 ber_bvarray_free( rs->sr_v2ref );
2508 rs->sr_v2ref = NULL;
2509 }
2510
2511 #ifdef BACKSQL_SYNCPROV
2512 if ( op->o_sync ) {
2513 Operation op2 = *op;
2514 SlapReply rs2 = { REP_RESULT };
2515 Entry *e = entry_alloc();
2516 slap_callback cb = { 0 };
2517
2518 op2.o_tag = LDAP_REQ_ADD;
2519 op2.o_bd = select_backend( &op->o_bd->be_nsuffix[0], 0 );
2520 op2.ora_e = e;
2521 op2.o_callback = &cb;
2522
2523 ber_dupbv( &e->e_name, op->o_bd->be_suffix );
2524 ber_dupbv( &e->e_nname, op->o_bd->be_nsuffix );
2525
2526 cb.sc_response = slap_null_cb;
2527
2528 op2.o_bd->be_add( &op2, &rs2 );
2529
2530 if ( op2.ora_e == e )
2531 entry_free( e );
2532 }
2533 #endif /* BACKSQL_SYNCPROV */
2534
2535 done:;
2536 (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
2537
2538 if ( bsi.bsi_attrs != NULL ) {
2539 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
2540 }
2541
2542 if ( !BER_BVISNULL( &nbase )
2543 && nbase.bv_val != op->o_req_ndn.bv_val )
2544 {
2545 ch_free( nbase.bv_val );
2546 }
2547
2548 /* restore scope ... FIXME: this should be done before ANY
2549 * frontend call that uses op */
2550 if ( op->ors_scope == BACKSQL_SCOPE_BASE_LIKE ) {
2551 op->ors_scope = LDAP_SCOPE_BASE;
2552 }
2553
2554 SQLTransact( SQL_NULL_HENV, dbh, SQL_ROLLBACK );
2555 Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n" );
2556
2557 return rs->sr_err;
2558 }
2559
2560 /* return LDAP_SUCCESS IFF we can retrieve the specified entry.
2561 */
2562 int
backsql_entry_get(Operation * op,struct berval * ndn,ObjectClass * oc,AttributeDescription * at,int rw,Entry ** ent)2563 backsql_entry_get(
2564 Operation *op,
2565 struct berval *ndn,
2566 ObjectClass *oc,
2567 AttributeDescription *at,
2568 int rw,
2569 Entry **ent )
2570 {
2571 backsql_srch_info bsi = { 0 };
2572 SQLHDBC dbh = SQL_NULL_HDBC;
2573 int rc;
2574 SlapReply rs = { 0 };
2575 AttributeName anlist[ 2 ];
2576
2577 *ent = NULL;
2578
2579 rc = backsql_get_db_conn( op, &dbh );
2580 if ( rc != LDAP_SUCCESS ) {
2581 return rc;
2582 }
2583
2584 if ( at ) {
2585 anlist[ 0 ].an_name = at->ad_cname;
2586 anlist[ 0 ].an_desc = at;
2587 BER_BVZERO( &anlist[ 1 ].an_name );
2588 }
2589
2590 bsi.bsi_e = entry_alloc();
2591 rc = backsql_init_search( &bsi,
2592 ndn,
2593 LDAP_SCOPE_BASE,
2594 (time_t)(-1), NULL,
2595 dbh, op, &rs, at ? anlist : NULL,
2596 BACKSQL_ISF_GET_ENTRY );
2597
2598 if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
2599 (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
2600 }
2601
2602 if ( rc == LDAP_SUCCESS ) {
2603
2604 #if 0 /* not supported at present */
2605 /* find attribute values */
2606 if ( is_entry_alias( bsi.bsi_e ) ) {
2607 Debug( LDAP_DEBUG_ACL,
2608 "<= backsql_entry_get: entry is an alias\n" );
2609 rc = LDAP_ALIAS_PROBLEM;
2610 goto return_results;
2611 }
2612 #endif
2613
2614 if ( is_entry_referral( bsi.bsi_e ) ) {
2615 Debug( LDAP_DEBUG_ACL,
2616 "<= backsql_entry_get: entry is a referral\n" );
2617 rc = LDAP_REFERRAL;
2618 goto return_results;
2619 }
2620
2621 if ( oc && !is_entry_objectclass( bsi.bsi_e, oc, 0 ) ) {
2622 Debug( LDAP_DEBUG_ACL,
2623 "<= backsql_entry_get: "
2624 "failed to find objectClass\n" );
2625 rc = LDAP_NO_SUCH_ATTRIBUTE;
2626 goto return_results;
2627 }
2628
2629 *ent = bsi.bsi_e;
2630 }
2631
2632 return_results:;
2633 if ( bsi.bsi_attrs != NULL ) {
2634 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
2635 }
2636
2637 if ( rc != LDAP_SUCCESS ) {
2638 if ( bsi.bsi_e ) {
2639 entry_free( bsi.bsi_e );
2640 }
2641 }
2642
2643 return rc;
2644 }
2645
2646 void
backsql_entry_clean(Operation * op,Entry * e)2647 backsql_entry_clean(
2648 Operation *op,
2649 Entry *e )
2650 {
2651 void *ctx;
2652
2653 ctx = ldap_pvt_thread_pool_context();
2654
2655 if ( ctx == NULL || ctx != op->o_tmpmemctx ) {
2656 if ( !BER_BVISNULL( &e->e_name ) ) {
2657 op->o_tmpfree( e->e_name.bv_val, op->o_tmpmemctx );
2658 BER_BVZERO( &e->e_name );
2659 }
2660
2661 if ( !BER_BVISNULL( &e->e_nname ) ) {
2662 op->o_tmpfree( e->e_nname.bv_val, op->o_tmpmemctx );
2663 BER_BVZERO( &e->e_nname );
2664 }
2665 }
2666
2667 entry_clean( e );
2668 }
2669
2670 int
backsql_entry_release(Operation * op,Entry * e,int rw)2671 backsql_entry_release(
2672 Operation *op,
2673 Entry *e,
2674 int rw )
2675 {
2676 backsql_entry_clean( op, e );
2677
2678 entry_free( e );
2679
2680 return 0;
2681 }
2682
2683 #ifndef BACKSQL_ARBITRARY_KEY
2684 /* This function is copied verbatim from back-bdb/search.c */
2685 static int
parse_paged_cookie(Operation * op,SlapReply * rs)2686 parse_paged_cookie( Operation *op, SlapReply *rs )
2687 {
2688 int rc = LDAP_SUCCESS;
2689 PagedResultsState *ps = op->o_pagedresults_state;
2690
2691 /* this function must be invoked only if the pagedResults
2692 * control has been detected, parsed and partially checked
2693 * by the frontend */
2694 assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
2695
2696 /* cookie decoding/checks deferred to backend... */
2697 if ( ps->ps_cookieval.bv_len ) {
2698 PagedResultsCookie reqcookie;
2699 if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) {
2700 /* bad cookie */
2701 rs->sr_text = "paged results cookie is invalid";
2702 rc = LDAP_PROTOCOL_ERROR;
2703 goto done;
2704 }
2705
2706 AC_MEMCPY( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie ));
2707
2708 if ( reqcookie > ps->ps_cookie ) {
2709 /* bad cookie */
2710 rs->sr_text = "paged results cookie is invalid";
2711 rc = LDAP_PROTOCOL_ERROR;
2712 goto done;
2713
2714 } else if ( reqcookie < ps->ps_cookie ) {
2715 rs->sr_text = "paged results cookie is invalid or old";
2716 rc = LDAP_UNWILLING_TO_PERFORM;
2717 goto done;
2718 }
2719
2720 } else {
2721 /* Initial request. Initialize state. */
2722 ps->ps_cookie = 0;
2723 ps->ps_count = 0;
2724 }
2725
2726 done:;
2727
2728 return rc;
2729 }
2730
2731 /* This function is copied nearly verbatim from back-bdb/search.c */
2732 static void
send_paged_response(Operation * op,SlapReply * rs,ID * lastid)2733 send_paged_response(
2734 Operation *op,
2735 SlapReply *rs,
2736 ID *lastid )
2737 {
2738 LDAPControl ctrl, *ctrls[2];
2739 BerElementBuffer berbuf;
2740 BerElement *ber = (BerElement *)&berbuf;
2741 PagedResultsCookie respcookie;
2742 struct berval cookie;
2743
2744 Debug(LDAP_DEBUG_ARGS,
2745 "send_paged_response: lastid=0x%08lx nentries=%d\n",
2746 lastid ? *lastid : 0, rs->sr_nentries );
2747
2748 BER_BVZERO( &ctrl.ldctl_value );
2749 ctrls[0] = &ctrl;
2750 ctrls[1] = NULL;
2751
2752 ber_init2( ber, NULL, LBER_USE_DER );
2753
2754 if ( lastid ) {
2755 respcookie = ( PagedResultsCookie )(*lastid);
2756 cookie.bv_len = sizeof( respcookie );
2757 cookie.bv_val = (char *)&respcookie;
2758
2759 } else {
2760 respcookie = ( PagedResultsCookie )0;
2761 BER_BVSTR( &cookie, "" );
2762 }
2763
2764 op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
2765 op->o_conn->c_pagedresults_state.ps_count =
2766 ((PagedResultsState *)op->o_pagedresults_state)->ps_count +
2767 rs->sr_nentries;
2768
2769 /* return size of 0 -- no estimate */
2770 ber_printf( ber, "{iO}", 0, &cookie );
2771
2772 if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
2773 goto done;
2774 }
2775
2776 ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
2777 ctrls[0]->ldctl_iscritical = 0;
2778
2779 rs->sr_ctrls = ctrls;
2780 rs->sr_err = LDAP_SUCCESS;
2781 send_ldap_result( op, rs );
2782 rs->sr_ctrls = NULL;
2783
2784 done:
2785 (void) ber_free_buf( ber );
2786 }
2787 #endif /* ! BACKSQL_ARBITRARY_KEY */
2788