]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/back-mdb/search.c
Merge remote branch 'origin/mdb.master' into OPENLDAP_REL_ENG_2_4
[openldap] / servers / slapd / back-mdb / search.c
index 2009f006a256caf48fc1d67a0fef5deba010b932..fda36ebf11d898a312741c75b3a04365987484a0 100644 (file)
@@ -2,7 +2,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 2000-2011 The OpenLDAP Foundation.
+ * Copyright 2000-2012 The OpenLDAP Foundation.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -32,8 +32,9 @@ static int search_candidates(
        SlapReply *rs,
        Entry *e,
        MDB_txn *txn,
+       MDB_cursor *mci,
        ID      *ids,
-       ID      *scopes );
+       ID2L    scopes );
 
 static int parse_paged_cookie( Operation *op, SlapReply *rs );
 
@@ -97,7 +98,7 @@ static Entry * deref_base (
                        break;
                }
 
-               rs->sr_err = mdb_dn2entry( op, txn, &ndn, &e, 0 );
+               rs->sr_err = mdb_dn2entry( op, txn, NULL, &ndn, &e, 0 );
                if (rs->sr_err) {
                        rs->sr_err = LDAP_ALIAS_PROBLEM;
                        rs->sr_text = "aliasedObject not found";
@@ -107,7 +108,7 @@ static Entry * deref_base (
                /* Free the previous entry, continue to work with the
                 * one we just retrieved.
                 */
-               mdb_entry_return( *matched );
+               mdb_entry_return( op, *matched );
 
                /* We found a regular entry. Return this to the caller.
                 */
@@ -130,7 +131,8 @@ static int search_aliases(
        SlapReply *rs,
        Entry *e,
        MDB_txn *txn,
-       ID *scopes,
+       MDB_cursor *mci,
+       ID2L scopes,
        ID *stack )
 {
        ID *aliases, *curscop, *visited, *newsubs, *oldsubs, *tmp;
@@ -158,7 +160,7 @@ static int search_aliases(
        MDB_IDL_ZERO( aliases );
        rs->sr_err = mdb_filter_candidates( op, txn, &af, aliases,
                curscop, visited );
-       if (rs->sr_err != LDAP_SUCCESS) {
+       if (rs->sr_err != LDAP_SUCCESS || MDB_IDL_IS_ZERO( aliases )) {
                return rs->sr_err;
        }
        oldsubs[0] = 1;
@@ -179,7 +181,7 @@ static int search_aliases(
                if (first) {
                        first = 0;
                } else {
-                       mdb_entry_return( e );
+                       mdb_entry_return( op, e );
                }
 
                /* Dereference all of the aliases in the current scope. */
@@ -187,7 +189,7 @@ static int search_aliases(
                for (ida = mdb_idl_first(curscop, &cursora); ida != NOID;
                        ida = mdb_idl_next(curscop, &cursora))
                {
-                       rs->sr_err = mdb_id2entry(op, txn, ida, &a);
+                       rs->sr_err = mdb_id2entry(op, mci, ida, &a);
                        if (rs->sr_err != LDAP_SUCCESS) {
                                continue;
                        }
@@ -196,7 +198,7 @@ static int search_aliases(
                         * turned into a range that spans IDs indiscriminately
                         */
                        if (!is_entry_alias(a)) {
-                               mdb_entry_return (a);
+                               mdb_entry_return(op, a);
                                continue;
                        }
 
@@ -208,16 +210,19 @@ static int search_aliases(
                                /* If the target was not already in our current scopes,
                                 * make note of it in the newsubs list.
                                 */
-                               if (mdb_idl_insert(scopes, a->e_id) == 0) {
+                               ID2 mid;
+                               mid.mid = a->e_id;
+                               mid.mval.mv_data = NULL;
+                               if (mdb_id2l_insert(scopes, &mid) == 0) {
                                        mdb_idl_insert(newsubs, a->e_id);
                                }
-                               mdb_entry_return( a );
+                               mdb_entry_return( op, a );
 
                        } else if (matched) {
                                /* Alias could not be dereferenced, or it deref'd to
                                 * an ID we've already seen. Ignore it.
                                 */
-                               mdb_entry_return( matched );
+                               mdb_entry_return( op, matched );
                                rs->sr_text = NULL;
                        }
                }
@@ -243,7 +248,7 @@ nextido:
                 * be found, ignore it and move on. This should never happen;
                 * we should never see the ID of an entry that doesn't exist.
                 */
-               rs->sr_err = mdb_id2entry(op, txn, ido, &e);
+               rs->sr_err = mdb_id2entry(op, mci, ido, &e);
                if ( rs->sr_err != LDAP_SUCCESS ) {
                        goto nextido;
                }
@@ -255,27 +260,60 @@ nextido:
  * a range and simple iteration hits missing entryIDs
  */
 static int
-mdb_get_nextid(struct mdb_info *mdb, MDB_txn *txn, ID *cursor)
+mdb_get_nextid(MDB_cursor *mci, ID *cursor)
 {
-       MDB_cursor *curs;
        MDB_val key;
        ID id;
        int rc;
 
        id = *cursor + 1;
-       rc = mdb_cursor_open( txn, mdb->mi_id2entry, &curs );
-       if ( rc )
-               return rc;
        key.mv_data = &id;
        key.mv_size = sizeof(ID);
-       rc = mdb_cursor_get( curs, &key, NULL, MDB_SET_RANGE );
-       mdb_cursor_close( curs );
+       rc = mdb_cursor_get( mci, &key, NULL, MDB_SET_RANGE );
        if ( rc )
                return rc;
        memcpy( cursor, key.mv_data, sizeof(ID));
        return 0;
 }
 
+static void scope_chunk_free( void *key, void *data )
+{
+       ID2 *p1, *p2;
+       for (p1 = data; p1; p1 = p2) {
+               p2 = p1[0].mval.mv_data;
+               ber_memfree_x(p1, NULL);
+       }
+}
+
+static ID2 *scope_chunk_get( Operation *op )
+{
+       struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+       ID2 *ret = NULL;
+
+       ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)scope_chunk_get,
+                       (void *)&ret, NULL );
+       if ( !ret ) {
+               ret = ch_malloc( MDB_IDL_UM_SIZE * sizeof( ID2 ));
+       } else {
+               void *r2 = ret[0].mval.mv_data;
+               ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)scope_chunk_get,
+                       r2, scope_chunk_free, NULL, NULL );
+       }
+       return ret;
+}
+
+static void scope_chunk_ret( Operation *op, ID2 *scopes )
+{
+       struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+       void *ret = NULL;
+
+       ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)scope_chunk_get,
+                       &ret, NULL );
+       scopes[0].mval.mv_data = ret;
+       ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)scope_chunk_get,
+                       (void *)scopes, scope_chunk_free, NULL, NULL );
+}
+
 int
 mdb_search( Operation *op, SlapReply *rs )
 {
@@ -283,19 +321,19 @@ mdb_search( Operation *op, SlapReply *rs )
        ID              id, cursor;
        ID              lastid = NOID;
        ID              candidates[MDB_IDL_UM_SIZE];
-       ID              scopes[MDB_IDL_DB_SIZE];
-       Entry           *e = NULL, base;
+       ID2             *scopes;
+       Entry           *e = NULL, *base = NULL;
        Entry           *matched = NULL;
        AttributeName   *attrs;
-       struct berval   realbase = BER_BVNULL;
        slap_mask_t     mask;
        time_t          stoptime;
        int             manageDSAit;
        int             tentries = 0;
+       IdScopes        isc;
+       MDB_cursor      *mci;
 
-       mdb_op_info     opinfo = {0}, *moi = &opinfo;
+       mdb_op_info     opinfo = {{{0}}}, *moi = &opinfo;
        MDB_txn                 *ltid = NULL;
-       MDB_cursor      *idcursor = NULL;
 
        Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(mdb_search) "\n", 0, 0, 0);
        attrs = op->oq_search.rs_attrs;
@@ -313,12 +351,23 @@ mdb_search( Operation *op, SlapReply *rs )
 
        ltid = moi->moi_txn;
 
+       rs->sr_err = mdb_cursor_open( ltid, mdb->mi_id2entry, &mci );
+       if ( rs->sr_err ) {
+               send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+               return rs->sr_err;
+       }
+
+       scopes = scope_chunk_get( op );
+       isc.mt = ltid;
+       isc.mc = NULL;
+       isc.scopes = scopes;
+
        if ( op->ors_deref & LDAP_DEREF_FINDING ) {
                MDB_IDL_ZERO(candidates);
        }
 dn2entry_retry:
        /* get entry with reader lock */
-       rs->sr_err = mdb_dn2entry( op, ltid, &op->o_req_ndn, &e, 1 );
+       rs->sr_err = mdb_dn2entry( op, ltid, NULL, &op->o_req_ndn, &e, 1 );
 
        switch(rs->sr_err) {
        case MDB_NOTFOUND:
@@ -346,7 +395,7 @@ dn2entry_retry:
                        if ( e ) {
                                build_new_dn( &op->o_req_ndn, &e->e_nname, &stub,
                                        op->o_tmpmemctx );
-                               mdb_entry_return (e);
+                               mdb_entry_return(op, e);
                                matched = NULL;
                                goto dn2entry_retry;
                        }
@@ -381,7 +430,7 @@ dn2entry_retry:
                                rs->sr_matched = matched_dn.bv_val;
                        }
 
-                       mdb_entry_return (matched);
+                       mdb_entry_return(op, matched);
                        matched = NULL;
 
                        if ( erefs ) {
@@ -420,7 +469,7 @@ dn2entry_retry:
                        rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
                }
 
-               mdb_entry_return(e);
+               mdb_entry_return( op,e);
                send_ldap_result( op, rs );
                goto done;
        }
@@ -435,7 +484,7 @@ dn2entry_retry:
 
                rs->sr_err = LDAP_REFERRAL;
 
-               mdb_entry_return( e );
+               mdb_entry_return( op, e );
                e = NULL;
 
                if ( erefs ) {
@@ -466,7 +515,7 @@ dn2entry_retry:
                ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
        {
                rs->sr_err = LDAP_ASSERTION_FAILED;
-               mdb_entry_return(e);
+               mdb_entry_return( op,e);
                send_ldap_result( op, rs );
                goto done;
        }
@@ -474,29 +523,21 @@ dn2entry_retry:
        /* compute it anyway; root does not use it */
        stoptime = op->o_time + op->ors_tlimit;
 
-       /* need normalized dn below */
-       ber_dupbv( &realbase, &e->e_nname );
-
-       /* Copy info to base, must free entry before accessing the database
-        * in search_candidates, to avoid deadlocks.
-        */
-       base.e_private = e->e_private;
-       base.e_nname = realbase;
-       base.e_id = e->e_id;
+       base = e;
 
-       mdb_entry_return(e);
        e = NULL;
 
        /* select candidates */
        if ( op->oq_search.rs_scope == LDAP_SCOPE_BASE ) {
-               rs->sr_err = base_candidate( op->o_bd, &base, candidates );
+               rs->sr_err = base_candidate( op->o_bd, base, candidates );
 
        } else {
                MDB_IDL_ZERO( candidates );
-               MDB_IDL_ZERO( scopes );
-               mdb_idl_insert( scopes, base.e_id );
-               rs->sr_err = search_candidates( op, rs, &base,
-                       ltid, candidates, scopes );
+               scopes[0].mid = 1;
+               scopes[1].mid = base->e_id;
+               scopes[1].mval.mv_data = NULL;
+               rs->sr_err = search_candidates( op, rs, base,
+                       ltid, mci, candidates, scopes );
        }
 
        /* start cursor at beginning of candidates.
@@ -594,43 +635,48 @@ loop_begin:
                        goto done;
                }
 
-               /* get the entry */
-               rs->sr_err = mdb_id2entry( op, ltid, id, &e );
+               if ( id == base->e_id ) {
+                       e = base;
+               } else {
 
-               if (rs->sr_err == LDAP_BUSY) {
-                       rs->sr_text = "ldap server busy";
-                       send_ldap_result( op, rs );
-                       goto done;
+                       /* get the entry */
+                       rs->sr_err = mdb_id2entry( op, mci, id, &e );
 
-               } else if ( rs->sr_err == LDAP_OTHER ) {
-                       rs->sr_text = "internal error";
-                       send_ldap_result( op, rs );
-                       goto done;
-               }
+                       if (rs->sr_err == LDAP_BUSY) {
+                               rs->sr_text = "ldap server busy";
+                               send_ldap_result( op, rs );
+                               goto done;
 
-               if ( e == NULL ) {
-                       if( !MDB_IDL_IS_RANGE(candidates) ) {
-                               /* only complain for non-range IDLs */
-                               Debug( LDAP_DEBUG_TRACE,
-                                       LDAP_XSTRING(mdb_search)
-                                       ": candidate %ld not found\n",
-                                       (long) id, 0, 0 );
-                       } else {
-                               /* get the next ID from the DB */
-                               rs->sr_err = mdb_get_nextid( mdb, ltid, &cursor );
-                               if ( rs->sr_err == MDB_NOTFOUND ) {
-                                       break;
-                               }
-                               if ( rs->sr_err ) {
-                                       rs->sr_err = LDAP_OTHER;
-                                       rs->sr_text = "internal error in get_nextid";
-                                       send_ldap_result( op, rs );
-                                       goto done;
-                               }
-                               cursor--;
+                       } else if ( rs->sr_err == LDAP_OTHER ) {
+                               rs->sr_text = "internal error";
+                               send_ldap_result( op, rs );
+                               goto done;
                        }
 
-                       goto loop_continue;
+                       if ( e == NULL ) {
+                               if( !MDB_IDL_IS_RANGE(candidates) ) {
+                                       /* only complain for non-range IDLs */
+                                       Debug( LDAP_DEBUG_TRACE,
+                                               LDAP_XSTRING(mdb_search)
+                                               ": candidate %ld not found\n",
+                                               (long) id, 0, 0 );
+                               } else {
+                                       /* get the next ID from the DB */
+                                       rs->sr_err = mdb_get_nextid( mci, &cursor );
+                                       if ( rs->sr_err == MDB_NOTFOUND ) {
+                                               break;
+                                       }
+                                       if ( rs->sr_err ) {
+                                               rs->sr_err = LDAP_OTHER;
+                                               rs->sr_text = "internal error in get_nextid";
+                                               send_ldap_result( op, rs );
+                                               goto done;
+                                       }
+                                       cursor--;
+                               }
+
+                               goto loop_continue;
+                       }
                }
 
                if ( is_entry_subentry( e ) ) {
@@ -655,25 +701,27 @@ loop_begin:
                /* Does this candidate actually satisfy the search scope?
                 */
                scopeok = 0;
+               isc.numrdns = 0;
                switch( op->ors_scope ) {
                case LDAP_SCOPE_BASE:
                        /* This is always true, yes? */
-                       if ( id == base.e_id ) scopeok = 1;
+                       if ( id == base->e_id ) scopeok = 1;
                        break;
 
 #ifdef LDAP_SCOPE_CHILDREN
                case LDAP_SCOPE_CHILDREN:
-                       if ( id == base.e_id ) break;
+                       if ( id == base->e_id ) break;
                        /* Fall-thru */
 #endif
                case LDAP_SCOPE_SUBTREE:
-                       if ( id == base.e_id ) {
+                       if ( id == base->e_id ) {
                                scopeok = 1;
                                break;
                        }
                        /* Fall-thru */
                case LDAP_SCOPE_ONELEVEL:
-                       if ( mdb_idscopes( op, ltid, &idcursor, id, scopes ) == MDB_SUCCESS ) scopeok = 1;
+                       isc.id = id;
+                       if ( mdb_idscopes( op, &isc ) == MDB_SUCCESS ) scopeok = 1;
                        break;
                }
 
@@ -704,8 +752,49 @@ loop_begin:
                        goto loop_continue;
                }
 
-               mdb_id2name( op, ltid, &idcursor, e->e_id,
-                       &e->e_name, &e->e_nname );
+               if (e != base) {
+                       struct berval pdn, pndn;
+                       char *d, *n;
+                       int i;
+                       /* child of base, just append RDNs to base->e_name */
+                       if ( isc.nscope == 1 ) {
+                               pdn = base->e_name;
+                               pndn = base->e_nname;
+                       } else {
+                               mdb_id2name( op, ltid, &isc.mc, scopes[isc.nscope].mid, &pdn, &pndn );
+                       }
+                       e->e_name.bv_len = pdn.bv_len;
+                       e->e_nname.bv_len = pndn.bv_len;
+                       for (i=0; i<isc.numrdns; i++) {
+                               e->e_name.bv_len += isc.rdns[i].bv_len + 1;
+                               e->e_nname.bv_len += isc.nrdns[i].bv_len + 1;
+                       }
+                       e->e_name.bv_val = op->o_tmpalloc(e->e_name.bv_len + 1, op->o_tmpmemctx);
+                       e->e_nname.bv_val = op->o_tmpalloc(e->e_nname.bv_len + 1, op->o_tmpmemctx);
+                       d = e->e_name.bv_val;
+                       n = e->e_nname.bv_val;
+                       for (i=0; i<isc.numrdns; i++) {
+                               memcpy(d, isc.rdns[i].bv_val, isc.rdns[i].bv_len);
+                               d += isc.rdns[i].bv_len;
+                               *d++ = ',';
+                               memcpy(n, isc.nrdns[i].bv_val, isc.nrdns[i].bv_len);
+                               n += isc.nrdns[i].bv_len;
+                               *n++ = ',';
+                       }
+                       if (pdn.bv_len) {
+                               memcpy(d, pdn.bv_val, pdn.bv_len+1);
+                               memcpy(n, pndn.bv_val, pndn.bv_len+1);
+                       } else {
+                               *--d = '\0';
+                               *--n = '\0';
+                               e->e_name.bv_len--;
+                               e->e_nname.bv_len--;
+                       }
+                       if (isc.nscope != 1) {
+                               op->o_tmpfree(pndn.bv_val, op->o_tmpmemctx);
+                               op->o_tmpfree(pdn.bv_val, op->o_tmpmemctx);
+                       }
+               }
 
                /*
                 * if it's a referral, add it to the list of referrals. only do
@@ -725,7 +814,7 @@ loop_begin:
 
                        send_search_reference( op, rs );
 
-                       mdb_entry_return( e );
+                       mdb_entry_return( op, e );
                        rs->sr_entry = NULL;
                        e = NULL;
 
@@ -743,7 +832,7 @@ loop_begin:
                        /* check size limit */
                        if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
                                if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size ) {
-                                       mdb_entry_return( e );
+                                       mdb_entry_return( op, e );
                                        e = NULL;
                                        send_paged_response( op, rs, &lastid, tentries );
                                        goto done;
@@ -763,7 +852,8 @@ loop_begin:
                                rs->sr_err = send_search_entry( op, rs );
                                rs->sr_attrs = NULL;
                                rs->sr_entry = NULL;
-                               mdb_entry_return( e );
+                               if (e != base)
+                                       mdb_entry_return( op, e );
                                e = NULL;
 
                                switch ( rs->sr_err ) {
@@ -794,8 +884,8 @@ loop_begin:
 
 loop_continue:
                if( e != NULL ) {
-                       /* free reader lock */
-                       mdb_entry_return( e );
+                       if ( e != base )
+                               mdb_entry_return( op, e );
                        RS_ASSERT( rs->sr_entry == NULL );
                        e = NULL;
                        rs->sr_entry = NULL;
@@ -816,17 +906,21 @@ nochange:
        rs->sr_err = LDAP_SUCCESS;
 
 done:
+       if( isc.mc )
+               mdb_cursor_close( isc.mc );
+       if (mci)
+               mdb_cursor_close( mci );
        if ( moi == &opinfo ) {
                mdb_txn_reset( moi->moi_txn );
                LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
        }
-       if( idcursor )
-               mdb_cursor_close( idcursor );
        if( rs->sr_v2ref ) {
                ber_bvarray_free( rs->sr_v2ref );
                rs->sr_v2ref = NULL;
        }
-       if( realbase.bv_val ) ch_free( realbase.bv_val );
+       if (base)
+               mdb_entry_return( op,base);
+       scope_chunk_ret( op, scopes );
 
        return rs->sr_err;
 }
@@ -915,8 +1009,9 @@ static int search_candidates(
        SlapReply *rs,
        Entry *e,
        MDB_txn *txn,
+       MDB_cursor *mci,
        ID      *ids,
-       ID      *scopes )
+       ID2L    scopes )
 {
        struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
        int rc, depth = 1;
@@ -981,7 +1076,7 @@ static int search_candidates(
        }
 
        if( op->ors_deref & LDAP_DEREF_SEARCHING ) {
-               rc = search_aliases( op, rs, e, txn, scopes, stack );
+               rc = search_aliases( op, rs, e, txn, mci, scopes, stack );
        } else {
                rc = LDAP_SUCCESS;
        }