static void *search_stack( Operation *op );
+typedef struct ww_ctx {
+ MDB_txn *txn;
+ MDB_cursor *mcd; /* if set, save cursor context */
+ ID key;
+ MDB_val data;
+ int flag;
+} ww_ctx;
+
+/* ITS#7904 if we get blocked while writing results to client,
+ * release the current reader txn and reacquire it after we
+ * unblock.
+ * Slight problem - if we're doing a scope-based walk (mdb_dn2id_walk)
+ * to return results, we need to remember the state of the mcd cursor.
+ * If the node that cursor was pointing to gets deleted while we're
+ * blocked, we may be unable to restore the cursor position. In that
+ * case return an LDAP_BUSY error - let the client know this search
+ * couldn't succeed, but might succeed on a retry.
+ */
+static void
+mdb_writewait( Operation *op, slap_callback *sc )
+{
+ ww_ctx *ww = sc->sc_private;
+ if ( !ww->flag ) {
+ if ( ww->mcd ) {
+ MDB_val key, data;
+ mdb_cursor_get( ww->mcd, &key, &data, MDB_GET_CURRENT );
+ memcpy( &ww->key, key.mv_data, sizeof(ID) );
+ ww->data.mv_size = data.mv_size;
+ ww->data.mv_data = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx );
+ memcpy(ww->data.mv_data, data.mv_data, data.mv_size);
+ }
+ mdb_txn_reset( ww->txn );
+ ww->flag = 1;
+ }
+}
+
+static int
+mdb_waitfixup( Operation *op, ww_ctx *ww, MDB_cursor *mci, MDB_cursor *mcd )
+{
+ int rc = 0;
+ ww->flag = 0;
+ mdb_txn_renew( ww->txn );
+ mdb_cursor_renew( ww->txn, mci );
+ mdb_cursor_renew( ww->txn, mcd );
+ if ( ww->mcd ) {
+ MDB_val key, data;
+ key.mv_size = sizeof(ID);
+ key.mv_data = &ww->key;
+ data = ww->data;
+ rc = mdb_cursor_get( mcd, &key, &data, MDB_GET_BOTH );
+ if ( rc == MDB_NOTFOUND ) {
+ data = ww->data;
+ rc = mdb_cursor_get( mcd, &key, &data, MDB_GET_BOTH_RANGE );
+ /* the loop will skip this node using NEXT_DUP but we want it
+ * sent, so go back one space first
+ */
+ if ( rc == MDB_SUCCESS )
+ mdb_cursor_get( mcd, &key, &data, MDB_PREV_DUP );
+ else
+ rc = LDAP_BUSY;
+ } else if ( rc ) {
+ rc = LDAP_OTHER;
+ }
+ op->o_tmpfree( ww->data.mv_data, op->o_tmpmemctx );
+ ww->data.mv_data = NULL;
+ }
+ return rc;
+}
+
int
mdb_search( Operation *op, SlapReply *rs )
{
int tentries = 0;
IdScopes isc;
MDB_cursor *mci, *mcd;
+ ww_ctx wwctx;
+ slap_callback cb = { 0 };
mdb_op_info opinfo = {{{0}}}, *moi = &opinfo;
MDB_txn *ltid = NULL;
iscopes[0] = 0;
}
+ wwctx.mcd = mcd;
isc.id = base->e_id;
isc.numrdns = 0;
rc = mdb_dn2id_walk( op, &isc );
cscope = 0;
} else {
id = mdb_idl_first( candidates, &cursor );
+ wwctx.mcd = NULL;
+ }
+
+ wwctx.flag = 0;
+ /* If we're running in our own read txn */
+ if ( moi == &opinfo ) {
+ cb.sc_writewait = mdb_writewait;
+ cb.sc_private = &wwctx;
+ wwctx.txn = ltid;
+ cb.sc_next = op->o_callback;
+ op->o_callback = &cb;
}
while (id != NOID)
send_search_reference( op, rs );
- mdb_entry_return( op, e );
+ if (e != base)
+ mdb_entry_return( op, e );
rs->sr_entry = NULL;
e = NULL;
ber_bvarray_free( erefs );
rs->sr_ref = NULL;
+ if ( wwctx.flag ) {
+ rs->sr_err = mdb_waitfixup( op, &wwctx, mci, mcd );
+ if ( rs->sr_err ) {
+ send_ldap_result( op, rs );
+ goto done;
+ }
+ }
+
goto loop_continue;
}
}
goto done;
}
+ if ( wwctx.flag ) {
+ rs->sr_err = mdb_waitfixup( op, &wwctx, mci, mcd );
+ if ( rs->sr_err ) {
+ send_ldap_result( op, rs );
+ goto done;
+ }
+ }
}
} else {
rs->sr_err = LDAP_SUCCESS;
done:
+ if ( cb.sc_private ) {
+ /* remove our writewait callback */
+ slap_callback **scp = &op->o_callback;
+ while ( *scp ) {
+ if ( *scp == &cb ) {
+ *scp = cb.sc_next;
+ cb.sc_private = NULL;
+ break;
+ }
+ }
+ }
mdb_cursor_close( mcd );
mdb_cursor_close( mci );
if ( moi == &opinfo ) {
rs->sr_v2ref = NULL;
}
if (base)
- mdb_entry_return( op,base);
+ mdb_entry_return( op, base );
scope_chunk_ret( op, scopes );
return rs->sr_err;