1
/* search.cpp - tools for slap tools */
2
/* $OpenLDAP: pkg/ldap/servers/slapd/back-ndb/search.cpp,v 1.3.2.2 2009/01/22 00:01:09 kurt Exp $ */
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
#ifdef notdef /* NDBapi is broken here */
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 );
637
scan->readTuples( NdbOperation::LM_CommittedRead );
638
rc = ndb_dn2bound( scan, &rdns );
640
/* TODO: if ( ocfilter ) set up scanfilter for objectclass matches
641
* column COND_LIKE "% <class> %"
644
switch( op->ors_scope ) {
645
case LDAP_SCOPE_ONELEVEL:
646
sf = new NdbScanFilter(scan);
647
if ( sf->begin() < 0 ||
648
sf->cmp(NdbScanFilter::COND_NOT_LIKE, rc+3, "_%",
649
STRLENOF("_%")) < 0 ||
651
rs->sr_err = LDAP_OTHER;
655
case LDAP_SCOPE_CHILDREN:
656
/* Note: RDN_COLUMN offset not needed here */
657
scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" );
659
case LDAP_SCOPE_SUBTREE:
662
scanID = scan->getValue( EID_COLUMN, idbuf );
663
scanOC = scan->getValue( OCS_COLUMN, ocbuf );
664
for ( i=0; i<NDB_MAX_RDNS; i++ ) {
665
rdns.nr_buf[i][0] = '\0';
666
scanDN[i] = scan->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
668
if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) {
669
rs->sr_err = LDAP_OTHER;
673
e.e_name.bv_val = dnBuf;
674
while ( scan->nextResult( true, true ) == 0 ) {
675
if ( op->o_abandon ) {
676
rs->sr_err = SLAPD_ABANDON;
679
if ( slapd_shutdown ) {
680
rs->sr_err = LDAP_UNAVAILABLE;
683
if ( op->ors_tlimit != SLAP_NO_LIMIT &&
684
slap_get_time() > stoptime ) {
685
rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
688
e.e_id = scanID->u_64_value();
689
NA.ocs = ndb_ref2oclist( ocbuf, op->o_tmpmemctx );
690
for ( i=0; i<NDB_MAX_RDNS; i++ ) {
691
if ( scanDN[i]->isNULL() || !rdns.nr_buf[i][0] )
696
for ( --i; i>=0; i-- ) {
699
buf = rdns.nr_buf[i];
701
ptr = lutil_strncopy( ptr, buf, len );
702
if ( i ) *ptr++ = ',';
705
e.e_name.bv_len = ptr - dnBuf;
706
dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx );
707
NA.txn = NA.ndb->startTransaction();
708
rc = ndb_entry_get_data( op, &NA, 0 );
710
ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
711
if ( !manageDSAit && is_entry_referral( &e )) {
712
BerVarray erefs = get_entry_referrals( op, &e );
713
rs->sr_ref = referral_rewrite( erefs, &e.e_name, NULL,
714
op->ors_scope == LDAP_SCOPE_ONELEVEL ?
715
LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
716
rc = send_search_reference( op, rs );
717
ber_bvarray_free( rs->sr_ref );
718
ber_bvarray_free( erefs );
720
} else if ( manageDSAit || !is_entry_glue( &e )) {
721
rc = test_filter( op, &e, op->ors_filter );
722
if ( rc == LDAP_COMPARE_TRUE ) {
724
rs->sr_attrs = op->ors_attrs;
726
rc = send_search_entry( op, rs );
732
attrs_free( e.e_attrs );
734
op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx );
742
send_ldap_result( op, rs );
746
extern NdbInterpretedCode *ndb_lastrow_code; /* init.cpp */
754
NdbIndexScanOperation *scan;
755
char idbuf[2*sizeof(ID)];
758
if ( NA->rdns->nr_num >= NDB_MAX_RDNS ) {
759
*hasChildren = LDAP_COMPARE_FALSE;
763
scan = NA->txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
766
scan->readTuples( NdbOperation::LM_Read, 0U, 0U, 1U );
767
rc = ndb_dn2bound( scan, NA->rdns );
768
if ( rc < NDB_MAX_RDNS ) {
769
scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" );
772
scan->interpret_exit_last_row();
774
scan->setInterpretedCode(ndb_lastrow_code);
776
scan->getValue( EID_COLUMN, idbuf );
777
if ( NA->txn->execute( NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1 )) {
780
if (rc < NDB_MAX_RDNS && scan->nextResult( true, true ) == 0 )
781
*hasChildren = LDAP_COMPARE_TRUE;
783
*hasChildren = LDAP_COMPARE_FALSE;
789
ndb_has_subordinates(
792
int *hasSubordinates )
799
rc = ndb_dn2rdns( &e->e_nname, &rdns );
802
rc = ndb_thread_handle( op, &NA.ndb );
803
NA.txn = NA.ndb->startTransaction();
805
rc = ndb_has_children( &NA, hasSubordinates );
814
* sets the supported operational attributes (if required)
823
assert( rs->sr_entry != NULL );
825
for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next )
828
if ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
829
ad_inlist( slap_schema.si_ad_hasSubordinates, rs->sr_attrs ) )
831
int hasSubordinates, rc;
833
rc = ndb_has_subordinates( op, rs->sr_entry, &hasSubordinates );
834
if ( rc == LDAP_SUCCESS ) {
835
*ap = slap_operational_hasSubordinate( hasSubordinates == LDAP_COMPARE_TRUE );
836
assert( *ap != NULL );