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-2014 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 855