Back to index

openldap  2.4.31
search.cpp
Go to the documentation of this file.
00001 /* search.cpp - tools for slap tools */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 2008-2012 The OpenLDAP Foundation.
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted only as authorized by the OpenLDAP
00010  * Public License.
00011  *
00012  * A copy of this license is available in the file LICENSE in the
00013  * top-level directory of the distribution or, alternatively, at
00014  * <http://www.OpenLDAP.org/license.html>.
00015  */
00016 /* ACKNOWLEDGEMENTS:
00017  * This work was initially developed by Howard Chu for inclusion
00018  * in OpenLDAP Software. This work was sponsored by MySQL.
00019  */
00020 
00021 #include "portable.h"
00022 
00023 #include <stdio.h>
00024 #include <ac/string.h>
00025 #include <ac/errno.h>
00026 
00027 #include "lutil.h"
00028 
00029 #include "back-ndb.h"
00030 
00031 static int
00032 ndb_dn2bound(
00033        NdbIndexScanOperation *myop,
00034        NdbRdns *rdns
00035 )
00036 {
00037        unsigned int i;
00038 
00039        /* Walk thru RDNs */
00040        for ( i=0; i<rdns->nr_num; i++ ) {
00041               /* Note: RDN_COLUMN offset not needed here */
00042               if ( myop->setBound( i, NdbIndexScanOperation::BoundEQ, rdns->nr_buf[i] ))
00043                      return LDAP_OTHER;
00044        }
00045        return i;
00046 }
00047 
00048 /* Check that all filter terms reside in the same table.
00049  *
00050  * If any of the filter terms are indexed, then only an IndexScan of the OL_index
00051  * will be performed. If none are indexed, but all the terms reside in a single
00052  * table, a Scan can be performed with the LDAP filter transformed into a ScanFilter.
00053  *
00054  * Otherwise, a full scan of the DB must be done with all filtering done by slapd.
00055  */
00056 static int ndb_filter_check( struct ndb_info *ni, Filter *f,
00057        NdbOcInfo **oci, int *indexed, int *ocfilter )
00058 {
00059        AttributeDescription *ad = NULL;
00060        ber_tag_t choice = f->f_choice;
00061        int rc = 0, undef = 0;
00062 
00063        if ( choice & SLAPD_FILTER_UNDEFINED ) {
00064               choice &= SLAPD_FILTER_MASK;
00065               undef = 1;
00066        }
00067        switch( choice ) {
00068        case LDAP_FILTER_AND:
00069        case LDAP_FILTER_OR:
00070        case LDAP_FILTER_NOT:
00071               for ( f = f->f_list; f; f=f->f_next ) {
00072                      rc = ndb_filter_check( ni, f, oci, indexed, ocfilter );
00073                      if ( rc ) return rc;
00074               }
00075               break;
00076        case LDAP_FILTER_PRESENT:
00077               ad = f->f_desc;
00078               break;
00079        case LDAP_FILTER_EQUALITY:
00080        case LDAP_FILTER_SUBSTRINGS:
00081        case LDAP_FILTER_GE:
00082        case LDAP_FILTER_LE:
00083        case LDAP_FILTER_APPROX:
00084               ad = f->f_av_desc;
00085               break;
00086        default:
00087               break;
00088        }
00089        if ( ad && !undef ) {
00090               NdbAttrInfo *ai;
00091               /* ObjectClass filtering is in dn2id table */
00092               if ( ad == slap_schema.si_ad_objectClass ) {
00093                      if ( choice == LDAP_FILTER_EQUALITY )
00094                             (*ocfilter)++;
00095                      return 0;
00096               }
00097               ai = ndb_ai_find( ni, ad->ad_type );
00098               if ( ai ) {
00099                      if ( ai->na_flag & NDB_INFO_INDEX )
00100                             (*indexed)++;
00101                      if ( *oci ) {
00102                             if ( ai->na_oi != *oci )
00103                                    rc = -1;
00104                      } else {
00105                             *oci = ai->na_oi;
00106                      }
00107               }
00108        }
00109        return rc;
00110 }
00111 
00112 static int ndb_filter_set( Operation *op, struct ndb_info *ni, Filter *f, int indexed,
00113        NdbIndexScanOperation *scan, NdbScanFilter *sf, int *bounds )
00114 {
00115        AttributeDescription *ad = NULL;
00116        ber_tag_t choice = f->f_choice;
00117        int undef = 0;
00118 
00119        if ( choice & SLAPD_FILTER_UNDEFINED ) {
00120               choice &= SLAPD_FILTER_MASK;
00121               undef = 1;
00122        }
00123        switch( choice ) {
00124        case LDAP_FILTER_NOT:
00125               /* no indexing for these */
00126               break;
00127        case LDAP_FILTER_OR:
00128               /* FIXME: these bounds aren't right. */
00129               if ( indexed ) {
00130                      scan->end_of_bound( (*bounds)++ );
00131               }
00132        case LDAP_FILTER_AND:
00133               if ( sf ) {
00134                      sf->begin( choice == LDAP_FILTER_OR ? NdbScanFilter::OR : NdbScanFilter::AND );
00135               }
00136               for ( f = f->f_list; f; f=f->f_next ) {
00137                      if ( ndb_filter_set( op, ni, f, indexed, scan, sf, bounds ))
00138                             return -1;
00139               }
00140               if ( sf ) {
00141                      sf->end();
00142               }
00143               break;
00144        case LDAP_FILTER_PRESENT:
00145               ad = f->f_desc;
00146               break;
00147        case LDAP_FILTER_EQUALITY:
00148        case LDAP_FILTER_SUBSTRINGS:
00149        case LDAP_FILTER_GE:
00150        case LDAP_FILTER_LE:
00151        case LDAP_FILTER_APPROX:
00152               ad = f->f_av_desc;
00153               break;
00154        default:
00155               break;
00156        }
00157        if ( ad && !undef ) {
00158               NdbAttrInfo *ai;
00159               /* ObjectClass filtering is in dn2id table */
00160               if ( ad == slap_schema.si_ad_objectClass ) {
00161                      return 0;
00162               }
00163               ai = ndb_ai_find( ni, ad->ad_type );
00164               if ( ai ) {
00165                      int rc;
00166                      if ( ai->na_flag & NDB_INFO_INDEX ) {
00167                             char *buf, *ptr;
00168                             NdbIndexScanOperation::BoundType bt;
00169 
00170                             switch(choice) {
00171                             case LDAP_FILTER_PRESENT:
00172                                    rc = scan->setBound( ai->na_ixcol - IDX_COLUMN,
00173                                           NdbIndexScanOperation::BoundGT, NULL );
00174                                    break;
00175                             case LDAP_FILTER_EQUALITY:
00176                             case LDAP_FILTER_APPROX:
00177                                    bt = NdbIndexScanOperation::BoundEQ;
00178                                    goto setit;
00179                             case LDAP_FILTER_GE:
00180                                    bt = NdbIndexScanOperation::BoundGE;
00181                                    goto setit;
00182                             case LDAP_FILTER_LE:
00183                                    bt = NdbIndexScanOperation::BoundLE;
00184                             setit:
00185                                    rc = f->f_av_value.bv_len+1;
00186                                    if ( ai->na_len > 255 )
00187                                           rc++;
00188                                    buf = (char *)op->o_tmpalloc( rc, op->o_tmpmemctx );
00189                                    rc = f->f_av_value.bv_len;
00190                                    buf[0] = rc & 0xff;
00191                                    ptr = buf+1;
00192                                    if ( ai->na_len > 255 ) {
00193                                           buf[1] = (rc >> 8);
00194                                           ptr++;
00195                                    }
00196                                    memcpy( ptr, f->f_av_value.bv_val, f->f_av_value.bv_len );
00197                                    rc = scan->setBound( ai->na_ixcol - IDX_COLUMN, bt, buf );
00198                                    op->o_tmpfree( buf, op->o_tmpmemctx );
00199                                    break;
00200                             default:
00201                                    break;
00202                             }
00203                      } else if ( sf ) {
00204                             char *buf, *ptr;
00205                             NdbScanFilter::BinaryCondition bc;
00206 
00207                             switch(choice) {
00208                             case LDAP_FILTER_PRESENT:
00209                                    rc = sf->isnotnull( ai->na_column );
00210                                    break;
00211                             case LDAP_FILTER_EQUALITY:
00212                             case LDAP_FILTER_APPROX:
00213                                    bc = NdbScanFilter::COND_EQ;
00214                                    goto setf;
00215                             case LDAP_FILTER_GE:
00216                                    bc = NdbScanFilter::COND_GE;
00217                                    goto setf;
00218                             case LDAP_FILTER_LE:
00219                                    bc = NdbScanFilter::COND_LE;
00220                             setf:
00221                                    rc = sf->cmp( bc, ai->na_column, f->f_av_value.bv_val, f->f_av_value.bv_len );
00222                                    break;
00223                             case LDAP_FILTER_SUBSTRINGS:
00224                                    rc = 0;
00225                                    if ( f->f_sub_initial.bv_val )
00226                                           rc += f->f_sub_initial.bv_len + 1;
00227                                    if ( f->f_sub_any ) {
00228                                           int i;
00229                                           if ( !rc ) rc++;
00230                                           for (i=0; f->f_sub_any[i].bv_val; i++)
00231                                                  rc += f->f_sub_any[i].bv_len + 1;
00232                                    }
00233                                    if ( f->f_sub_final.bv_val ) {
00234                                           if ( !rc ) rc++;
00235                                           rc += f->f_sub_final.bv_len;
00236                                    }
00237                                    buf = (char *)op->o_tmpalloc( rc+1, op->o_tmpmemctx );
00238                                    ptr = buf;
00239                                    if ( f->f_sub_initial.bv_val ) {
00240                                           memcpy( ptr, f->f_sub_initial.bv_val, f->f_sub_initial.bv_len );
00241                                           ptr += f->f_sub_initial.bv_len;
00242                                           *ptr++ = '%';
00243                                    }
00244                                    if ( f->f_sub_any ) {
00245                                           int i;
00246                                           if ( ptr == buf )
00247                                                  *ptr++ = '%';
00248                                           for (i=0; f->f_sub_any[i].bv_val; i++) {
00249                                                  memcpy( ptr, f->f_sub_any[i].bv_val, f->f_sub_any[i].bv_len );
00250                                                  ptr += f->f_sub_any[i].bv_len;
00251                                                  *ptr++ = '%';
00252                                           }
00253                                    }
00254                                    if ( f->f_sub_final.bv_val ) {
00255                                           if ( ptr == buf )
00256                                                  *ptr++ = '%';
00257                                           memcpy( ptr, f->f_sub_final.bv_val, f->f_sub_final.bv_len );
00258                                           ptr += f->f_sub_final.bv_len;
00259                                    }
00260                                    *ptr = '\0';
00261                                    rc = sf->cmp( NdbScanFilter::COND_LIKE, ai->na_column, buf, ptr - buf );
00262                                    op->o_tmpfree( buf, op->o_tmpmemctx );
00263                                    break;
00264                             }
00265                      }
00266               }
00267        }
00268        return 0;
00269 }
00270 
00271 static int ndb_oc_search( Operation *op, SlapReply *rs, Ndb *ndb, NdbTransaction *txn,
00272        NdbRdns *rbase, NdbOcInfo *oci, int indexed )
00273 {
00274        struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
00275        const NdbDictionary::Dictionary *myDict = ndb->getDictionary();
00276        const NdbDictionary::Table *myTable;
00277        const NdbDictionary::Index *myIndex;
00278        NdbIndexScanOperation *scan;
00279        NdbIndexOperation *ixop;
00280        NdbScanFilter *sf = NULL;
00281        struct berval *ocs;
00282        NdbRecAttr *scanID, *scanOC, *scanDN[NDB_MAX_RDNS];
00283        char dnBuf[2048], *ptr;
00284        NdbRdns rdns;
00285        NdbArgs NA;
00286        char idbuf[2*sizeof(ID)];
00287        char ocbuf[NDB_OC_BUFLEN];
00288        int i, rc, bounds;
00289        Entry e = {0};
00290        Uint64 eid;
00291        time_t stoptime;
00292        int manageDSAit;
00293 
00294        stoptime = op->o_time + op->ors_tlimit;
00295        manageDSAit = get_manageDSAit( op );
00296 
00297        myTable = myDict->getTable( oci->no_table.bv_val );
00298        if ( indexed ) { 
00299               scan = txn->getNdbIndexScanOperation( INDEX_NAME, DN2ID_TABLE );
00300               if ( !scan )
00301                      return LDAP_OTHER;
00302               scan->readTuples( NdbOperation::LM_CommittedRead );
00303        } else {
00304               myIndex = myDict->getIndex( "eid$unique", DN2ID_TABLE );
00305               if ( !myIndex ) {
00306                      Debug( LDAP_DEBUG_ANY, DN2ID_TABLE " eid index is missing!\n", 0, 0, 0 );
00307                      rs->sr_err = LDAP_OTHER;
00308                      goto leave;
00309               }
00310               scan = (NdbIndexScanOperation *)txn->getNdbScanOperation( myTable );
00311               if ( !scan )
00312                      return LDAP_OTHER;
00313               scan->readTuples( NdbOperation::LM_CommittedRead );
00314 #if 1
00315               sf = new NdbScanFilter(scan);
00316               if ( !sf )
00317                      return LDAP_OTHER;
00318               switch ( op->ors_filter->f_choice ) {
00319               case LDAP_FILTER_AND:
00320               case LDAP_FILTER_OR:
00321               case LDAP_FILTER_NOT:
00322                      break;
00323               default:
00324                      if ( sf->begin() < 0 ) {
00325                             rc = LDAP_OTHER;
00326                             goto leave;
00327                      }
00328               }
00329 #endif
00330        }
00331 
00332        bounds = 0;
00333        rc = ndb_filter_set( op, ni, op->ors_filter, indexed, scan, sf, &bounds );
00334        if ( rc )
00335               goto leave;
00336        if ( sf ) sf->end();
00337        
00338        scanID = scan->getValue( EID_COLUMN, idbuf );
00339        if ( indexed ) {
00340               scanOC = scan->getValue( OCS_COLUMN, ocbuf );
00341               for ( i=0; i<NDB_MAX_RDNS; i++ ) {
00342                      rdns.nr_buf[i][0] = '\0';
00343                      scanDN[i] = scan->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
00344               }
00345        }
00346 
00347        if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) {
00348               rs->sr_err = LDAP_OTHER;
00349               goto leave;
00350        }
00351 
00352        e.e_name.bv_val = dnBuf;
00353        NA.e = &e;
00354        NA.ndb = ndb;
00355        while ( scan->nextResult( true, true ) == 0 ) {
00356               NdbTransaction *tx2;
00357               if ( op->o_abandon ) {
00358                      rs->sr_err = SLAPD_ABANDON;
00359                      break;
00360               }
00361               if ( slapd_shutdown ) {
00362                      rs->sr_err = LDAP_UNAVAILABLE;
00363                      break;
00364               }
00365               if ( op->ors_tlimit != SLAP_NO_LIMIT &&
00366                      slap_get_time() > stoptime ) {
00367                      rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
00368                      break;
00369               }
00370 
00371               eid = scanID->u_64_value();
00372               e.e_id = eid;
00373               if ( !indexed ) {
00374                      tx2 = ndb->startTransaction( myTable );
00375                      if ( !tx2 ) {
00376                             rs->sr_err = LDAP_OTHER;
00377                             goto leave;
00378                      }
00379 
00380                      ixop = tx2->getNdbIndexOperation( myIndex );
00381                      if ( !ixop ) {
00382                             tx2->close();
00383                             rs->sr_err = LDAP_OTHER;
00384                             goto leave;
00385                      }
00386                      ixop->readTuple( NdbOperation::LM_CommittedRead );
00387                      ixop->equal( EID_COLUMN, eid );
00388 
00389                      scanOC = ixop->getValue( OCS_COLUMN, ocbuf );
00390                      for ( i=0; i<NDB_MAX_RDNS; i++ ) {
00391                             rdns.nr_buf[i][0] = '\0';
00392                             scanDN[i] = ixop->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
00393                      }
00394                      rc = tx2->execute( NdbTransaction::Commit, NdbOperation::AbortOnError, 1 );
00395                      tx2->close();
00396                      if ( rc ) {
00397                             rs->sr_err = LDAP_OTHER;
00398                             goto leave;
00399                      }
00400               }
00401 
00402               ocs = ndb_ref2oclist( ocbuf, op->o_tmpmemctx );
00403               for ( i=0; i<NDB_MAX_RDNS; i++ ) {
00404                      if ( scanDN[i]->isNULL() || !rdns.nr_buf[i][0] )
00405                             break;
00406               }
00407               rdns.nr_num = i;
00408 
00409               /* entry must be subordinate to the base */
00410               if ( i < rbase->nr_num ) {
00411                      continue;
00412               }
00413 
00414               ptr = dnBuf;
00415               for ( --i; i>=0; i-- ) {
00416                      char *buf;
00417                      int len;
00418                      buf = rdns.nr_buf[i];
00419                      len = *buf++;
00420                      ptr = lutil_strncopy( ptr, buf, len );
00421                      if ( i ) *ptr++ = ',';
00422               }
00423               *ptr = '\0';
00424               e.e_name.bv_len = ptr - dnBuf;
00425 
00426               /* More scope checks */
00427               /* If indexed, these can be moved into the ScanFilter */
00428               switch( op->ors_scope ) {
00429               case LDAP_SCOPE_ONELEVEL:
00430                      if ( rdns.nr_num != rbase->nr_num+1 )
00431                             continue;
00432               case LDAP_SCOPE_SUBORDINATE:
00433                      if ( rdns.nr_num == rbase->nr_num )
00434                             continue;
00435               case LDAP_SCOPE_SUBTREE:
00436               default:
00437                      if ( e.e_name.bv_len <= op->o_req_dn.bv_len ) {
00438                             if ( op->ors_scope != LDAP_SCOPE_SUBTREE ||
00439                                    strcasecmp( op->o_req_dn.bv_val, e.e_name.bv_val ))
00440                                    continue;
00441                      } else if ( strcasecmp( op->o_req_dn.bv_val, e.e_name.bv_val +
00442                             e.e_name.bv_len - op->o_req_dn.bv_len ))
00443                             continue;
00444               }
00445 
00446               dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx );
00447               {
00448 #if 1  /* NDBapi was broken here but seems to work now */
00449                      Ndb::Key_part_ptr keys[2];
00450                      char xbuf[512];
00451                      keys[0].ptr = &eid;
00452                      keys[0].len = sizeof(eid);
00453                      keys[1].ptr = NULL;
00454                      keys[1].len = 0;
00455                      tx2 = ndb->startTransaction( myTable, keys, xbuf, sizeof(xbuf));
00456 #else
00457                      tx2 = ndb->startTransaction( myTable );
00458 #endif
00459                      if ( !tx2 ) {
00460                             rs->sr_err = LDAP_OTHER;
00461                             goto leave;
00462                      }
00463                      NA.txn = tx2;
00464                      NA.ocs = ocs;
00465                      rc = ndb_entry_get_data( op, &NA, 0 );
00466                      tx2->close();
00467               }
00468               ber_bvarray_free_x( ocs, op->o_tmpmemctx );
00469               if ( !manageDSAit && is_entry_referral( &e )) {
00470                      BerVarray erefs = get_entry_referrals( op, &e );
00471                      rs->sr_ref = referral_rewrite( erefs, &e.e_name, NULL,
00472                             op->ors_scope == LDAP_SCOPE_ONELEVEL ?
00473                                    LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
00474                      rc = send_search_reference( op, rs );
00475                      ber_bvarray_free( rs->sr_ref );
00476                      ber_bvarray_free( erefs );
00477                      rs->sr_ref = NULL;
00478               } else if ( manageDSAit || !is_entry_glue( &e )) {
00479                      rc = test_filter( op, &e, op->ors_filter );
00480                      if ( rc == LDAP_COMPARE_TRUE ) {
00481                             rs->sr_entry = &e;
00482                             rs->sr_attrs = op->ors_attrs;
00483                             rs->sr_flags = 0;
00484                             rc = send_search_entry( op, rs );
00485                             rs->sr_entry = NULL;
00486                             rs->sr_attrs = NULL;
00487                      } else {
00488                             rc = 0;
00489                      }
00490               }
00491               attrs_free( e.e_attrs );
00492               e.e_attrs = NULL;
00493               op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx );
00494               if ( rc ) break;
00495        }
00496 leave:
00497        if ( sf ) delete sf;
00498        return rc;
00499 }
00500 
00501 extern "C"
00502 int ndb_back_search( Operation *op, SlapReply *rs )
00503 {
00504        struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
00505        NdbTransaction *txn;
00506        NdbIndexScanOperation *scan;
00507        NdbScanFilter *sf = NULL;
00508        Entry e = {0};
00509        int rc, i, ocfilter, indexed;
00510        struct berval matched;
00511        NdbRecAttr *scanID, *scanOC, *scanDN[NDB_MAX_RDNS];
00512        char dnBuf[2048], *ptr;
00513        char idbuf[2*sizeof(ID)];
00514        char ocbuf[NDB_OC_BUFLEN];
00515        NdbRdns rdns;
00516        NdbOcInfo *oci;
00517        NdbArgs NA;
00518        slap_mask_t mask;
00519        time_t stoptime;
00520        int manageDSAit;
00521 
00522        rc = ndb_thread_handle( op, &NA.ndb );
00523        rdns.nr_num = 0;
00524 
00525        manageDSAit = get_manageDSAit( op );
00526 
00527        txn = NA.ndb->startTransaction();
00528        if ( !txn ) {
00529               Debug( LDAP_DEBUG_TRACE,
00530                      LDAP_XSTRING(ndb_back_search) ": startTransaction failed: %s (%d)\n",
00531                      NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
00532               rs->sr_err = LDAP_OTHER;
00533               rs->sr_text = "internal error";
00534               goto leave;
00535        }
00536 
00537        NA.txn = txn;
00538        e.e_name = op->o_req_dn;
00539        e.e_nname = op->o_req_ndn;
00540        NA.e = &e;
00541        NA.rdns = &rdns;
00542        NA.ocs = NULL;
00543 
00544        rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
00545        if ( rs->sr_err ) {
00546               if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
00547                      rs->sr_matched = matched.bv_val;
00548                      if ( NA.ocs )
00549                             ndb_check_referral( op, rs, &NA );
00550               }
00551               goto leave;
00552        }
00553 
00554        if ( !access_allowed_mask( op, &e, slap_schema.si_ad_entry,
00555               NULL, ACL_SEARCH, NULL, &mask )) {
00556               if ( !ACL_GRANT( mask, ACL_DISCLOSE ))
00557                      rs->sr_err = LDAP_NO_SUCH_OBJECT;
00558               else
00559                      rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
00560               ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
00561               goto leave;
00562        }
00563 
00564        rs->sr_err = ndb_entry_get_data( op, &NA, 0 );
00565        ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
00566        if ( rs->sr_err )
00567               goto leave;
00568 
00569        if ( !manageDSAit && is_entry_referral( &e )) {
00570               rs->sr_ref = get_entry_referrals( op, &e );
00571               rs->sr_err = LDAP_REFERRAL;
00572               if ( rs->sr_ref )
00573                      rs->sr_flags |= REP_REF_MUSTBEFREED;
00574               rs->sr_matched = e.e_name.bv_val;
00575               attrs_free( e.e_attrs );
00576               e.e_attrs = NULL;
00577               goto leave;
00578        }
00579 
00580        if ( !manageDSAit && is_entry_glue( &e )) {
00581               rs->sr_err = LDAP_NO_SUCH_OBJECT;
00582               goto leave;
00583        }
00584 
00585        if ( get_assert( op ) && test_filter( op, &e, (Filter *)get_assertion( op )) !=
00586               LDAP_COMPARE_TRUE ) {
00587               rs->sr_err = LDAP_ASSERTION_FAILED;
00588               attrs_free( e.e_attrs );
00589               e.e_attrs = NULL;
00590               goto leave;
00591        }
00592 
00593        /* admin ignores tlimits */
00594        stoptime = op->o_time + op->ors_tlimit;
00595 
00596        if ( op->ors_scope == LDAP_SCOPE_BASE ) {
00597               rc = test_filter( op, &e, op->ors_filter );
00598               if ( rc == LDAP_COMPARE_TRUE ) {
00599                      rs->sr_entry = &e;
00600                      rs->sr_attrs = op->ors_attrs;
00601                      rs->sr_flags = 0;
00602                      send_search_entry( op, rs );
00603                      rs->sr_entry = NULL;
00604               }
00605               attrs_free( e.e_attrs );
00606               e.e_attrs = NULL;
00607               rs->sr_err = LDAP_SUCCESS;
00608               goto leave;
00609        } else {
00610               attrs_free( e.e_attrs );
00611               e.e_attrs = NULL;
00612               if ( rdns.nr_num == NDB_MAX_RDNS ) {
00613                      if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ||
00614                             op->ors_scope == LDAP_SCOPE_CHILDREN )
00615                      rs->sr_err = LDAP_SUCCESS;
00616                      goto leave;
00617               }
00618        }
00619 
00620        /* See if we can handle the filter. Filtering on objectClass is only done
00621         * in the DN2ID table scan. If all other filter terms reside in one table,
00622         * then we scan the OC table instead of the DN2ID table.
00623         */
00624        oci = NULL;
00625        indexed = 0;
00626        ocfilter = 0;
00627        rc = ndb_filter_check( ni, op->ors_filter, &oci, &indexed, &ocfilter );
00628        if ( rc ) {
00629               Debug( LDAP_DEBUG_TRACE, "ndb_back_search: "
00630                      "filter attributes from multiple tables, indexing ignored\n",
00631                      0, 0, 0 );
00632        } else if ( oci ) {
00633               rc = ndb_oc_search( op, rs, NA.ndb, txn, &rdns, oci, indexed );
00634               goto leave;
00635        }
00636 
00637        scan = txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
00638        if ( !scan ) {
00639               rs->sr_err = LDAP_OTHER;
00640               goto leave;
00641        }
00642        scan->readTuples( NdbOperation::LM_CommittedRead );
00643        rc = ndb_dn2bound( scan, &rdns );
00644 
00645        /* TODO: if ( ocfilter ) set up scanfilter for objectclass matches
00646         * column COND_LIKE "% <class> %"
00647         */
00648 
00649        switch( op->ors_scope ) {
00650        case LDAP_SCOPE_ONELEVEL:
00651               sf = new NdbScanFilter(scan);
00652               if ( sf->begin() < 0 ||
00653                      sf->cmp(NdbScanFilter::COND_NOT_LIKE, rc+3, "_%",
00654                             STRLENOF("_%")) < 0 ||
00655                      sf->end() < 0 ) {
00656                      rs->sr_err = LDAP_OTHER;
00657                      goto leave;
00658               }
00659               /* FALLTHRU */
00660        case LDAP_SCOPE_CHILDREN:
00661               /* Note: RDN_COLUMN offset not needed here */
00662               scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" );
00663               /* FALLTHRU */
00664        case LDAP_SCOPE_SUBTREE:
00665               break;
00666        }
00667        scanID = scan->getValue( EID_COLUMN, idbuf );
00668        scanOC = scan->getValue( OCS_COLUMN, ocbuf );
00669        for ( i=0; i<NDB_MAX_RDNS; i++ ) {
00670               rdns.nr_buf[i][0] = '\0';
00671               scanDN[i] = scan->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
00672        }
00673        if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) {
00674               rs->sr_err = LDAP_OTHER;
00675               goto leave;
00676        }
00677 
00678        e.e_name.bv_val = dnBuf;
00679        while ( scan->nextResult( true, true ) == 0 ) {
00680               if ( op->o_abandon ) {
00681                      rs->sr_err = SLAPD_ABANDON;
00682                      break;
00683               }
00684               if ( slapd_shutdown ) {
00685                      rs->sr_err = LDAP_UNAVAILABLE;
00686                      break;
00687               }
00688               if ( op->ors_tlimit != SLAP_NO_LIMIT &&
00689                      slap_get_time() > stoptime ) {
00690                      rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
00691                      break;
00692               }
00693               e.e_id = scanID->u_64_value();
00694               NA.ocs = ndb_ref2oclist( ocbuf, op->o_tmpmemctx );
00695               for ( i=0; i<NDB_MAX_RDNS; i++ ) {
00696                      if ( scanDN[i]->isNULL() || !rdns.nr_buf[i][0] )
00697                             break;
00698               }
00699               ptr = dnBuf;
00700               rdns.nr_num = i;
00701               for ( --i; i>=0; i-- ) {
00702                      char *buf;
00703                      int len;
00704                      buf = rdns.nr_buf[i];
00705                      len = *buf++;
00706                      ptr = lutil_strncopy( ptr, buf, len );
00707                      if ( i ) *ptr++ = ',';
00708               }
00709               *ptr = '\0';
00710               e.e_name.bv_len = ptr - dnBuf;
00711               dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx );
00712               NA.txn = NA.ndb->startTransaction();
00713               rc = ndb_entry_get_data( op, &NA, 0 );
00714               NA.txn->close();
00715               ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
00716               if ( !manageDSAit && is_entry_referral( &e )) {
00717                      BerVarray erefs = get_entry_referrals( op, &e );
00718                      rs->sr_ref = referral_rewrite( erefs, &e.e_name, NULL,
00719                             op->ors_scope == LDAP_SCOPE_ONELEVEL ?
00720                                    LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
00721                      rc = send_search_reference( op, rs );
00722                      ber_bvarray_free( rs->sr_ref );
00723                      ber_bvarray_free( erefs );
00724                      rs->sr_ref = NULL;
00725               } else if ( manageDSAit || !is_entry_glue( &e )) {
00726                      rc = test_filter( op, &e, op->ors_filter );
00727                      if ( rc == LDAP_COMPARE_TRUE ) {
00728                             rs->sr_entry = &e;
00729                             rs->sr_attrs = op->ors_attrs;
00730                             rs->sr_flags = 0;
00731                             rc = send_search_entry( op, rs );
00732                             rs->sr_entry = NULL;
00733                      } else {
00734                             rc = 0;
00735                      }
00736               }
00737               attrs_free( e.e_attrs );
00738               e.e_attrs = NULL;
00739               op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx );
00740               if ( rc ) break;
00741        }
00742 leave:
00743        if ( sf )
00744               delete sf;
00745        if ( txn )
00746               txn->close();
00747        send_ldap_result( op, rs );
00748        return rs->sr_err;
00749 }
00750 
00751 extern NdbInterpretedCode *ndb_lastrow_code;     /* init.cpp */
00752 
00753 extern "C" int
00754 ndb_has_children(
00755        NdbArgs *NA,
00756        int *hasChildren
00757 )
00758 {
00759        NdbIndexScanOperation *scan;
00760        char idbuf[2*sizeof(ID)];
00761        int rc;
00762 
00763        if ( NA->rdns->nr_num >= NDB_MAX_RDNS ) {
00764               *hasChildren = LDAP_COMPARE_FALSE;
00765               return 0;
00766        }
00767 
00768        scan = NA->txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
00769        if ( !scan )
00770               return LDAP_OTHER;
00771        scan->readTuples( NdbOperation::LM_Read, 0U, 0U, 1U );
00772        rc = ndb_dn2bound( scan, NA->rdns );
00773        if ( rc < NDB_MAX_RDNS ) {
00774               scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" );
00775        }
00776 #if 0
00777        scan->interpret_exit_last_row();
00778 #else
00779        scan->setInterpretedCode(ndb_lastrow_code);
00780 #endif
00781        scan->getValue( EID_COLUMN, idbuf );
00782        if ( NA->txn->execute( NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1 )) {
00783               return LDAP_OTHER;
00784        }
00785        if (rc < NDB_MAX_RDNS && scan->nextResult( true, true ) == 0 )
00786               *hasChildren = LDAP_COMPARE_TRUE;
00787        else
00788               *hasChildren = LDAP_COMPARE_FALSE;
00789        scan->close();
00790        return 0;
00791 }
00792 
00793 extern "C" int
00794 ndb_has_subordinates(
00795        Operation *op,
00796        Entry *e,
00797        int *hasSubordinates )
00798 {
00799        NdbArgs NA;
00800        NdbRdns rdns;
00801        int rc;
00802 
00803        NA.rdns = &rdns;
00804        rc = ndb_dn2rdns( &e->e_nname, &rdns );
00805 
00806        if ( rc == 0 ) {
00807               rc = ndb_thread_handle( op, &NA.ndb );
00808               NA.txn = NA.ndb->startTransaction();
00809               if ( NA.txn ) {
00810                      rc = ndb_has_children( &NA, hasSubordinates );
00811                      NA.txn->close();
00812               }
00813        }
00814 
00815        return rc;
00816 }
00817 
00818 /*
00819  * sets the supported operational attributes (if required)
00820  */
00821 extern "C" int
00822 ndb_operational(
00823        Operation     *op,
00824        SlapReply     *rs )
00825 {
00826        Attribute     **ap;
00827 
00828        assert( rs->sr_entry != NULL );
00829 
00830        for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next ) {
00831               if ( (*ap)->a_desc == slap_schema.si_ad_hasSubordinates ) {
00832                      break;
00833               }
00834        }
00835 
00836        if ( *ap == NULL &&
00837               attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_hasSubordinates ) == NULL &&
00838               ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
00839                      ad_inlist( slap_schema.si_ad_hasSubordinates, rs->sr_attrs ) ) )
00840        {
00841               int    hasSubordinates, rc;
00842 
00843               rc = ndb_has_subordinates( op, rs->sr_entry, &hasSubordinates );
00844               if ( rc == LDAP_SUCCESS ) {
00845                      *ap = slap_operational_hasSubordinate( hasSubordinates == LDAP_COMPARE_TRUE );
00846                      assert( *ap != NULL );
00847 
00848                      ap = &(*ap)->a_next;
00849               }
00850        }
00851 
00852        return LDAP_SUCCESS;
00853 }
00854