1 /* search.cpp - tools for slap tools */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2008 The OpenLDAP Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
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>.
17 * This work was initially developed by Howard Chu for inclusion
18 * in OpenLDAP Software. This work was sponsored by MySQL.
24 #include <ac/string.h>
33 NdbIndexScanOperation *myop,
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] ))
48 /* Check that all filter terms reside in the same table.
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.
54 * Otherwise, a full scan of the DB must be done with all filtering done by slapd.
56 static int ndb_filter_check( struct ndb_info *ni, Filter *f,
57 NdbOcInfo **oci, int *indexed, int *ocfilter )
59 AttributeDescription *ad = NULL;
60 ber_tag_t choice = f->f_choice;
61 int rc = 0, undef = 0;
63 if ( choice & SLAPD_FILTER_UNDEFINED ) {
64 choice &= SLAPD_FILTER_MASK;
71 for ( f = f->f_list; f; f=f->f_next ) {
72 rc = ndb_filter_check( ni, f, oci, indexed, ocfilter );
76 case LDAP_FILTER_PRESENT:
79 case LDAP_FILTER_EQUALITY:
80 case LDAP_FILTER_SUBSTRINGS:
83 case LDAP_FILTER_APPROX:
91 /* ObjectClass filtering is in dn2id table */
92 if ( ad == slap_schema.si_ad_objectClass ) {
93 if ( choice == LDAP_FILTER_EQUALITY )
97 ai = ndb_ai_find( ni, ad->ad_type );
99 if ( ai->na_flag & NDB_INFO_INDEX )
102 if ( ai->na_oi != *oci )
112 static int ndb_filter_set( Operation *op, struct ndb_info *ni, Filter *f, int indexed,
113 NdbIndexScanOperation *scan, NdbScanFilter **sf, int *bounds )
115 AttributeDescription *ad = NULL;
116 ber_tag_t choice = f->f_choice;
119 if ( choice & SLAPD_FILTER_UNDEFINED ) {
120 choice &= SLAPD_FILTER_MASK;
124 case LDAP_FILTER_NOT:
125 /* no indexing for these */
128 /* FIXME: these bounds aren't right. */
130 scan->end_of_bound( (*bounds)++ );
132 case LDAP_FILTER_AND:
133 for ( f = f->f_list; f; f=f->f_next ) {
134 if ( ndb_filter_set( op, ni, f, indexed, scan, sf, bounds ))
138 case LDAP_FILTER_PRESENT:
141 case LDAP_FILTER_EQUALITY:
142 case LDAP_FILTER_SUBSTRINGS:
145 case LDAP_FILTER_APPROX:
151 if ( ad && !undef ) {
153 /* ObjectClass filtering is in dn2id table */
154 if ( ad == slap_schema.si_ad_objectClass ) {
157 ai = ndb_ai_find( ni, ad->ad_type );
159 if ( ai->na_flag & NDB_INFO_INDEX ) {
161 NdbIndexScanOperation::BoundType bt;
165 case LDAP_FILTER_PRESENT:
166 rc = scan->setBound( ai->na_ixcol - IDX_COLUMN,
167 NdbIndexScanOperation::BoundGT, NULL );
169 case LDAP_FILTER_EQUALITY:
170 case LDAP_FILTER_APPROX:
171 bt = NdbIndexScanOperation::BoundEQ;
174 bt = NdbIndexScanOperation::BoundGE;
177 bt = NdbIndexScanOperation::BoundLE;
179 rc = f->f_av_value.bv_len+1;
180 if ( ai->na_len > 255 )
182 buf = (char *)op->o_tmpalloc( rc, op->o_tmpmemctx );
183 rc = f->f_av_value.bv_len;
186 if ( ai->na_len > 255 ) {
190 memcpy( ptr, f->f_av_value.bv_val, f->f_av_value.bv_len );
191 rc = scan->setBound( ai->na_ixcol - IDX_COLUMN, bt, buf );
192 op->o_tmpfree( buf, op->o_tmpmemctx );
204 static int ndb_oc_search( Operation *op, SlapReply *rs, Ndb *ndb, NdbTransaction *txn,
205 NdbRdns *rbase, NdbOcInfo *oci, int indexed )
207 struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
208 const NdbDictionary::Dictionary *myDict = ndb->getDictionary();
209 const NdbDictionary::Table *myTable;
210 const NdbDictionary::Index *myIndex;
211 NdbIndexScanOperation *scan;
212 NdbIndexOperation *ixop;
213 NdbScanFilter *sf = NULL;
214 struct berval *ocs, matched;
215 NdbRecAttr *scanID, *scanOC, *scanDN[NDB_MAX_RDNS];
216 char dnBuf[2048], *ptr;
219 char idbuf[2*sizeof(ID)];
220 char ocbuf[NDB_OC_BUFLEN];
225 myTable = myDict->getTable( oci->no_table.bv_val );
227 scan = txn->getNdbIndexScanOperation( INDEX_NAME, DN2ID_TABLE );
231 myIndex = myDict->getIndex( "eid$unique", DN2ID_TABLE );
233 Debug( LDAP_DEBUG_ANY, DN2ID_TABLE " eid index is missing!\n", 0, 0, 0 );
234 rs->sr_err = LDAP_OTHER;
237 scan = (NdbIndexScanOperation *)txn->getNdbScanOperation( myTable );
240 sf = new NdbScanFilter(scan);
243 switch ( op->ors_filter->f_choice ) {
244 case LDAP_FILTER_AND:
246 case LDAP_FILTER_NOT:
249 if ( sf->begin() < 0 ) {
255 scan->readTuples( NdbOperation::LM_CommittedRead );
258 rc = ndb_filter_set( op, ni, op->ors_filter, indexed, scan, &sf, &bounds );
262 scanID = scan->getValue( EID_COLUMN, idbuf );
264 scanOC = scan->getValue( OCS_COLUMN, ocbuf );
265 for ( i=0; i<NDB_MAX_RDNS; i++ ) {
266 rdns.nr_buf[i][0] = '\0';
267 scanDN[i] = scan->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
271 if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) {
272 rs->sr_err = LDAP_OTHER;
276 e.e_name.bv_val = dnBuf;
279 while ( scan->nextResult() == 0 ) {
283 eid = scanID->u_64_value();
286 tx2 = ndb->startTransaction( myTable );
288 rs->sr_err = LDAP_OTHER;
292 ixop = tx2->getNdbIndexOperation( myIndex );
295 rs->sr_err = LDAP_OTHER;
298 ixop->readTuple( NdbOperation::LM_CommittedRead );
299 ixop->equal( EID_COLUMN, eid );
301 scanOC = ixop->getValue( OCS_COLUMN, ocbuf );
302 for ( i=0; i<NDB_MAX_RDNS; i++ ) {
303 rdns.nr_buf[i][0] = '\0';
304 scanDN[i] = ixop->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
306 rc = tx2->execute( NdbTransaction::Commit, NdbOperation::AbortOnError, 1 );
309 rs->sr_err = LDAP_OTHER;
314 ocs = ndb_ref2oclist( ocbuf );
315 for ( i=0; i<NDB_MAX_RDNS; i++ ) {
316 if ( scanDN[i]->isNULL() || !rdns.nr_buf[i][0] )
321 /* entry must be subordinate to the base */
322 if ( i < rbase->nr_num ) {
327 for ( --i; i>=0; i-- ) {
330 buf = rdns.nr_buf[i];
332 ptr = lutil_strncopy( ptr, buf, len );
333 if ( i ) *ptr++ = ',';
336 e.e_name.bv_len = ptr - dnBuf;
338 /* More scope checks */
339 /* If indexed, these can be moved into the ScanFilter */
340 switch( op->ors_scope ) {
341 case LDAP_SCOPE_ONELEVEL:
342 if ( rdns.nr_num != rbase->nr_num+1 )
344 case LDAP_SCOPE_SUBORDINATE:
345 if ( rdns.nr_num == rbase->nr_num )
347 case LDAP_SCOPE_SUBTREE:
349 if ( e.e_name.bv_len <= op->o_req_dn.bv_len ) {
350 if ( op->ors_scope != LDAP_SCOPE_SUBTREE ||
351 strcasecmp( op->o_req_dn.bv_val, e.e_name.bv_val ))
353 } else if ( strcasecmp( op->o_req_dn.bv_val, e.e_name.bv_val +
354 e.e_name.bv_len - op->o_req_dn.bv_len ))
358 dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx );
360 #ifdef notdef /* NDBapi is broken here */
361 Ndb::Key_part_ptr keys[2];
364 keys[0].len = sizeof(eid);
367 tx2 = ndb->startTransaction( myTable, keys, xbuf, sizeof(xbuf));
369 tx2 = ndb->startTransaction( myTable );
372 rs->sr_err = LDAP_OTHER;
377 rc = ndb_entry_get_data( op->o_bd, &NA, 0 );
380 ber_bvarray_free( ocs );
381 rc = test_filter( op, &e, op->ors_filter );
382 if ( rc == LDAP_COMPARE_TRUE ) {
384 rs->sr_attrs = op->ors_attrs;
386 rc = send_search_entry( op, rs );
391 attrs_free( e.e_attrs );
393 op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx );
402 int ndb_back_search( Operation *op, SlapReply *rs )
404 struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
405 NdbTransaction *txn, *tx2;
406 NdbIndexScanOperation *scan;
407 NdbScanFilter *sf = NULL;
409 int rc, i, ocfilter, indexed;
410 struct berval matched;
411 NdbRecAttr *scanID, *scanOC, *scanDN[NDB_MAX_RDNS];
412 char dnBuf[2048], *ptr;
413 char idbuf[2*sizeof(ID)];
414 char ocbuf[NDB_OC_BUFLEN];
419 rc = ndb_thread_handle( op, &NA.ndb );
422 txn = NA.ndb->startTransaction();
424 Debug( LDAP_DEBUG_TRACE,
425 LDAP_XSTRING(ndb_back_search) ": startTransaction failed: %s (%d)\n",
426 NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
427 rs->sr_err = LDAP_OTHER;
428 rs->sr_text = "internal error";
433 e.e_name = op->o_req_dn;
436 NA.ocs = op->ors_scope == LDAP_SCOPE_BASE ? NULL : &matched;
438 rs->sr_err = ndb_entry_get_info( op->o_bd, &NA, 0, &matched );
440 if ( rs->sr_err == LDAP_NO_SUCH_OBJECT )
441 rs->sr_matched = matched.bv_val;
445 if ( op->ors_scope == LDAP_SCOPE_BASE ) {
446 e.e_name = op->o_req_dn;
447 e.e_nname = op->o_req_ndn;
448 rc = ndb_entry_get_data( op->o_bd, &NA, 0 );
449 ber_bvarray_free( NA.ocs );
450 rc = test_filter( op, &e, op->ors_filter );
451 if ( rc == LDAP_COMPARE_TRUE ) {
453 rs->sr_attrs = op->ors_attrs;
455 send_search_entry( op, rs );
458 attrs_free( e.e_attrs );
460 rs->sr_err = LDAP_SUCCESS;
462 } else if ( rdns.nr_num == NDB_MAX_RDNS ) {
463 if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ||
464 op->ors_scope == LDAP_SCOPE_CHILDREN )
465 rs->sr_err = LDAP_SUCCESS;
469 /* See if we can handle the filter. Filtering on objectClass is only done
470 * in the DN2ID table scan. If all other filter terms reside in one table,
471 * then we scan the OC table instead of the DN2ID table.
476 rc = ndb_filter_check( ni, op->ors_filter, &oci, &indexed, &ocfilter );
478 Debug( LDAP_DEBUG_TRACE, "ndb_back_search: "
479 "filter attributes from multiple tables, indexing ignored\n",
482 rc = ndb_oc_search( op, rs, NA.ndb, txn, &rdns, oci, indexed );
486 scan = txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
487 scan->readTuples( NdbOperation::LM_CommittedRead );
488 rc = ndb_dn2bound( scan, &rdns );
490 switch( op->ors_scope ) {
491 case LDAP_SCOPE_ONELEVEL:
492 sf = new NdbScanFilter(scan);
493 if ( sf->begin() < 0 ||
494 sf->cmp(NdbScanFilter::COND_NOT_LIKE, rc+3, "_%",
495 STRLENOF("_%")) < 0 ||
497 rs->sr_err = LDAP_OTHER;
501 case LDAP_SCOPE_CHILDREN:
502 /* Note: RDN_COLUMN offset not needed here */
503 scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" );
505 case LDAP_SCOPE_SUBTREE:
508 scanID = scan->getValue( EID_COLUMN, idbuf );
509 scanOC = scan->getValue( OCS_COLUMN, ocbuf );
510 for ( i=0; i<NDB_MAX_RDNS; i++ ) {
511 rdns.nr_buf[i][0] = '\0';
512 scanDN[i] = scan->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
514 if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) {
515 rs->sr_err = LDAP_OTHER;
519 e.e_name.bv_val = dnBuf;
520 while ( scan->nextResult() == 0 ) {
523 e.e_id = scanID->u_64_value();
524 NA.ocs = ndb_ref2oclist( ocbuf );
525 for ( i=0; i<NDB_MAX_RDNS; i++ ) {
526 if ( scanDN[i]->isNULL() || !rdns.nr_buf[i][0] )
531 for ( --i; i>=0; i-- ) {
534 buf = rdns.nr_buf[i];
536 ptr = lutil_strncopy( ptr, buf, len );
537 if ( i ) *ptr++ = ',';
540 e.e_name.bv_len = ptr - dnBuf;
541 dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx );
542 NA.txn = NA.ndb->startTransaction();
543 rc = ndb_entry_get_data( op->o_bd, &NA, 0 );
545 ber_bvarray_free( NA.ocs );
546 rc = test_filter( op, &e, op->ors_filter );
547 if ( rc == LDAP_COMPARE_TRUE ) {
549 rs->sr_attrs = op->ors_attrs;
551 rc = send_search_entry( op, rs );
556 attrs_free( e.e_attrs );
558 op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx );
565 send_ldap_result( op, rs );
575 NdbIndexScanOperation *scan;
576 char idbuf[2*sizeof(ID)];
579 if ( NA->rdns->nr_num >= NDB_MAX_RDNS ) {
580 *hasChildren = LDAP_COMPARE_FALSE;
584 scan = NA->txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
587 scan->readTuples( NdbOperation::LM_Read, 0U, 0U, 1U );
588 rc = ndb_dn2bound( scan, NA->rdns );
589 if ( rc < NDB_MAX_RDNS ) {
590 scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" );
592 scan->interpret_exit_last_row();
593 scan->getValue( EID_COLUMN, idbuf );
594 if ( NA->txn->execute( NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1 )) {
597 if (rc < NDB_MAX_RDNS && scan->nextResult() == 0 )
598 *hasChildren = LDAP_COMPARE_TRUE;
600 *hasChildren = LDAP_COMPARE_FALSE;
605 ndb_has_subordinates(
608 int *hasSubordinates )
615 rc = ndb_dn2rdns( &e->e_nname, &rdns );
618 rc = ndb_thread_handle( op, &NA.ndb );
619 NA.txn = NA.ndb->startTransaction();
621 rc = ndb_has_children( &NA, hasSubordinates );
630 * sets the supported operational attributes (if required)
639 assert( rs->sr_entry != NULL );
641 for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next )
644 if ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
645 ad_inlist( slap_schema.si_ad_hasSubordinates, rs->sr_attrs ) )
647 int hasSubordinates, rc;
649 rc = ndb_has_subordinates( op, rs->sr_entry, &hasSubordinates );
650 if ( rc == LDAP_SUCCESS ) {
651 *ap = slap_operational_hasSubordinate( hasSubordinates == LDAP_COMPARE_TRUE );
652 assert( *ap != NULL );