/* search.cpp - tools for slap tools */ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * * Copyright 2008-2021 The OpenLDAP Foundation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted only as authorized by the OpenLDAP * Public License. * * A copy of this license is available in the file LICENSE in the * top-level directory of the distribution or, alternatively, at * . */ /* ACKNOWLEDGEMENTS: * This work was initially developed by Howard Chu for inclusion * in OpenLDAP Software. This work was sponsored by MySQL. */ #include "portable.h" #include #include #include #include "lutil.h" #include "back-ndb.h" static int ndb_dn2bound( NdbIndexScanOperation *myop, NdbRdns *rdns ) { unsigned int i; /* Walk thru RDNs */ for ( i=0; inr_num; i++ ) { /* Note: RDN_COLUMN offset not needed here */ if ( myop->setBound( i, NdbIndexScanOperation::BoundEQ, rdns->nr_buf[i] )) return LDAP_OTHER; } return i; } /* Check that all filter terms reside in the same table. * * If any of the filter terms are indexed, then only an IndexScan of the OL_index * will be performed. If none are indexed, but all the terms reside in a single * table, a Scan can be performed with the LDAP filter transformed into a ScanFilter. * * Otherwise, a full scan of the DB must be done with all filtering done by slapd. */ static int ndb_filter_check( struct ndb_info *ni, Filter *f, NdbOcInfo **oci, int *indexed, int *ocfilter ) { AttributeDescription *ad = NULL; ber_tag_t choice = f->f_choice; int rc = 0, undef = 0; if ( choice & SLAPD_FILTER_UNDEFINED ) { choice &= SLAPD_FILTER_MASK; undef = 1; } switch( choice ) { case LDAP_FILTER_AND: case LDAP_FILTER_OR: case LDAP_FILTER_NOT: for ( f = f->f_list; f; f=f->f_next ) { rc = ndb_filter_check( ni, f, oci, indexed, ocfilter ); if ( rc ) return rc; } break; case LDAP_FILTER_PRESENT: ad = f->f_desc; break; case LDAP_FILTER_EQUALITY: case LDAP_FILTER_SUBSTRINGS: case LDAP_FILTER_GE: case LDAP_FILTER_LE: case LDAP_FILTER_APPROX: ad = f->f_av_desc; break; default: break; } if ( ad && !undef ) { NdbAttrInfo *ai; /* ObjectClass filtering is in dn2id table */ if ( ad == slap_schema.si_ad_objectClass ) { if ( choice == LDAP_FILTER_EQUALITY ) (*ocfilter)++; return 0; } ai = ndb_ai_find( ni, ad->ad_type ); if ( ai ) { if ( ai->na_flag & NDB_INFO_INDEX ) (*indexed)++; if ( *oci ) { if ( ai->na_oi != *oci ) rc = -1; } else { *oci = ai->na_oi; } } } return rc; } static int ndb_filter_set( Operation *op, struct ndb_info *ni, Filter *f, int indexed, NdbIndexScanOperation *scan, NdbScanFilter *sf, int *bounds ) { AttributeDescription *ad = NULL; ber_tag_t choice = f->f_choice; int undef = 0; if ( choice & SLAPD_FILTER_UNDEFINED ) { choice &= SLAPD_FILTER_MASK; undef = 1; } switch( choice ) { case LDAP_FILTER_NOT: /* no indexing for these */ break; case LDAP_FILTER_OR: /* FIXME: these bounds aren't right. */ if ( indexed ) { scan->end_of_bound( (*bounds)++ ); } case LDAP_FILTER_AND: if ( sf ) { sf->begin( choice == LDAP_FILTER_OR ? NdbScanFilter::OR : NdbScanFilter::AND ); } for ( f = f->f_list; f; f=f->f_next ) { if ( ndb_filter_set( op, ni, f, indexed, scan, sf, bounds )) return -1; } if ( sf ) { sf->end(); } break; case LDAP_FILTER_PRESENT: ad = f->f_desc; break; case LDAP_FILTER_EQUALITY: case LDAP_FILTER_SUBSTRINGS: case LDAP_FILTER_GE: case LDAP_FILTER_LE: case LDAP_FILTER_APPROX: ad = f->f_av_desc; break; default: break; } if ( ad && !undef ) { NdbAttrInfo *ai; /* ObjectClass filtering is in dn2id table */ if ( ad == slap_schema.si_ad_objectClass ) { return 0; } ai = ndb_ai_find( ni, ad->ad_type ); if ( ai ) { int rc; if ( ai->na_flag & NDB_INFO_INDEX ) { char *buf, *ptr; NdbIndexScanOperation::BoundType bt; switch(choice) { case LDAP_FILTER_PRESENT: rc = scan->setBound( ai->na_ixcol - IDX_COLUMN, NdbIndexScanOperation::BoundGT, NULL ); break; case LDAP_FILTER_EQUALITY: case LDAP_FILTER_APPROX: bt = NdbIndexScanOperation::BoundEQ; goto setit; case LDAP_FILTER_GE: bt = NdbIndexScanOperation::BoundGE; goto setit; case LDAP_FILTER_LE: bt = NdbIndexScanOperation::BoundLE; setit: rc = f->f_av_value.bv_len+1; if ( ai->na_len > 255 ) rc++; buf = (char *)op->o_tmpalloc( rc, op->o_tmpmemctx ); rc = f->f_av_value.bv_len; buf[0] = rc & 0xff; ptr = buf+1; if ( ai->na_len > 255 ) { buf[1] = (rc >> 8); ptr++; } memcpy( ptr, f->f_av_value.bv_val, f->f_av_value.bv_len ); rc = scan->setBound( ai->na_ixcol - IDX_COLUMN, bt, buf ); op->o_tmpfree( buf, op->o_tmpmemctx ); break; default: break; } } else if ( sf ) { char *buf, *ptr; NdbScanFilter::BinaryCondition bc; switch(choice) { case LDAP_FILTER_PRESENT: rc = sf->isnotnull( ai->na_column ); break; case LDAP_FILTER_EQUALITY: case LDAP_FILTER_APPROX: bc = NdbScanFilter::COND_EQ; goto setf; case LDAP_FILTER_GE: bc = NdbScanFilter::COND_GE; goto setf; case LDAP_FILTER_LE: bc = NdbScanFilter::COND_LE; setf: rc = sf->cmp( bc, ai->na_column, f->f_av_value.bv_val, f->f_av_value.bv_len ); break; case LDAP_FILTER_SUBSTRINGS: rc = 0; if ( f->f_sub_initial.bv_val ) rc += f->f_sub_initial.bv_len + 1; if ( f->f_sub_any ) { int i; if ( !rc ) rc++; for (i=0; f->f_sub_any[i].bv_val; i++) rc += f->f_sub_any[i].bv_len + 1; } if ( f->f_sub_final.bv_val ) { if ( !rc ) rc++; rc += f->f_sub_final.bv_len; } buf = (char *)op->o_tmpalloc( rc+1, op->o_tmpmemctx ); ptr = buf; if ( f->f_sub_initial.bv_val ) { memcpy( ptr, f->f_sub_initial.bv_val, f->f_sub_initial.bv_len ); ptr += f->f_sub_initial.bv_len; *ptr++ = '%'; } if ( f->f_sub_any ) { int i; if ( ptr == buf ) *ptr++ = '%'; for (i=0; f->f_sub_any[i].bv_val; i++) { memcpy( ptr, f->f_sub_any[i].bv_val, f->f_sub_any[i].bv_len ); ptr += f->f_sub_any[i].bv_len; *ptr++ = '%'; } } if ( f->f_sub_final.bv_val ) { if ( ptr == buf ) *ptr++ = '%'; memcpy( ptr, f->f_sub_final.bv_val, f->f_sub_final.bv_len ); ptr += f->f_sub_final.bv_len; } *ptr = '\0'; rc = sf->cmp( NdbScanFilter::COND_LIKE, ai->na_column, buf, ptr - buf ); op->o_tmpfree( buf, op->o_tmpmemctx ); break; } } } } return 0; } static int ndb_oc_search( Operation *op, SlapReply *rs, Ndb *ndb, NdbTransaction *txn, NdbRdns *rbase, NdbOcInfo *oci, int indexed ) { struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private; const NdbDictionary::Dictionary *myDict = ndb->getDictionary(); const NdbDictionary::Table *myTable; const NdbDictionary::Index *myIndex; NdbIndexScanOperation *scan; NdbIndexOperation *ixop; NdbScanFilter *sf = NULL; struct berval *ocs; NdbRecAttr *scanID, *scanOC, *scanDN[NDB_MAX_RDNS]; char dnBuf[2048], *ptr; NdbRdns rdns; NdbArgs NA; char idbuf[2*sizeof(ID)]; char ocbuf[NDB_OC_BUFLEN]; int i, rc, bounds; Entry e = {0}; Uint64 eid; time_t stoptime; int manageDSAit; stoptime = op->o_time + op->ors_tlimit; manageDSAit = get_manageDSAit( op ); myTable = myDict->getTable( oci->no_table.bv_val ); if ( indexed ) { scan = txn->getNdbIndexScanOperation( INDEX_NAME, DN2ID_TABLE ); if ( !scan ) return LDAP_OTHER; scan->readTuples( NdbOperation::LM_CommittedRead ); } else { myIndex = myDict->getIndex( "eid$unique", DN2ID_TABLE ); if ( !myIndex ) { Debug( LDAP_DEBUG_ANY, DN2ID_TABLE " eid index is missing!\n", 0, 0, 0 ); rs->sr_err = LDAP_OTHER; goto leave; } scan = (NdbIndexScanOperation *)txn->getNdbScanOperation( myTable ); if ( !scan ) return LDAP_OTHER; scan->readTuples( NdbOperation::LM_CommittedRead ); #if 1 sf = new NdbScanFilter(scan); if ( !sf ) return LDAP_OTHER; switch ( op->ors_filter->f_choice ) { case LDAP_FILTER_AND: case LDAP_FILTER_OR: case LDAP_FILTER_NOT: break; default: if ( sf->begin() < 0 ) { rc = LDAP_OTHER; goto leave; } } #endif } bounds = 0; rc = ndb_filter_set( op, ni, op->ors_filter, indexed, scan, sf, &bounds ); if ( rc ) goto leave; if ( sf ) sf->end(); scanID = scan->getValue( EID_COLUMN, idbuf ); if ( indexed ) { scanOC = scan->getValue( OCS_COLUMN, ocbuf ); for ( i=0; igetValue( RDN_COLUMN+i, rdns.nr_buf[i] ); } } if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) { rs->sr_err = LDAP_OTHER; goto leave; } e.e_name.bv_val = dnBuf; NA.e = &e; NA.ndb = ndb; while ( scan->nextResult( true, true ) == 0 ) { NdbTransaction *tx2; if ( op->o_abandon ) { rs->sr_err = SLAPD_ABANDON; break; } if ( slapd_shutdown ) { rs->sr_err = LDAP_UNAVAILABLE; break; } if ( op->ors_tlimit != SLAP_NO_LIMIT && slap_get_time() > stoptime ) { rs->sr_err = LDAP_TIMELIMIT_EXCEEDED; break; } eid = scanID->u_64_value(); e.e_id = eid; if ( !indexed ) { tx2 = ndb->startTransaction( myTable ); if ( !tx2 ) { rs->sr_err = LDAP_OTHER; goto leave; } ixop = tx2->getNdbIndexOperation( myIndex ); if ( !ixop ) { tx2->close(); rs->sr_err = LDAP_OTHER; goto leave; } ixop->readTuple( NdbOperation::LM_CommittedRead ); ixop->equal( EID_COLUMN, eid ); scanOC = ixop->getValue( OCS_COLUMN, ocbuf ); for ( i=0; igetValue( RDN_COLUMN+i, rdns.nr_buf[i] ); } rc = tx2->execute( NdbTransaction::Commit, NdbOperation::AbortOnError, 1 ); tx2->close(); if ( rc ) { rs->sr_err = LDAP_OTHER; goto leave; } } ocs = ndb_ref2oclist( ocbuf, op->o_tmpmemctx ); for ( i=0; iisNULL() || !rdns.nr_buf[i][0] ) break; } rdns.nr_num = i; /* entry must be subordinate to the base */ if ( i < rbase->nr_num ) { continue; } ptr = dnBuf; for ( --i; i>=0; i-- ) { char *buf; int len; buf = rdns.nr_buf[i]; len = *buf++; ptr = lutil_strncopy( ptr, buf, len ); if ( i ) *ptr++ = ','; } *ptr = '\0'; e.e_name.bv_len = ptr - dnBuf; /* More scope checks */ /* If indexed, these can be moved into the ScanFilter */ switch( op->ors_scope ) { case LDAP_SCOPE_ONELEVEL: if ( rdns.nr_num != rbase->nr_num+1 ) continue; case LDAP_SCOPE_SUBORDINATE: if ( rdns.nr_num == rbase->nr_num ) continue; case LDAP_SCOPE_SUBTREE: default: if ( e.e_name.bv_len <= op->o_req_dn.bv_len ) { if ( op->ors_scope != LDAP_SCOPE_SUBTREE || strcasecmp( op->o_req_dn.bv_val, e.e_name.bv_val )) continue; } else if ( strcasecmp( op->o_req_dn.bv_val, e.e_name.bv_val + e.e_name.bv_len - op->o_req_dn.bv_len )) continue; } dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx ); { #if 1 /* NDBapi was broken here but seems to work now */ Ndb::Key_part_ptr keys[2]; char xbuf[512]; keys[0].ptr = &eid; keys[0].len = sizeof(eid); keys[1].ptr = NULL; keys[1].len = 0; tx2 = ndb->startTransaction( myTable, keys, xbuf, sizeof(xbuf)); #else tx2 = ndb->startTransaction( myTable ); #endif if ( !tx2 ) { rs->sr_err = LDAP_OTHER; goto leave; } NA.txn = tx2; NA.ocs = ocs; rc = ndb_entry_get_data( op, &NA, 0 ); tx2->close(); } ber_bvarray_free_x( ocs, op->o_tmpmemctx ); if ( !manageDSAit && is_entry_referral( &e )) { BerVarray erefs = get_entry_referrals( op, &e ); rs->sr_ref = referral_rewrite( erefs, &e.e_name, NULL, op->ors_scope == LDAP_SCOPE_ONELEVEL ? LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE ); rc = send_search_reference( op, rs ); ber_bvarray_free( rs->sr_ref ); ber_bvarray_free( erefs ); rs->sr_ref = NULL; } else if ( manageDSAit || !is_entry_glue( &e )) { rc = test_filter( op, &e, op->ors_filter ); if ( rc == LDAP_COMPARE_TRUE ) { rs->sr_entry = &e; rs->sr_attrs = op->ors_attrs; rs->sr_flags = 0; rc = send_search_entry( op, rs ); rs->sr_entry = NULL; rs->sr_attrs = NULL; } else { rc = 0; } } attrs_free( e.e_attrs ); e.e_attrs = NULL; op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx ); if ( rc ) break; } leave: if ( sf ) delete sf; return rc; } extern "C" int ndb_back_search( Operation *op, SlapReply *rs ) { struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private; NdbTransaction *txn; NdbIndexScanOperation *scan; NdbScanFilter *sf = NULL; Entry e = {0}; int rc, i, ocfilter, indexed; struct berval matched; NdbRecAttr *scanID, *scanOC, *scanDN[NDB_MAX_RDNS]; char dnBuf[2048], *ptr; char idbuf[2*sizeof(ID)]; char ocbuf[NDB_OC_BUFLEN]; NdbRdns rdns; NdbOcInfo *oci; NdbArgs NA; slap_mask_t mask; time_t stoptime; int manageDSAit; rc = ndb_thread_handle( op, &NA.ndb ); rdns.nr_num = 0; manageDSAit = get_manageDSAit( op ); txn = NA.ndb->startTransaction(); if ( !txn ) { Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(ndb_back_search) ": startTransaction failed: %s (%d)\n", NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 ); rs->sr_err = LDAP_OTHER; rs->sr_text = "internal error"; goto leave; } NA.txn = txn; e.e_name = op->o_req_dn; e.e_nname = op->o_req_ndn; NA.e = &e; NA.rdns = &rdns; NA.ocs = NULL; rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched ); if ( rs->sr_err ) { if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) { rs->sr_matched = matched.bv_val; if ( NA.ocs ) ndb_check_referral( op, rs, &NA ); } goto leave; } if ( !access_allowed_mask( op, &e, slap_schema.si_ad_entry, NULL, ACL_SEARCH, NULL, &mask )) { if ( !ACL_GRANT( mask, ACL_DISCLOSE )) rs->sr_err = LDAP_NO_SUCH_OBJECT; else rs->sr_err = LDAP_INSUFFICIENT_ACCESS; ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx ); goto leave; } rs->sr_err = ndb_entry_get_data( op, &NA, 0 ); ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx ); if ( rs->sr_err ) goto leave; if ( !manageDSAit && is_entry_referral( &e )) { rs->sr_ref = get_entry_referrals( op, &e ); rs->sr_err = LDAP_REFERRAL; if ( rs->sr_ref ) rs->sr_flags |= REP_REF_MUSTBEFREED; rs->sr_matched = e.e_name.bv_val; attrs_free( e.e_attrs ); e.e_attrs = NULL; goto leave; } if ( !manageDSAit && is_entry_glue( &e )) { rs->sr_err = LDAP_NO_SUCH_OBJECT; goto leave; } if ( get_assert( op ) && test_filter( op, &e, (Filter *)get_assertion( op )) != LDAP_COMPARE_TRUE ) { rs->sr_err = LDAP_ASSERTION_FAILED; attrs_free( e.e_attrs ); e.e_attrs = NULL; goto leave; } /* admin ignores tlimits */ stoptime = op->o_time + op->ors_tlimit; if ( op->ors_scope == LDAP_SCOPE_BASE ) { rc = test_filter( op, &e, op->ors_filter ); if ( rc == LDAP_COMPARE_TRUE ) { rs->sr_entry = &e; rs->sr_attrs = op->ors_attrs; rs->sr_flags = 0; send_search_entry( op, rs ); rs->sr_entry = NULL; } attrs_free( e.e_attrs ); e.e_attrs = NULL; rs->sr_err = LDAP_SUCCESS; goto leave; } else { attrs_free( e.e_attrs ); e.e_attrs = NULL; if ( rdns.nr_num == NDB_MAX_RDNS ) { if ( op->ors_scope == LDAP_SCOPE_ONELEVEL || op->ors_scope == LDAP_SCOPE_CHILDREN ) rs->sr_err = LDAP_SUCCESS; goto leave; } } /* See if we can handle the filter. Filtering on objectClass is only done * in the DN2ID table scan. If all other filter terms reside in one table, * then we scan the OC table instead of the DN2ID table. */ oci = NULL; indexed = 0; ocfilter = 0; rc = ndb_filter_check( ni, op->ors_filter, &oci, &indexed, &ocfilter ); if ( rc ) { Debug( LDAP_DEBUG_TRACE, "ndb_back_search: " "filter attributes from multiple tables, indexing ignored\n", 0, 0, 0 ); } else if ( oci ) { rc = ndb_oc_search( op, rs, NA.ndb, txn, &rdns, oci, indexed ); goto leave; } scan = txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE ); if ( !scan ) { rs->sr_err = LDAP_OTHER; goto leave; } scan->readTuples( NdbOperation::LM_CommittedRead ); rc = ndb_dn2bound( scan, &rdns ); /* TODO: if ( ocfilter ) set up scanfilter for objectclass matches * column COND_LIKE "% %" */ switch( op->ors_scope ) { case LDAP_SCOPE_ONELEVEL: sf = new NdbScanFilter(scan); if ( sf->begin() < 0 || sf->cmp(NdbScanFilter::COND_NOT_LIKE, rc+3, "_%", STRLENOF("_%")) < 0 || sf->end() < 0 ) { rs->sr_err = LDAP_OTHER; goto leave; } /* FALLTHRU */ case LDAP_SCOPE_CHILDREN: /* Note: RDN_COLUMN offset not needed here */ scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" ); /* FALLTHRU */ case LDAP_SCOPE_SUBTREE: break; } scanID = scan->getValue( EID_COLUMN, idbuf ); scanOC = scan->getValue( OCS_COLUMN, ocbuf ); for ( i=0; igetValue( RDN_COLUMN+i, rdns.nr_buf[i] ); } if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) { rs->sr_err = LDAP_OTHER; goto leave; } e.e_name.bv_val = dnBuf; while ( scan->nextResult( true, true ) == 0 ) { if ( op->o_abandon ) { rs->sr_err = SLAPD_ABANDON; break; } if ( slapd_shutdown ) { rs->sr_err = LDAP_UNAVAILABLE; break; } if ( op->ors_tlimit != SLAP_NO_LIMIT && slap_get_time() > stoptime ) { rs->sr_err = LDAP_TIMELIMIT_EXCEEDED; break; } e.e_id = scanID->u_64_value(); NA.ocs = ndb_ref2oclist( ocbuf, op->o_tmpmemctx ); for ( i=0; iisNULL() || !rdns.nr_buf[i][0] ) break; } ptr = dnBuf; rdns.nr_num = i; for ( --i; i>=0; i-- ) { char *buf; int len; buf = rdns.nr_buf[i]; len = *buf++; ptr = lutil_strncopy( ptr, buf, len ); if ( i ) *ptr++ = ','; } *ptr = '\0'; e.e_name.bv_len = ptr - dnBuf; dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx ); NA.txn = NA.ndb->startTransaction(); rc = ndb_entry_get_data( op, &NA, 0 ); NA.txn->close(); ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx ); if ( !manageDSAit && is_entry_referral( &e )) { BerVarray erefs = get_entry_referrals( op, &e ); rs->sr_ref = referral_rewrite( erefs, &e.e_name, NULL, op->ors_scope == LDAP_SCOPE_ONELEVEL ? LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE ); rc = send_search_reference( op, rs ); ber_bvarray_free( rs->sr_ref ); ber_bvarray_free( erefs ); rs->sr_ref = NULL; } else if ( manageDSAit || !is_entry_glue( &e )) { rc = test_filter( op, &e, op->ors_filter ); if ( rc == LDAP_COMPARE_TRUE ) { rs->sr_entry = &e; rs->sr_attrs = op->ors_attrs; rs->sr_flags = 0; rc = send_search_entry( op, rs ); rs->sr_entry = NULL; } else { rc = 0; } } attrs_free( e.e_attrs ); e.e_attrs = NULL; op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx ); if ( rc ) break; } leave: if ( sf ) delete sf; if ( txn ) txn->close(); send_ldap_result( op, rs ); return rs->sr_err; } extern NdbInterpretedCode *ndb_lastrow_code; /* init.cpp */ extern "C" int ndb_has_children( NdbArgs *NA, int *hasChildren ) { NdbIndexScanOperation *scan; char idbuf[2*sizeof(ID)]; int rc; if ( NA->rdns->nr_num >= NDB_MAX_RDNS ) { *hasChildren = LDAP_COMPARE_FALSE; return 0; } scan = NA->txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE ); if ( !scan ) return LDAP_OTHER; scan->readTuples( NdbOperation::LM_Read, 0U, 0U, 1U ); rc = ndb_dn2bound( scan, NA->rdns ); if ( rc < NDB_MAX_RDNS ) { scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" ); } #if 0 scan->interpret_exit_last_row(); #else scan->setInterpretedCode(ndb_lastrow_code); #endif scan->getValue( EID_COLUMN, idbuf ); if ( NA->txn->execute( NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1 )) { return LDAP_OTHER; } if (rc < NDB_MAX_RDNS && scan->nextResult( true, true ) == 0 ) *hasChildren = LDAP_COMPARE_TRUE; else *hasChildren = LDAP_COMPARE_FALSE; scan->close(); return 0; } extern "C" int ndb_has_subordinates( Operation *op, Entry *e, int *hasSubordinates ) { NdbArgs NA; NdbRdns rdns; int rc; NA.rdns = &rdns; rc = ndb_dn2rdns( &e->e_nname, &rdns ); if ( rc == 0 ) { rc = ndb_thread_handle( op, &NA.ndb ); NA.txn = NA.ndb->startTransaction(); if ( NA.txn ) { rc = ndb_has_children( &NA, hasSubordinates ); NA.txn->close(); } } return rc; } /* * sets the supported operational attributes (if required) */ extern "C" int ndb_operational( Operation *op, SlapReply *rs ) { Attribute **ap; assert( rs->sr_entry != NULL ); for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next ) { if ( (*ap)->a_desc == slap_schema.si_ad_hasSubordinates ) { break; } } if ( *ap == NULL && attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_hasSubordinates ) == NULL && ( SLAP_OPATTRS( rs->sr_attr_flags ) || ad_inlist( slap_schema.si_ad_hasSubordinates, rs->sr_attrs ) ) ) { int hasSubordinates, rc; rc = ndb_has_subordinates( op, rs->sr_entry, &hasSubordinates ); if ( rc == LDAP_SUCCESS ) { *ap = slap_operational_hasSubordinate( hasSubordinates == LDAP_COMPARE_TRUE ); assert( *ap != NULL ); ap = &(*ap)->a_next; } } return LDAP_SUCCESS; }