1 /* search.cpp - tools for slap tools */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 *
5 * Copyright 2008-2021 The OpenLDAP Foundation.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
10 * Public License.
11 *
12 * A copy of this license is available in the file LICENSE in the
13 * top-level directory of the distribution or, alternatively, at
14 * <http://www.OpenLDAP.org/license.html>.
15 */
16 /* ACKNOWLEDGEMENTS:
17 * This work was initially developed by Howard Chu for inclusion
18 * in OpenLDAP Software. This work was sponsored by MySQL.
19 */
20
21 #include "portable.h"
22
23 #include <stdio.h>
24 #include <ac/string.h>
25 #include <ac/errno.h>
26
27 #include "lutil.h"
28
29 #include "back-ndb.h"
30
31 static int
ndb_dn2bound(NdbIndexScanOperation * myop,NdbRdns * rdns)32 ndb_dn2bound(
33 NdbIndexScanOperation *myop,
34 NdbRdns *rdns
35 )
36 {
37 unsigned int i;
38
39 /* Walk thru RDNs */
40 for ( i=0; i<rdns->nr_num; i++ ) {
41 /* Note: RDN_COLUMN offset not needed here */
42 if ( myop->setBound( i, NdbIndexScanOperation::BoundEQ, rdns->nr_buf[i] ))
43 return LDAP_OTHER;
44 }
45 return i;
46 }
47
48 /* Check that all filter terms reside in the same table.
49 *
50 * If any of the filter terms are indexed, then only an IndexScan of the OL_index
51 * will be performed. If none are indexed, but all the terms reside in a single
52 * table, a Scan can be performed with the LDAP filter transformed into a ScanFilter.
53 *
54 * Otherwise, a full scan of the DB must be done with all filtering done by slapd.
55 */
ndb_filter_check(struct ndb_info * ni,Filter * f,NdbOcInfo ** oci,int * indexed,int * ocfilter)56 static int ndb_filter_check( struct ndb_info *ni, Filter *f,
57 NdbOcInfo **oci, int *indexed, int *ocfilter )
58 {
59 AttributeDescription *ad = NULL;
60 ber_tag_t choice = f->f_choice;
61 int rc = 0, undef = 0;
62
63 if ( choice & SLAPD_FILTER_UNDEFINED ) {
64 choice &= SLAPD_FILTER_MASK;
65 undef = 1;
66 }
67 switch( choice ) {
68 case LDAP_FILTER_AND:
69 case LDAP_FILTER_OR:
70 case LDAP_FILTER_NOT:
71 for ( f = f->f_list; f; f=f->f_next ) {
72 rc = ndb_filter_check( ni, f, oci, indexed, ocfilter );
73 if ( rc ) return rc;
74 }
75 break;
76 case LDAP_FILTER_PRESENT:
77 ad = f->f_desc;
78 break;
79 case LDAP_FILTER_EQUALITY:
80 case LDAP_FILTER_SUBSTRINGS:
81 case LDAP_FILTER_GE:
82 case LDAP_FILTER_LE:
83 case LDAP_FILTER_APPROX:
84 ad = f->f_av_desc;
85 break;
86 default:
87 break;
88 }
89 if ( ad && !undef ) {
90 NdbAttrInfo *ai;
91 /* ObjectClass filtering is in dn2id table */
92 if ( ad == slap_schema.si_ad_objectClass ) {
93 if ( choice == LDAP_FILTER_EQUALITY )
94 (*ocfilter)++;
95 return 0;
96 }
97 ai = ndb_ai_find( ni, ad->ad_type );
98 if ( ai ) {
99 if ( ai->na_flag & NDB_INFO_INDEX )
100 (*indexed)++;
101 if ( *oci ) {
102 if ( ai->na_oi != *oci )
103 rc = -1;
104 } else {
105 *oci = ai->na_oi;
106 }
107 }
108 }
109 return rc;
110 }
111
ndb_filter_set(Operation * op,struct ndb_info * ni,Filter * f,int indexed,NdbIndexScanOperation * scan,NdbScanFilter * sf,int * bounds)112 static int ndb_filter_set( Operation *op, struct ndb_info *ni, Filter *f, int indexed,
113 NdbIndexScanOperation *scan, NdbScanFilter *sf, int *bounds )
114 {
115 AttributeDescription *ad = NULL;
116 ber_tag_t choice = f->f_choice;
117 int undef = 0;
118
119 if ( choice & SLAPD_FILTER_UNDEFINED ) {
120 choice &= SLAPD_FILTER_MASK;
121 undef = 1;
122 }
123 switch( choice ) {
124 case LDAP_FILTER_NOT:
125 /* no indexing for these */
126 break;
127 case LDAP_FILTER_OR:
128 /* FIXME: these bounds aren't right. */
129 if ( indexed ) {
130 scan->end_of_bound( (*bounds)++ );
131 }
132 case LDAP_FILTER_AND:
133 if ( sf ) {
134 sf->begin( choice == LDAP_FILTER_OR ? NdbScanFilter::OR : NdbScanFilter::AND );
135 }
136 for ( f = f->f_list; f; f=f->f_next ) {
137 if ( ndb_filter_set( op, ni, f, indexed, scan, sf, bounds ))
138 return -1;
139 }
140 if ( sf ) {
141 sf->end();
142 }
143 break;
144 case LDAP_FILTER_PRESENT:
145 ad = f->f_desc;
146 break;
147 case LDAP_FILTER_EQUALITY:
148 case LDAP_FILTER_SUBSTRINGS:
149 case LDAP_FILTER_GE:
150 case LDAP_FILTER_LE:
151 case LDAP_FILTER_APPROX:
152 ad = f->f_av_desc;
153 break;
154 default:
155 break;
156 }
157 if ( ad && !undef ) {
158 NdbAttrInfo *ai;
159 /* ObjectClass filtering is in dn2id table */
160 if ( ad == slap_schema.si_ad_objectClass ) {
161 return 0;
162 }
163 ai = ndb_ai_find( ni, ad->ad_type );
164 if ( ai ) {
165 int rc;
166 if ( ai->na_flag & NDB_INFO_INDEX ) {
167 char *buf, *ptr;
168 NdbIndexScanOperation::BoundType bt;
169
170 switch(choice) {
171 case LDAP_FILTER_PRESENT:
172 rc = scan->setBound( ai->na_ixcol - IDX_COLUMN,
173 NdbIndexScanOperation::BoundGT, NULL );
174 break;
175 case LDAP_FILTER_EQUALITY:
176 case LDAP_FILTER_APPROX:
177 bt = NdbIndexScanOperation::BoundEQ;
178 goto setit;
179 case LDAP_FILTER_GE:
180 bt = NdbIndexScanOperation::BoundGE;
181 goto setit;
182 case LDAP_FILTER_LE:
183 bt = NdbIndexScanOperation::BoundLE;
184 setit:
185 rc = f->f_av_value.bv_len+1;
186 if ( ai->na_len > 255 )
187 rc++;
188 buf = (char *)op->o_tmpalloc( rc, op->o_tmpmemctx );
189 rc = f->f_av_value.bv_len;
190 buf[0] = rc & 0xff;
191 ptr = buf+1;
192 if ( ai->na_len > 255 ) {
193 buf[1] = (rc >> 8);
194 ptr++;
195 }
196 memcpy( ptr, f->f_av_value.bv_val, f->f_av_value.bv_len );
197 rc = scan->setBound( ai->na_ixcol - IDX_COLUMN, bt, buf );
198 op->o_tmpfree( buf, op->o_tmpmemctx );
199 break;
200 default:
201 break;
202 }
203 } else if ( sf ) {
204 char *buf, *ptr;
205 NdbScanFilter::BinaryCondition bc;
206
207 switch(choice) {
208 case LDAP_FILTER_PRESENT:
209 rc = sf->isnotnull( ai->na_column );
210 break;
211 case LDAP_FILTER_EQUALITY:
212 case LDAP_FILTER_APPROX:
213 bc = NdbScanFilter::COND_EQ;
214 goto setf;
215 case LDAP_FILTER_GE:
216 bc = NdbScanFilter::COND_GE;
217 goto setf;
218 case LDAP_FILTER_LE:
219 bc = NdbScanFilter::COND_LE;
220 setf:
221 rc = sf->cmp( bc, ai->na_column, f->f_av_value.bv_val, f->f_av_value.bv_len );
222 break;
223 case LDAP_FILTER_SUBSTRINGS:
224 rc = 0;
225 if ( f->f_sub_initial.bv_val )
226 rc += f->f_sub_initial.bv_len + 1;
227 if ( f->f_sub_any ) {
228 int i;
229 if ( !rc ) rc++;
230 for (i=0; f->f_sub_any[i].bv_val; i++)
231 rc += f->f_sub_any[i].bv_len + 1;
232 }
233 if ( f->f_sub_final.bv_val ) {
234 if ( !rc ) rc++;
235 rc += f->f_sub_final.bv_len;
236 }
237 buf = (char *)op->o_tmpalloc( rc+1, op->o_tmpmemctx );
238 ptr = buf;
239 if ( f->f_sub_initial.bv_val ) {
240 memcpy( ptr, f->f_sub_initial.bv_val, f->f_sub_initial.bv_len );
241 ptr += f->f_sub_initial.bv_len;
242 *ptr++ = '%';
243 }
244 if ( f->f_sub_any ) {
245 int i;
246 if ( ptr == buf )
247 *ptr++ = '%';
248 for (i=0; f->f_sub_any[i].bv_val; i++) {
249 memcpy( ptr, f->f_sub_any[i].bv_val, f->f_sub_any[i].bv_len );
250 ptr += f->f_sub_any[i].bv_len;
251 *ptr++ = '%';
252 }
253 }
254 if ( f->f_sub_final.bv_val ) {
255 if ( ptr == buf )
256 *ptr++ = '%';
257 memcpy( ptr, f->f_sub_final.bv_val, f->f_sub_final.bv_len );
258 ptr += f->f_sub_final.bv_len;
259 }
260 *ptr = '\0';
261 rc = sf->cmp( NdbScanFilter::COND_LIKE, ai->na_column, buf, ptr - buf );
262 op->o_tmpfree( buf, op->o_tmpmemctx );
263 break;
264 }
265 }
266 }
267 }
268 return 0;
269 }
270
ndb_oc_search(Operation * op,SlapReply * rs,Ndb * ndb,NdbTransaction * txn,NdbRdns * rbase,NdbOcInfo * oci,int indexed)271 static int ndb_oc_search( Operation *op, SlapReply *rs, Ndb *ndb, NdbTransaction *txn,
272 NdbRdns *rbase, NdbOcInfo *oci, int indexed )
273 {
274 struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
275 const NdbDictionary::Dictionary *myDict = ndb->getDictionary();
276 const NdbDictionary::Table *myTable;
277 const NdbDictionary::Index *myIndex;
278 NdbIndexScanOperation *scan;
279 NdbIndexOperation *ixop;
280 NdbScanFilter *sf = NULL;
281 struct berval *ocs;
282 NdbRecAttr *scanID, *scanOC, *scanDN[NDB_MAX_RDNS];
283 char dnBuf[2048], *ptr;
284 NdbRdns rdns;
285 NdbArgs NA;
286 char idbuf[2*sizeof(ID)];
287 char ocbuf[NDB_OC_BUFLEN];
288 int i, rc, bounds;
289 Entry e = {0};
290 Uint64 eid;
291 time_t stoptime;
292 int manageDSAit;
293
294 stoptime = op->o_time + op->ors_tlimit;
295 manageDSAit = get_manageDSAit( op );
296
297 myTable = myDict->getTable( oci->no_table.bv_val );
298 if ( indexed ) {
299 scan = txn->getNdbIndexScanOperation( INDEX_NAME, DN2ID_TABLE );
300 if ( !scan )
301 return LDAP_OTHER;
302 scan->readTuples( NdbOperation::LM_CommittedRead );
303 } else {
304 myIndex = myDict->getIndex( "eid$unique", DN2ID_TABLE );
305 if ( !myIndex ) {
306 Debug( LDAP_DEBUG_ANY, DN2ID_TABLE " eid index is missing!\n", 0, 0, 0 );
307 rs->sr_err = LDAP_OTHER;
308 goto leave;
309 }
310 scan = (NdbIndexScanOperation *)txn->getNdbScanOperation( myTable );
311 if ( !scan )
312 return LDAP_OTHER;
313 scan->readTuples( NdbOperation::LM_CommittedRead );
314 #if 1
315 sf = new NdbScanFilter(scan);
316 if ( !sf )
317 return LDAP_OTHER;
318 switch ( op->ors_filter->f_choice ) {
319 case LDAP_FILTER_AND:
320 case LDAP_FILTER_OR:
321 case LDAP_FILTER_NOT:
322 break;
323 default:
324 if ( sf->begin() < 0 ) {
325 rc = LDAP_OTHER;
326 goto leave;
327 }
328 }
329 #endif
330 }
331
332 bounds = 0;
333 rc = ndb_filter_set( op, ni, op->ors_filter, indexed, scan, sf, &bounds );
334 if ( rc )
335 goto leave;
336 if ( sf ) sf->end();
337
338 scanID = scan->getValue( EID_COLUMN, idbuf );
339 if ( indexed ) {
340 scanOC = scan->getValue( OCS_COLUMN, ocbuf );
341 for ( i=0; i<NDB_MAX_RDNS; i++ ) {
342 rdns.nr_buf[i][0] = '\0';
343 scanDN[i] = scan->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
344 }
345 }
346
347 if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) {
348 rs->sr_err = LDAP_OTHER;
349 goto leave;
350 }
351
352 e.e_name.bv_val = dnBuf;
353 NA.e = &e;
354 NA.ndb = ndb;
355 while ( scan->nextResult( true, true ) == 0 ) {
356 NdbTransaction *tx2;
357 if ( op->o_abandon ) {
358 rs->sr_err = SLAPD_ABANDON;
359 break;
360 }
361 if ( slapd_shutdown ) {
362 rs->sr_err = LDAP_UNAVAILABLE;
363 break;
364 }
365 if ( op->ors_tlimit != SLAP_NO_LIMIT &&
366 slap_get_time() > stoptime ) {
367 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
368 break;
369 }
370
371 eid = scanID->u_64_value();
372 e.e_id = eid;
373 if ( !indexed ) {
374 tx2 = ndb->startTransaction( myTable );
375 if ( !tx2 ) {
376 rs->sr_err = LDAP_OTHER;
377 goto leave;
378 }
379
380 ixop = tx2->getNdbIndexOperation( myIndex );
381 if ( !ixop ) {
382 tx2->close();
383 rs->sr_err = LDAP_OTHER;
384 goto leave;
385 }
386 ixop->readTuple( NdbOperation::LM_CommittedRead );
387 ixop->equal( EID_COLUMN, eid );
388
389 scanOC = ixop->getValue( OCS_COLUMN, ocbuf );
390 for ( i=0; i<NDB_MAX_RDNS; i++ ) {
391 rdns.nr_buf[i][0] = '\0';
392 scanDN[i] = ixop->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
393 }
394 rc = tx2->execute( NdbTransaction::Commit, NdbOperation::AbortOnError, 1 );
395 tx2->close();
396 if ( rc ) {
397 rs->sr_err = LDAP_OTHER;
398 goto leave;
399 }
400 }
401
402 ocs = ndb_ref2oclist( ocbuf, op->o_tmpmemctx );
403 for ( i=0; i<NDB_MAX_RDNS; i++ ) {
404 if ( scanDN[i]->isNULL() || !rdns.nr_buf[i][0] )
405 break;
406 }
407 rdns.nr_num = i;
408
409 /* entry must be subordinate to the base */
410 if ( i < rbase->nr_num ) {
411 continue;
412 }
413
414 ptr = dnBuf;
415 for ( --i; i>=0; i-- ) {
416 char *buf;
417 int len;
418 buf = rdns.nr_buf[i];
419 len = *buf++;
420 ptr = lutil_strncopy( ptr, buf, len );
421 if ( i ) *ptr++ = ',';
422 }
423 *ptr = '\0';
424 e.e_name.bv_len = ptr - dnBuf;
425
426 /* More scope checks */
427 /* If indexed, these can be moved into the ScanFilter */
428 switch( op->ors_scope ) {
429 case LDAP_SCOPE_ONELEVEL:
430 if ( rdns.nr_num != rbase->nr_num+1 )
431 continue;
432 case LDAP_SCOPE_SUBORDINATE:
433 if ( rdns.nr_num == rbase->nr_num )
434 continue;
435 case LDAP_SCOPE_SUBTREE:
436 default:
437 if ( e.e_name.bv_len <= op->o_req_dn.bv_len ) {
438 if ( op->ors_scope != LDAP_SCOPE_SUBTREE ||
439 strcasecmp( op->o_req_dn.bv_val, e.e_name.bv_val ))
440 continue;
441 } else if ( strcasecmp( op->o_req_dn.bv_val, e.e_name.bv_val +
442 e.e_name.bv_len - op->o_req_dn.bv_len ))
443 continue;
444 }
445
446 dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx );
447 {
448 #if 1 /* NDBapi was broken here but seems to work now */
449 Ndb::Key_part_ptr keys[2];
450 char xbuf[512];
451 keys[0].ptr = &eid;
452 keys[0].len = sizeof(eid);
453 keys[1].ptr = NULL;
454 keys[1].len = 0;
455 tx2 = ndb->startTransaction( myTable, keys, xbuf, sizeof(xbuf));
456 #else
457 tx2 = ndb->startTransaction( myTable );
458 #endif
459 if ( !tx2 ) {
460 rs->sr_err = LDAP_OTHER;
461 goto leave;
462 }
463 NA.txn = tx2;
464 NA.ocs = ocs;
465 rc = ndb_entry_get_data( op, &NA, 0 );
466 tx2->close();
467 }
468 ber_bvarray_free_x( ocs, op->o_tmpmemctx );
469 if ( !manageDSAit && is_entry_referral( &e )) {
470 BerVarray erefs = get_entry_referrals( op, &e );
471 rs->sr_ref = referral_rewrite( erefs, &e.e_name, NULL,
472 op->ors_scope == LDAP_SCOPE_ONELEVEL ?
473 LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
474 rc = send_search_reference( op, rs );
475 ber_bvarray_free( rs->sr_ref );
476 ber_bvarray_free( erefs );
477 rs->sr_ref = NULL;
478 } else if ( manageDSAit || !is_entry_glue( &e )) {
479 rc = test_filter( op, &e, op->ors_filter );
480 if ( rc == LDAP_COMPARE_TRUE ) {
481 rs->sr_entry = &e;
482 rs->sr_attrs = op->ors_attrs;
483 rs->sr_flags = 0;
484 rc = send_search_entry( op, rs );
485 rs->sr_entry = NULL;
486 rs->sr_attrs = NULL;
487 } else {
488 rc = 0;
489 }
490 }
491 attrs_free( e.e_attrs );
492 e.e_attrs = NULL;
493 op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx );
494 if ( rc ) break;
495 }
496 leave:
497 if ( sf ) delete sf;
498 return rc;
499 }
500
501 extern "C"
ndb_back_search(Operation * op,SlapReply * rs)502 int ndb_back_search( Operation *op, SlapReply *rs )
503 {
504 struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
505 NdbTransaction *txn;
506 NdbIndexScanOperation *scan;
507 NdbScanFilter *sf = NULL;
508 Entry e = {0};
509 int rc, i, ocfilter, indexed;
510 struct berval matched;
511 NdbRecAttr *scanID, *scanOC, *scanDN[NDB_MAX_RDNS];
512 char dnBuf[2048], *ptr;
513 char idbuf[2*sizeof(ID)];
514 char ocbuf[NDB_OC_BUFLEN];
515 NdbRdns rdns;
516 NdbOcInfo *oci;
517 NdbArgs NA;
518 slap_mask_t mask;
519 time_t stoptime;
520 int manageDSAit;
521
522 rc = ndb_thread_handle( op, &NA.ndb );
523 rdns.nr_num = 0;
524
525 manageDSAit = get_manageDSAit( op );
526
527 txn = NA.ndb->startTransaction();
528 if ( !txn ) {
529 Debug( LDAP_DEBUG_TRACE,
530 LDAP_XSTRING(ndb_back_search) ": startTransaction failed: %s (%d)\n",
531 NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
532 rs->sr_err = LDAP_OTHER;
533 rs->sr_text = "internal error";
534 goto leave;
535 }
536
537 NA.txn = txn;
538 e.e_name = op->o_req_dn;
539 e.e_nname = op->o_req_ndn;
540 NA.e = &e;
541 NA.rdns = &rdns;
542 NA.ocs = NULL;
543
544 rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
545 if ( rs->sr_err ) {
546 if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
547 rs->sr_matched = matched.bv_val;
548 if ( NA.ocs )
549 ndb_check_referral( op, rs, &NA );
550 }
551 goto leave;
552 }
553
554 if ( !access_allowed_mask( op, &e, slap_schema.si_ad_entry,
555 NULL, ACL_SEARCH, NULL, &mask )) {
556 if ( !ACL_GRANT( mask, ACL_DISCLOSE ))
557 rs->sr_err = LDAP_NO_SUCH_OBJECT;
558 else
559 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
560 ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
561 goto leave;
562 }
563
564 rs->sr_err = ndb_entry_get_data( op, &NA, 0 );
565 ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
566 if ( rs->sr_err )
567 goto leave;
568
569 if ( !manageDSAit && is_entry_referral( &e )) {
570 rs->sr_ref = get_entry_referrals( op, &e );
571 rs->sr_err = LDAP_REFERRAL;
572 if ( rs->sr_ref )
573 rs->sr_flags |= REP_REF_MUSTBEFREED;
574 rs->sr_matched = e.e_name.bv_val;
575 attrs_free( e.e_attrs );
576 e.e_attrs = NULL;
577 goto leave;
578 }
579
580 if ( !manageDSAit && is_entry_glue( &e )) {
581 rs->sr_err = LDAP_NO_SUCH_OBJECT;
582 goto leave;
583 }
584
585 if ( get_assert( op ) && test_filter( op, &e, (Filter *)get_assertion( op )) !=
586 LDAP_COMPARE_TRUE ) {
587 rs->sr_err = LDAP_ASSERTION_FAILED;
588 attrs_free( e.e_attrs );
589 e.e_attrs = NULL;
590 goto leave;
591 }
592
593 /* admin ignores tlimits */
594 stoptime = op->o_time + op->ors_tlimit;
595
596 if ( op->ors_scope == LDAP_SCOPE_BASE ) {
597 rc = test_filter( op, &e, op->ors_filter );
598 if ( rc == LDAP_COMPARE_TRUE ) {
599 rs->sr_entry = &e;
600 rs->sr_attrs = op->ors_attrs;
601 rs->sr_flags = 0;
602 send_search_entry( op, rs );
603 rs->sr_entry = NULL;
604 }
605 attrs_free( e.e_attrs );
606 e.e_attrs = NULL;
607 rs->sr_err = LDAP_SUCCESS;
608 goto leave;
609 } else {
610 attrs_free( e.e_attrs );
611 e.e_attrs = NULL;
612 if ( rdns.nr_num == NDB_MAX_RDNS ) {
613 if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ||
614 op->ors_scope == LDAP_SCOPE_CHILDREN )
615 rs->sr_err = LDAP_SUCCESS;
616 goto leave;
617 }
618 }
619
620 /* See if we can handle the filter. Filtering on objectClass is only done
621 * in the DN2ID table scan. If all other filter terms reside in one table,
622 * then we scan the OC table instead of the DN2ID table.
623 */
624 oci = NULL;
625 indexed = 0;
626 ocfilter = 0;
627 rc = ndb_filter_check( ni, op->ors_filter, &oci, &indexed, &ocfilter );
628 if ( rc ) {
629 Debug( LDAP_DEBUG_TRACE, "ndb_back_search: "
630 "filter attributes from multiple tables, indexing ignored\n",
631 0, 0, 0 );
632 } else if ( oci ) {
633 rc = ndb_oc_search( op, rs, NA.ndb, txn, &rdns, oci, indexed );
634 goto leave;
635 }
636
637 scan = txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
638 if ( !scan ) {
639 rs->sr_err = LDAP_OTHER;
640 goto leave;
641 }
642 scan->readTuples( NdbOperation::LM_CommittedRead );
643 rc = ndb_dn2bound( scan, &rdns );
644
645 /* TODO: if ( ocfilter ) set up scanfilter for objectclass matches
646 * column COND_LIKE "% <class> %"
647 */
648
649 switch( op->ors_scope ) {
650 case LDAP_SCOPE_ONELEVEL:
651 sf = new NdbScanFilter(scan);
652 if ( sf->begin() < 0 ||
653 sf->cmp(NdbScanFilter::COND_NOT_LIKE, rc+3, "_%",
654 STRLENOF("_%")) < 0 ||
655 sf->end() < 0 ) {
656 rs->sr_err = LDAP_OTHER;
657 goto leave;
658 }
659 /* FALLTHRU */
660 case LDAP_SCOPE_CHILDREN:
661 /* Note: RDN_COLUMN offset not needed here */
662 scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" );
663 /* FALLTHRU */
664 case LDAP_SCOPE_SUBTREE:
665 break;
666 }
667 scanID = scan->getValue( EID_COLUMN, idbuf );
668 scanOC = scan->getValue( OCS_COLUMN, ocbuf );
669 for ( i=0; i<NDB_MAX_RDNS; i++ ) {
670 rdns.nr_buf[i][0] = '\0';
671 scanDN[i] = scan->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
672 }
673 if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) {
674 rs->sr_err = LDAP_OTHER;
675 goto leave;
676 }
677
678 e.e_name.bv_val = dnBuf;
679 while ( scan->nextResult( true, true ) == 0 ) {
680 if ( op->o_abandon ) {
681 rs->sr_err = SLAPD_ABANDON;
682 break;
683 }
684 if ( slapd_shutdown ) {
685 rs->sr_err = LDAP_UNAVAILABLE;
686 break;
687 }
688 if ( op->ors_tlimit != SLAP_NO_LIMIT &&
689 slap_get_time() > stoptime ) {
690 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
691 break;
692 }
693 e.e_id = scanID->u_64_value();
694 NA.ocs = ndb_ref2oclist( ocbuf, op->o_tmpmemctx );
695 for ( i=0; i<NDB_MAX_RDNS; i++ ) {
696 if ( scanDN[i]->isNULL() || !rdns.nr_buf[i][0] )
697 break;
698 }
699 ptr = dnBuf;
700 rdns.nr_num = i;
701 for ( --i; i>=0; i-- ) {
702 char *buf;
703 int len;
704 buf = rdns.nr_buf[i];
705 len = *buf++;
706 ptr = lutil_strncopy( ptr, buf, len );
707 if ( i ) *ptr++ = ',';
708 }
709 *ptr = '\0';
710 e.e_name.bv_len = ptr - dnBuf;
711 dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx );
712 NA.txn = NA.ndb->startTransaction();
713 rc = ndb_entry_get_data( op, &NA, 0 );
714 NA.txn->close();
715 ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
716 if ( !manageDSAit && is_entry_referral( &e )) {
717 BerVarray erefs = get_entry_referrals( op, &e );
718 rs->sr_ref = referral_rewrite( erefs, &e.e_name, NULL,
719 op->ors_scope == LDAP_SCOPE_ONELEVEL ?
720 LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
721 rc = send_search_reference( op, rs );
722 ber_bvarray_free( rs->sr_ref );
723 ber_bvarray_free( erefs );
724 rs->sr_ref = NULL;
725 } else if ( manageDSAit || !is_entry_glue( &e )) {
726 rc = test_filter( op, &e, op->ors_filter );
727 if ( rc == LDAP_COMPARE_TRUE ) {
728 rs->sr_entry = &e;
729 rs->sr_attrs = op->ors_attrs;
730 rs->sr_flags = 0;
731 rc = send_search_entry( op, rs );
732 rs->sr_entry = NULL;
733 } else {
734 rc = 0;
735 }
736 }
737 attrs_free( e.e_attrs );
738 e.e_attrs = NULL;
739 op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx );
740 if ( rc ) break;
741 }
742 leave:
743 if ( sf )
744 delete sf;
745 if ( txn )
746 txn->close();
747 send_ldap_result( op, rs );
748 return rs->sr_err;
749 }
750
751 extern NdbInterpretedCode *ndb_lastrow_code; /* init.cpp */
752
753 extern "C" int
ndb_has_children(NdbArgs * NA,int * hasChildren)754 ndb_has_children(
755 NdbArgs *NA,
756 int *hasChildren
757 )
758 {
759 NdbIndexScanOperation *scan;
760 char idbuf[2*sizeof(ID)];
761 int rc;
762
763 if ( NA->rdns->nr_num >= NDB_MAX_RDNS ) {
764 *hasChildren = LDAP_COMPARE_FALSE;
765 return 0;
766 }
767
768 scan = NA->txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
769 if ( !scan )
770 return LDAP_OTHER;
771 scan->readTuples( NdbOperation::LM_Read, 0U, 0U, 1U );
772 rc = ndb_dn2bound( scan, NA->rdns );
773 if ( rc < NDB_MAX_RDNS ) {
774 scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" );
775 }
776 #if 0
777 scan->interpret_exit_last_row();
778 #else
779 scan->setInterpretedCode(ndb_lastrow_code);
780 #endif
781 scan->getValue( EID_COLUMN, idbuf );
782 if ( NA->txn->execute( NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1 )) {
783 return LDAP_OTHER;
784 }
785 if (rc < NDB_MAX_RDNS && scan->nextResult( true, true ) == 0 )
786 *hasChildren = LDAP_COMPARE_TRUE;
787 else
788 *hasChildren = LDAP_COMPARE_FALSE;
789 scan->close();
790 return 0;
791 }
792
793 extern "C" int
ndb_has_subordinates(Operation * op,Entry * e,int * hasSubordinates)794 ndb_has_subordinates(
795 Operation *op,
796 Entry *e,
797 int *hasSubordinates )
798 {
799 NdbArgs NA;
800 NdbRdns rdns;
801 int rc;
802
803 NA.rdns = &rdns;
804 rc = ndb_dn2rdns( &e->e_nname, &rdns );
805
806 if ( rc == 0 ) {
807 rc = ndb_thread_handle( op, &NA.ndb );
808 NA.txn = NA.ndb->startTransaction();
809 if ( NA.txn ) {
810 rc = ndb_has_children( &NA, hasSubordinates );
811 NA.txn->close();
812 }
813 }
814
815 return rc;
816 }
817
818 /*
819 * sets the supported operational attributes (if required)
820 */
821 extern "C" int
ndb_operational(Operation * op,SlapReply * rs)822 ndb_operational(
823 Operation *op,
824 SlapReply *rs )
825 {
826 Attribute **ap;
827
828 assert( rs->sr_entry != NULL );
829
830 for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next ) {
831 if ( (*ap)->a_desc == slap_schema.si_ad_hasSubordinates ) {
832 break;
833 }
834 }
835
836 if ( *ap == NULL &&
837 attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_hasSubordinates ) == NULL &&
838 ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
839 ad_inlist( slap_schema.si_ad_hasSubordinates, rs->sr_attrs ) ) )
840 {
841 int hasSubordinates, rc;
842
843 rc = ndb_has_subordinates( op, rs->sr_entry, &hasSubordinates );
844 if ( rc == LDAP_SUCCESS ) {
845 *ap = slap_operational_hasSubordinate( hasSubordinates == LDAP_COMPARE_TRUE );
846 assert( *ap != NULL );
847
848 ap = &(*ap)->a_next;
849 }
850 }
851
852 return LDAP_SUCCESS;
853 }
854
855