1 /* search.cpp - tools for slap tools */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2008-2009 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:
134 sf->begin( choice == LDAP_FILTER_OR ? NdbScanFilter::OR : NdbScanFilter::AND );
136 for ( f = f->f_list; f; f=f->f_next ) {
137 if ( ndb_filter_set( op, ni, f, indexed, scan, sf, bounds ))
144 case LDAP_FILTER_PRESENT:
147 case LDAP_FILTER_EQUALITY:
148 case LDAP_FILTER_SUBSTRINGS:
151 case LDAP_FILTER_APPROX:
157 if ( ad && !undef ) {
159 /* ObjectClass filtering is in dn2id table */
160 if ( ad == slap_schema.si_ad_objectClass ) {
163 ai = ndb_ai_find( ni, ad->ad_type );
166 if ( ai->na_flag & NDB_INFO_INDEX ) {
168 NdbIndexScanOperation::BoundType bt;
171 case LDAP_FILTER_PRESENT:
172 rc = scan->setBound( ai->na_ixcol - IDX_COLUMN,
173 NdbIndexScanOperation::BoundGT, NULL );
175 case LDAP_FILTER_EQUALITY:
176 case LDAP_FILTER_APPROX:
177 bt = NdbIndexScanOperation::BoundEQ;
180 bt = NdbIndexScanOperation::BoundGE;
183 bt = NdbIndexScanOperation::BoundLE;
185 rc = f->f_av_value.bv_len+1;
186 if ( ai->na_len > 255 )
188 buf = (char *)op->o_tmpalloc( rc, op->o_tmpmemctx );
189 rc = f->f_av_value.bv_len;
192 if ( ai->na_len > 255 ) {
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 );
205 NdbScanFilter::BinaryCondition bc;
208 case LDAP_FILTER_PRESENT:
209 rc = sf->isnotnull( ai->na_column );
211 case LDAP_FILTER_EQUALITY:
212 case LDAP_FILTER_APPROX:
213 bc = NdbScanFilter::COND_EQ;
216 bc = NdbScanFilter::COND_GE;
219 bc = NdbScanFilter::COND_LE;
221 rc = sf->cmp( bc, ai->na_column, f->f_av_value.bv_val, f->f_av_value.bv_len );
223 case LDAP_FILTER_SUBSTRINGS:
225 if ( f->f_sub_initial.bv_val )
226 rc += f->f_sub_initial.bv_len + 1;
227 if ( f->f_sub_any ) {
230 for (i=0; f->f_sub_any[i].bv_val; i++)
231 rc += f->f_sub_any[i].bv_len + 1;
233 if ( f->f_sub_final.bv_val ) {
235 rc += f->f_sub_final.bv_len;
237 buf = (char *)op->o_tmpalloc( rc+1, op->o_tmpmemctx );
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;
244 if ( f->f_sub_any ) {
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;
254 if ( f->f_sub_final.bv_val ) {
257 memcpy( ptr, f->f_sub_final.bv_val, f->f_sub_final.bv_len );
258 ptr += f->f_sub_final.bv_len;
261 rc = sf->cmp( NdbScanFilter::COND_LIKE, ai->na_column, buf, ptr - buf );
262 op->o_tmpfree( buf, op->o_tmpmemctx );
271 static int ndb_oc_search( Operation *op, SlapReply *rs, Ndb *ndb, NdbTransaction *txn,
272 NdbRdns *rbase, NdbOcInfo *oci, int indexed )
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;
282 NdbRecAttr *scanID, *scanOC, *scanDN[NDB_MAX_RDNS];
283 char dnBuf[2048], *ptr;
286 char idbuf[2*sizeof(ID)];
287 char ocbuf[NDB_OC_BUFLEN];
294 stoptime = op->o_time + op->ors_tlimit;
295 manageDSAit = get_manageDSAit( op );
297 myTable = myDict->getTable( oci->no_table.bv_val );
299 scan = txn->getNdbIndexScanOperation( INDEX_NAME, DN2ID_TABLE );
302 scan->readTuples( NdbOperation::LM_CommittedRead );
304 myIndex = myDict->getIndex( "eid$unique", DN2ID_TABLE );
306 Debug( LDAP_DEBUG_ANY, DN2ID_TABLE " eid index is missing!\n", 0, 0, 0 );
307 rs->sr_err = LDAP_OTHER;
310 scan = (NdbIndexScanOperation *)txn->getNdbScanOperation( myTable );
313 scan->readTuples( NdbOperation::LM_CommittedRead );
315 sf = new NdbScanFilter(scan);
318 switch ( op->ors_filter->f_choice ) {
319 case LDAP_FILTER_AND:
321 case LDAP_FILTER_NOT:
324 if ( sf->begin() < 0 ) {
333 rc = ndb_filter_set( op, ni, op->ors_filter, indexed, scan, sf, &bounds );
338 scanID = scan->getValue( EID_COLUMN, idbuf );
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] );
347 if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) {
348 rs->sr_err = LDAP_OTHER;
352 e.e_name.bv_val = dnBuf;
355 while ( scan->nextResult( true, true ) == 0 ) {
357 if ( op->o_abandon ) {
358 rs->sr_err = SLAPD_ABANDON;
361 if ( slapd_shutdown ) {
362 rs->sr_err = LDAP_UNAVAILABLE;
365 if ( op->ors_tlimit != SLAP_NO_LIMIT &&
366 slap_get_time() > stoptime ) {
367 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
371 eid = scanID->u_64_value();
374 tx2 = ndb->startTransaction( myTable );
376 rs->sr_err = LDAP_OTHER;
380 ixop = tx2->getNdbIndexOperation( myIndex );
383 rs->sr_err = LDAP_OTHER;
386 ixop->readTuple( NdbOperation::LM_CommittedRead );
387 ixop->equal( EID_COLUMN, eid );
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] );
394 rc = tx2->execute( NdbTransaction::Commit, NdbOperation::AbortOnError, 1 );
397 rs->sr_err = LDAP_OTHER;
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] )
409 /* entry must be subordinate to the base */
410 if ( i < rbase->nr_num ) {
415 for ( --i; i>=0; i-- ) {
418 buf = rdns.nr_buf[i];
420 ptr = lutil_strncopy( ptr, buf, len );
421 if ( i ) *ptr++ = ',';
424 e.e_name.bv_len = ptr - dnBuf;
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 )
432 case LDAP_SCOPE_SUBORDINATE:
433 if ( rdns.nr_num == rbase->nr_num )
435 case LDAP_SCOPE_SUBTREE:
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 ))
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 ))
446 dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx );
448 #if 1 /* NDBapi was broken here but seems to work now */
449 Ndb::Key_part_ptr keys[2];
452 keys[0].len = sizeof(eid);
455 tx2 = ndb->startTransaction( myTable, keys, xbuf, sizeof(xbuf));
457 tx2 = ndb->startTransaction( myTable );
460 rs->sr_err = LDAP_OTHER;
465 rc = ndb_entry_get_data( op, &NA, 0 );
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 );
478 } else if ( manageDSAit || !is_entry_glue( &e )) {
479 rc = test_filter( op, &e, op->ors_filter );
480 if ( rc == LDAP_COMPARE_TRUE ) {
482 rs->sr_attrs = op->ors_attrs;
484 rc = send_search_entry( op, rs );
490 attrs_free( e.e_attrs );
492 op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx );
501 int ndb_back_search( Operation *op, SlapReply *rs )
503 struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
505 NdbIndexScanOperation *scan;
506 NdbScanFilter *sf = NULL;
508 int rc, i, ocfilter, indexed;
509 struct berval matched;
510 NdbRecAttr *scanID, *scanOC, *scanDN[NDB_MAX_RDNS];
511 char dnBuf[2048], *ptr;
512 char idbuf[2*sizeof(ID)];
513 char ocbuf[NDB_OC_BUFLEN];
521 rc = ndb_thread_handle( op, &NA.ndb );
524 manageDSAit = get_manageDSAit( op );
526 txn = NA.ndb->startTransaction();
528 Debug( LDAP_DEBUG_TRACE,
529 LDAP_XSTRING(ndb_back_search) ": startTransaction failed: %s (%d)\n",
530 NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
531 rs->sr_err = LDAP_OTHER;
532 rs->sr_text = "internal error";
537 e.e_name = op->o_req_dn;
538 e.e_nname = op->o_req_ndn;
543 rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
545 if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
546 rs->sr_matched = matched.bv_val;
548 ndb_check_referral( op, rs, &NA );
553 if ( !access_allowed_mask( op, &e, slap_schema.si_ad_entry,
554 NULL, ACL_SEARCH, NULL, &mask )) {
555 if ( !ACL_GRANT( mask, ACL_DISCLOSE ))
556 rs->sr_err = LDAP_NO_SUCH_OBJECT;
558 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
559 ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
563 rs->sr_err = ndb_entry_get_data( op, &NA, 0 );
564 ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
568 if ( !manageDSAit && is_entry_referral( &e )) {
569 rs->sr_ref = get_entry_referrals( op, &e );
570 rs->sr_err = LDAP_REFERRAL;
572 rs->sr_flags |= REP_REF_MUSTBEFREED;
573 rs->sr_matched = e.e_name.bv_val;
574 attrs_free( e.e_attrs );
579 if ( !manageDSAit && is_entry_glue( &e )) {
580 rs->sr_err = LDAP_NO_SUCH_OBJECT;
584 if ( get_assert( op ) && test_filter( op, &e, (Filter *)get_assertion( op )) !=
585 LDAP_COMPARE_TRUE ) {
586 rs->sr_err = LDAP_ASSERTION_FAILED;
587 attrs_free( e.e_attrs );
592 /* admin ignores tlimits */
593 stoptime = op->o_time + op->ors_tlimit;
595 if ( op->ors_scope == LDAP_SCOPE_BASE ) {
596 rc = test_filter( op, &e, op->ors_filter );
597 if ( rc == LDAP_COMPARE_TRUE ) {
599 rs->sr_attrs = op->ors_attrs;
601 send_search_entry( op, rs );
604 attrs_free( e.e_attrs );
606 rs->sr_err = LDAP_SUCCESS;
609 attrs_free( e.e_attrs );
611 if ( rdns.nr_num == NDB_MAX_RDNS ) {
612 if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ||
613 op->ors_scope == LDAP_SCOPE_CHILDREN )
614 rs->sr_err = LDAP_SUCCESS;
619 /* See if we can handle the filter. Filtering on objectClass is only done
620 * in the DN2ID table scan. If all other filter terms reside in one table,
621 * then we scan the OC table instead of the DN2ID table.
626 rc = ndb_filter_check( ni, op->ors_filter, &oci, &indexed, &ocfilter );
628 Debug( LDAP_DEBUG_TRACE, "ndb_back_search: "
629 "filter attributes from multiple tables, indexing ignored\n",
632 rc = ndb_oc_search( op, rs, NA.ndb, txn, &rdns, oci, indexed );
636 scan = txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
638 rs->sr_err = LDAP_OTHER;
641 scan->readTuples( NdbOperation::LM_CommittedRead );
642 rc = ndb_dn2bound( scan, &rdns );
644 /* TODO: if ( ocfilter ) set up scanfilter for objectclass matches
645 * column COND_LIKE "% <class> %"
648 switch( op->ors_scope ) {
649 case LDAP_SCOPE_ONELEVEL:
650 sf = new NdbScanFilter(scan);
651 if ( sf->begin() < 0 ||
652 sf->cmp(NdbScanFilter::COND_NOT_LIKE, rc+3, "_%",
653 STRLENOF("_%")) < 0 ||
655 rs->sr_err = LDAP_OTHER;
659 case LDAP_SCOPE_CHILDREN:
660 /* Note: RDN_COLUMN offset not needed here */
661 scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" );
663 case LDAP_SCOPE_SUBTREE:
666 scanID = scan->getValue( EID_COLUMN, idbuf );
667 scanOC = scan->getValue( OCS_COLUMN, ocbuf );
668 for ( i=0; i<NDB_MAX_RDNS; i++ ) {
669 rdns.nr_buf[i][0] = '\0';
670 scanDN[i] = scan->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
672 if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) {
673 rs->sr_err = LDAP_OTHER;
677 e.e_name.bv_val = dnBuf;
678 while ( scan->nextResult( true, true ) == 0 ) {
679 if ( op->o_abandon ) {
680 rs->sr_err = SLAPD_ABANDON;
683 if ( slapd_shutdown ) {
684 rs->sr_err = LDAP_UNAVAILABLE;
687 if ( op->ors_tlimit != SLAP_NO_LIMIT &&
688 slap_get_time() > stoptime ) {
689 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
692 e.e_id = scanID->u_64_value();
693 NA.ocs = ndb_ref2oclist( ocbuf, op->o_tmpmemctx );
694 for ( i=0; i<NDB_MAX_RDNS; i++ ) {
695 if ( scanDN[i]->isNULL() || !rdns.nr_buf[i][0] )
700 for ( --i; i>=0; i-- ) {
703 buf = rdns.nr_buf[i];
705 ptr = lutil_strncopy( ptr, buf, len );
706 if ( i ) *ptr++ = ',';
709 e.e_name.bv_len = ptr - dnBuf;
710 dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx );
711 NA.txn = NA.ndb->startTransaction();
712 rc = ndb_entry_get_data( op, &NA, 0 );
714 ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
715 if ( !manageDSAit && is_entry_referral( &e )) {
716 BerVarray erefs = get_entry_referrals( op, &e );
717 rs->sr_ref = referral_rewrite( erefs, &e.e_name, NULL,
718 op->ors_scope == LDAP_SCOPE_ONELEVEL ?
719 LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
720 rc = send_search_reference( op, rs );
721 ber_bvarray_free( rs->sr_ref );
722 ber_bvarray_free( erefs );
724 } else if ( manageDSAit || !is_entry_glue( &e )) {
725 rc = test_filter( op, &e, op->ors_filter );
726 if ( rc == LDAP_COMPARE_TRUE ) {
728 rs->sr_attrs = op->ors_attrs;
730 rc = send_search_entry( op, rs );
736 attrs_free( e.e_attrs );
738 op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx );
746 send_ldap_result( op, rs );
750 extern NdbInterpretedCode *ndb_lastrow_code; /* init.cpp */
758 NdbIndexScanOperation *scan;
759 char idbuf[2*sizeof(ID)];
762 if ( NA->rdns->nr_num >= NDB_MAX_RDNS ) {
763 *hasChildren = LDAP_COMPARE_FALSE;
767 scan = NA->txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
770 scan->readTuples( NdbOperation::LM_Read, 0U, 0U, 1U );
771 rc = ndb_dn2bound( scan, NA->rdns );
772 if ( rc < NDB_MAX_RDNS ) {
773 scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" );
776 scan->interpret_exit_last_row();
778 scan->setInterpretedCode(ndb_lastrow_code);
780 scan->getValue( EID_COLUMN, idbuf );
781 if ( NA->txn->execute( NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1 )) {
784 if (rc < NDB_MAX_RDNS && scan->nextResult( true, true ) == 0 )
785 *hasChildren = LDAP_COMPARE_TRUE;
787 *hasChildren = LDAP_COMPARE_FALSE;
793 ndb_has_subordinates(
796 int *hasSubordinates )
803 rc = ndb_dn2rdns( &e->e_nname, &rdns );
806 rc = ndb_thread_handle( op, &NA.ndb );
807 NA.txn = NA.ndb->startTransaction();
809 rc = ndb_has_children( &NA, hasSubordinates );
818 * sets the supported operational attributes (if required)
827 assert( rs->sr_entry != NULL );
829 for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next )
832 if ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
833 ad_inlist( slap_schema.si_ad_hasSubordinates, rs->sr_attrs ) )
835 int hasSubordinates, rc;
837 rc = ndb_has_subordinates( op, rs->sr_entry, &hasSubordinates );
838 if ( rc == LDAP_SUCCESS ) {
839 *ap = slap_operational_hasSubordinate( hasSubordinates == LDAP_COMPARE_TRUE );
840 assert( *ap != NULL );