]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ndb/search.cpp
91aaaef6a03a3a08d142502b5d5593e70bc470ff
[openldap] / servers / slapd / back-ndb / search.cpp
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-2017 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
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  */
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
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
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"
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
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
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
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