1 /* search.cpp - tools for slap tools */ 2 /* OpenLDAP: pkg/ldap/servers/slapd/back-ndb/search.cpp,v 1.3.2.5 2010/04/13 20:23:35 kurt Exp */ 3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 4 * 5 * Copyright 2008-2010 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[32]; 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 } else { 487 rc = 0; 488 } 489 } 490 attrs_free( e.e_attrs ); 491 e.e_attrs = NULL; 492 op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx ); 493 if ( rc ) break; 494 } 495 leave: 496 if ( sf ) delete sf; 497 return rc; 498 } 499 500 extern "C" 501 int ndb_back_search( Operation *op, SlapReply *rs ) 502 { 503 struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private; 504 NdbTransaction *txn; 505 NdbIndexScanOperation *scan; 506 NdbScanFilter *sf = NULL; 507 Entry e = {0}; 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]; 514 NdbRdns rdns; 515 NdbOcInfo *oci; 516 NdbArgs NA; 517 slap_mask_t mask; 518 time_t stoptime; 519 int manageDSAit; 520 521 rc = ndb_thread_handle( op, &NA.ndb ); 522 rdns.nr_num = 0; 523 524 manageDSAit = get_manageDSAit( op ); 525 526 txn = NA.ndb->startTransaction(); 527 if ( !txn ) { 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"; 533 goto leave; 534 } 535 536 NA.txn = txn; 537 e.e_name = op->o_req_dn; 538 e.e_nname = op->o_req_ndn; 539 NA.e = &e; 540 NA.rdns = &rdns; 541 NA.ocs = NULL; 542 543 rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched ); 544 if ( rs->sr_err ) { 545 if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) { 546 rs->sr_matched = matched.bv_val; 547 if ( NA.ocs ) 548 ndb_check_referral( op, rs, &NA ); 549 } 550 goto leave; 551 } 552 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; 557 else 558 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 559 ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx ); 560 goto leave; 561 } 562 563 rs->sr_err = ndb_entry_get_data( op, &NA, 0 ); 564 ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx ); 565 if ( rs->sr_err ) 566 goto leave; 567 568 if ( !manageDSAit && is_entry_referral( &e )) { 569 rs->sr_ref = get_entry_referrals( op, &e ); 570 rs->sr_err = LDAP_REFERRAL; 571 if ( rs->sr_ref ) 572 rs->sr_flags |= REP_REF_MUSTBEFREED; 573 rs->sr_matched = e.e_name.bv_val; 574 attrs_free( e.e_attrs ); 575 e.e_attrs = NULL; 576 goto leave; 577 } 578 579 if ( !manageDSAit && is_entry_glue( &e )) { 580 rs->sr_err = LDAP_NO_SUCH_OBJECT; 581 goto leave; 582 } 583 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 ); 588 e.e_attrs = NULL; 589 goto leave; 590 } 591 592 /* admin ignores tlimits */ 593 stoptime = op->o_time + op->ors_tlimit; 594 595 if ( op->ors_scope == LDAP_SCOPE_BASE ) { 596 rc = test_filter( op, &e, op->ors_filter ); 597 if ( rc == LDAP_COMPARE_TRUE ) { 598 rs->sr_entry = &e; 599 rs->sr_attrs = op->ors_attrs; 600 rs->sr_flags = 0; 601 send_search_entry( op, rs ); 602 rs->sr_entry = NULL; 603 } 604 attrs_free( e.e_attrs ); 605 e.e_attrs = NULL; 606 rs->sr_err = LDAP_SUCCESS; 607 goto leave; 608 } else { 609 attrs_free( e.e_attrs ); 610 e.e_attrs = NULL; 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; 615 goto leave; 616 } 617 } 618 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. 622 */ 623 oci = NULL; 624 indexed = 0; 625 ocfilter = 0; 626 rc = ndb_filter_check( ni, op->ors_filter, &oci, &indexed, &ocfilter ); 627 if ( rc ) { 628 Debug( LDAP_DEBUG_TRACE, "ndb_back_search: " 629 "filter attributes from multiple tables, indexing ignored\n", 630 0, 0, 0 ); 631 } else if ( oci ) { 632 rc = ndb_oc_search( op, rs, NA.ndb, txn, &rdns, oci, indexed ); 633 goto leave; 634 } 635 636 scan = txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE ); 637 if ( !scan ) { 638 rs->sr_err = LDAP_OTHER; 639 goto leave; 640 } 641 scan->readTuples( NdbOperation::LM_CommittedRead ); 642 rc = ndb_dn2bound( scan, &rdns ); 643 644 /* TODO: if ( ocfilter ) set up scanfilter for objectclass matches 645 * column COND_LIKE "% <class> %" 646 */ 647 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 || 654 sf->end() < 0 ) { 655 rs->sr_err = LDAP_OTHER; 656 goto leave; 657 } 658 /* FALLTHRU */ 659 case LDAP_SCOPE_CHILDREN: 660 /* Note: RDN_COLUMN offset not needed here */ 661 scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" ); 662 /* FALLTHRU */ 663 case LDAP_SCOPE_SUBTREE: 664 break; 665 } 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] ); 671 } 672 if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) { 673 rs->sr_err = LDAP_OTHER; 674 goto leave; 675 } 676 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; 681 break; 682 } 683 if ( slapd_shutdown ) { 684 rs->sr_err = LDAP_UNAVAILABLE; 685 break; 686 } 687 if ( op->ors_tlimit != SLAP_NO_LIMIT && 688 slap_get_time() > stoptime ) { 689 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED; 690 break; 691 } 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] ) 696 break; 697 } 698 ptr = dnBuf; 699 rdns.nr_num = i; 700 for ( --i; i>=0; i-- ) { 701 char *buf; 702 int len; 703 buf = rdns.nr_buf[i]; 704 len = *buf++; 705 ptr = lutil_strncopy( ptr, buf, len ); 706 if ( i ) *ptr++ = ','; 707 } 708 *ptr = '\0'; 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 ); 713 NA.txn->close(); 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 ); 723 rs->sr_ref = NULL; 724 } else if ( manageDSAit || !is_entry_glue( &e )) { 725 rc = test_filter( op, &e, op->ors_filter ); 726 if ( rc == LDAP_COMPARE_TRUE ) { 727 rs->sr_entry = &e; 728 rs->sr_attrs = op->ors_attrs; 729 rs->sr_flags = 0; 730 rc = send_search_entry( op, rs ); 731 rs->sr_entry = NULL; 732 } else { 733 rc = 0; 734 } 735 } 736 attrs_free( e.e_attrs ); 737 e.e_attrs = NULL; 738 op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx ); 739 if ( rc ) break; 740 } 741 leave: 742 if ( sf ) 743 delete sf; 744 if ( txn ) 745 txn->close(); 746 send_ldap_result( op, rs ); 747 return rs->sr_err; 748 } 749 750 extern NdbInterpretedCode *ndb_lastrow_code; /* init.cpp */ 751 752 extern "C" int 753 ndb_has_children( 754 NdbArgs *NA, 755 int *hasChildren 756 ) 757 { 758 NdbIndexScanOperation *scan; 759 char idbuf[2*sizeof(ID)]; 760 int rc; 761 762 if ( NA->rdns->nr_num >= NDB_MAX_RDNS ) { 763 *hasChildren = LDAP_COMPARE_FALSE; 764 return 0; 765 } 766 767 scan = NA->txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE ); 768 if ( !scan ) 769 return LDAP_OTHER; 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" ); 774 } 775 #if 0 776 scan->interpret_exit_last_row(); 777 #else 778 scan->setInterpretedCode(ndb_lastrow_code); 779 #endif 780 scan->getValue( EID_COLUMN, idbuf ); 781 if ( NA->txn->execute( NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1 )) { 782 return LDAP_OTHER; 783 } 784 if (rc < NDB_MAX_RDNS && scan->nextResult( true, true ) == 0 ) 785 *hasChildren = LDAP_COMPARE_TRUE; 786 else 787 *hasChildren = LDAP_COMPARE_FALSE; 788 scan->close(); 789 return 0; 790 } 791 792 extern "C" int 793 ndb_has_subordinates( 794 Operation *op, 795 Entry *e, 796 int *hasSubordinates ) 797 { 798 NdbArgs NA; 799 NdbRdns rdns; 800 int rc; 801 802 NA.rdns = &rdns; 803 rc = ndb_dn2rdns( &e->e_nname, &rdns ); 804 805 if ( rc == 0 ) { 806 rc = ndb_thread_handle( op, &NA.ndb ); 807 NA.txn = NA.ndb->startTransaction(); 808 if ( NA.txn ) { 809 rc = ndb_has_children( &NA, hasSubordinates ); 810 NA.txn->close(); 811 } 812 } 813 814 return rc; 815 } 816 817 /* 818 * sets the supported operational attributes (if required) 819 */ 820 extern "C" int 821 ndb_operational( 822 Operation *op, 823 SlapReply *rs ) 824 { 825 Attribute **ap; 826 827 assert( rs->sr_entry != NULL ); 828 829 for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next ) 830 /* just count */ ; 831 832 if ( SLAP_OPATTRS( rs->sr_attr_flags ) || 833 ad_inlist( slap_schema.si_ad_hasSubordinates, rs->sr_attrs ) ) 834 { 835 int hasSubordinates, rc; 836 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 ); 841 842 ap = &(*ap)->a_next; 843 } 844 } 845 846 return LDAP_SUCCESS; 847 } 848 849