mdbx: rework cursor's couple (required for further fixing).

Change-Id: Ic89c59eaea36d14a26e29d2012c693d92474a748
This commit is contained in:
Leonid Yuriev 2018-08-29 19:15:59 +03:00 committed by Leo Yuriev
parent e25b30b5ce
commit 02f3230e0c
2 changed files with 93 additions and 93 deletions

View File

@ -691,6 +691,11 @@ typedef struct MDBX_xcursor {
uint8_t mx_dbflag; uint8_t mx_dbflag;
} MDBX_xcursor; } MDBX_xcursor;
typedef struct MDBX_cursor_couple {
MDBX_cursor outer;
MDBX_xcursor inner;
} MDBX_cursor_couple;
/* Check if there is an inited xcursor, so XCURSOR_REFRESH() is proper */ /* Check if there is an inited xcursor, so XCURSOR_REFRESH() is proper */
#define XCURSOR_INITED(mc) \ #define XCURSOR_INITED(mc) \
((mc)->mc_xcursor && ((mc)->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)) ((mc)->mc_xcursor && ((mc)->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED))

View File

@ -1074,7 +1074,7 @@ static int __must_check_result mdbx_cursor_last(MDBX_cursor *mc, MDBX_val *key,
MDBX_val *data); MDBX_val *data);
static int __must_check_result mdbx_cursor_init(MDBX_cursor *mc, MDBX_txn *txn, static int __must_check_result mdbx_cursor_init(MDBX_cursor *mc, MDBX_txn *txn,
MDBX_dbi dbi, MDBX_xcursor *mx); MDBX_dbi dbi);
static int __must_check_result mdbx_xcursor_init0(MDBX_cursor *mc); static int __must_check_result mdbx_xcursor_init0(MDBX_cursor *mc);
static int __must_check_result mdbx_xcursor_init1(MDBX_cursor *mc, static int __must_check_result mdbx_xcursor_init1(MDBX_cursor *mc,
MDBX_node *node); MDBX_node *node);
@ -2233,7 +2233,7 @@ static int mdbx_page_alloc(MDBX_cursor *mc, unsigned num, MDBX_page **mp,
/* Prepare to fetch more and coalesce */ /* Prepare to fetch more and coalesce */
oldest = (flags & MDBX_LIFORECLAIM) ? mdbx_find_oldest(txn) oldest = (flags & MDBX_LIFORECLAIM) ? mdbx_find_oldest(txn)
: env->me_oldest[0]; : env->me_oldest[0];
rc = mdbx_cursor_init(&recur, txn, FREE_DBI, NULL); rc = mdbx_cursor_init(&recur, txn, FREE_DBI);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(rc != MDBX_SUCCESS))
goto fail; goto fail;
if (flags & MDBX_LIFORECLAIM) { if (flags & MDBX_LIFORECLAIM) {
@ -3517,7 +3517,6 @@ static int mdbx_prep_backlog(MDBX_txn *txn, MDBX_cursor *mc) {
* it matches the actual number of pages being used. * it matches the actual number of pages being used.
* All named DBs must be open for a correct count. */ * All named DBs must be open for a correct count. */
static int mdbx_audit(MDBX_txn *txn, unsigned befree_stored) { static int mdbx_audit(MDBX_txn *txn, unsigned befree_stored) {
MDBX_cursor mc;
MDBX_val key, data; MDBX_val key, data;
const pgno_t pending = const pgno_t pending =
@ -3529,21 +3528,22 @@ static int mdbx_audit(MDBX_txn *txn, unsigned befree_stored) {
: 0) + : 0) +
(txn->mt_befree_pages ? txn->mt_befree_pages[0] - befree_stored (txn->mt_befree_pages ? txn->mt_befree_pages[0] - befree_stored
: 0); : 0);
int rc = mdbx_cursor_init(&mc, txn, FREE_DBI, NULL);
MDBX_cursor_couple cx;
int rc = mdbx_cursor_init(&cx.outer, txn, FREE_DBI);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(rc != MDBX_SUCCESS))
return rc; return rc;
pgno_t freecount = 0; pgno_t freecount = 0;
while ((rc = mdbx_cursor_get(&mc, &key, &data, MDBX_NEXT)) == 0) while ((rc = mdbx_cursor_get(&cx.outer, &key, &data, MDBX_NEXT)) == 0)
freecount += *(pgno_t *)data.iov_base; freecount += *(pgno_t *)data.iov_base;
mdbx_tassert(txn, rc == MDBX_NOTFOUND); mdbx_tassert(txn, rc == MDBX_NOTFOUND);
pgno_t count = 0; pgno_t count = 0;
for (MDBX_dbi i = FREE_DBI; i <= MAIN_DBI; i++) { for (MDBX_dbi i = FREE_DBI; i <= MAIN_DBI; i++) {
MDBX_xcursor mx;
if (!(txn->mt_dbflags[i] & DB_VALID)) if (!(txn->mt_dbflags[i] & DB_VALID))
continue; continue;
rc = mdbx_cursor_init(&mc, txn, i, &mx); rc = mdbx_cursor_init(&cx.outer, txn, i);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(rc != MDBX_SUCCESS))
return rc; return rc;
if (txn->mt_dbs[i].md_root == P_INVALID) if (txn->mt_dbs[i].md_root == P_INVALID)
@ -3551,9 +3551,9 @@ static int mdbx_audit(MDBX_txn *txn, unsigned befree_stored) {
count += txn->mt_dbs[i].md_branch_pages + txn->mt_dbs[i].md_leaf_pages + count += txn->mt_dbs[i].md_branch_pages + txn->mt_dbs[i].md_leaf_pages +
txn->mt_dbs[i].md_overflow_pages; txn->mt_dbs[i].md_overflow_pages;
rc = mdbx_page_search(&mc, NULL, MDBX_PS_FIRST); rc = mdbx_page_search(&cx.outer, NULL, MDBX_PS_FIRST);
while (rc == MDBX_SUCCESS) { while (rc == MDBX_SUCCESS) {
MDBX_page *mp = mc.mc_pg[mc.mc_top]; MDBX_page *mp = cx.outer.mc_pg[cx.outer.mc_top];
for (unsigned j = 0; j < NUMKEYS(mp); j++) { for (unsigned j = 0; j < NUMKEYS(mp); j++) {
MDBX_node *leaf = NODEPTR(mp, j); MDBX_node *leaf = NODEPTR(mp, j);
if ((leaf->mn_flags & (F_DUPDATA | F_SUBDATA)) == F_SUBDATA) { if ((leaf->mn_flags & (F_DUPDATA | F_SUBDATA)) == F_SUBDATA) {
@ -3575,7 +3575,7 @@ static int mdbx_audit(MDBX_txn *txn, unsigned befree_stored) {
db->md_branch_pages + db->md_leaf_pages + db->md_overflow_pages; db->md_branch_pages + db->md_leaf_pages + db->md_overflow_pages;
} }
} }
rc = mdbx_cursor_sibling(&mc, 1); rc = mdbx_cursor_sibling(&cx.outer, 1);
} }
mdbx_tassert(txn, rc == MDBX_NOTFOUND); mdbx_tassert(txn, rc == MDBX_NOTFOUND);
} }
@ -3610,7 +3610,7 @@ static int mdbx_update_gc(MDBX_txn *txn) {
const bool lifo = (env->me_flags & MDBX_LIFORECLAIM) != 0; const bool lifo = (env->me_flags & MDBX_LIFORECLAIM) != 0;
MDBX_cursor mc; MDBX_cursor mc;
int rc = mdbx_cursor_init(&mc, txn, FREE_DBI, NULL); int rc = mdbx_cursor_init(&mc, txn, FREE_DBI);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(rc != MDBX_SUCCESS))
return rc; return rc;
@ -4465,7 +4465,7 @@ int mdbx_txn_commit(MDBX_txn *txn) {
MDBX_val data; MDBX_val data;
data.iov_len = sizeof(MDBX_db); data.iov_len = sizeof(MDBX_db);
rc = mdbx_cursor_init(&mc, txn, MAIN_DBI, NULL); rc = mdbx_cursor_init(&mc, txn, MAIN_DBI);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(rc != MDBX_SUCCESS))
goto fail; goto fail;
for (i = CORE_DBS; i < txn->mt_numdbs; i++) { for (i = CORE_DBS; i < txn->mt_numdbs; i++) {
@ -6743,7 +6743,7 @@ static int mdbx_page_search(MDBX_cursor *mc, MDBX_val *key, int flags) {
MDBX_cursor mc2; MDBX_cursor mc2;
if (unlikely(TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi))) if (unlikely(TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi)))
return MDBX_BAD_DBI; return MDBX_BAD_DBI;
rc = mdbx_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, NULL); rc = mdbx_cursor_init(&mc2, mc->mc_txn, MAIN_DBI);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(rc != MDBX_SUCCESS))
return rc; return rc;
rc = mdbx_page_search(&mc2, &mc->mc_dbx->md_name, 0); rc = mdbx_page_search(&mc2, &mc->mc_dbx->md_name, 0);
@ -6911,8 +6911,6 @@ static __inline int mdbx_node_read(MDBX_cursor *mc, MDBX_node *leaf,
} }
int mdbx_get(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data) { int mdbx_get(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data) {
MDBX_cursor mc;
MDBX_xcursor mx;
int exact = 0; int exact = 0;
DKBUF; DKBUF;
@ -6933,10 +6931,11 @@ int mdbx_get(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data) {
if (unlikely(txn->mt_flags & MDBX_TXN_BLOCKED)) if (unlikely(txn->mt_flags & MDBX_TXN_BLOCKED))
return MDBX_BAD_TXN; return MDBX_BAD_TXN;
int rc = mdbx_cursor_init(&mc, txn, dbi, &mx); MDBX_cursor_couple cx;
int rc = mdbx_cursor_init(&cx.outer, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(rc != MDBX_SUCCESS))
return rc; return rc;
return mdbx_cursor_set(&mc, key, data, MDBX_SET, &exact); return mdbx_cursor_set(&cx.outer, key, data, MDBX_SET, &exact);
} }
/* Find a sibling for a page. /* Find a sibling for a page.
@ -7651,14 +7650,13 @@ static int mdbx_cursor_touch(MDBX_cursor *mc) {
(*mc->mc_dbflag & (DB_DIRTY | DB_DUPDATA)) == 0) { (*mc->mc_dbflag & (DB_DIRTY | DB_DUPDATA)) == 0) {
mdbx_cassert(mc, (mc->mc_flags & C_RECLAIMING) == 0); mdbx_cassert(mc, (mc->mc_flags & C_RECLAIMING) == 0);
/* Touch DB record of named DB */ /* Touch DB record of named DB */
MDBX_cursor mc2; MDBX_cursor_couple cx;
MDBX_xcursor mcx;
if (TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi)) if (TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi))
return MDBX_BAD_DBI; return MDBX_BAD_DBI;
rc = mdbx_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, &mcx); rc = mdbx_cursor_init(&cx.outer, mc->mc_txn, MAIN_DBI);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(rc != MDBX_SUCCESS))
return rc; return rc;
rc = mdbx_page_search(&mc2, &mc->mc_dbx->md_name, MDBX_PS_MODIFY); rc = mdbx_page_search(&cx.outer, &mc->mc_dbx->md_name, MDBX_PS_MODIFY);
if (unlikely(rc)) if (unlikely(rc))
return rc; return rc;
*mc->mc_dbflag |= DB_DIRTY; *mc->mc_dbflag |= DB_DIRTY;
@ -8851,8 +8849,7 @@ static int mdbx_xcursor_init2(MDBX_cursor *mc, MDBX_xcursor *src_mx,
} }
/* Initialize a cursor for a given transaction and database. */ /* Initialize a cursor for a given transaction and database. */
static int mdbx_cursor_init(MDBX_cursor *mc, MDBX_txn *txn, MDBX_dbi dbi, static int mdbx_cursor_init(MDBX_cursor *mc, MDBX_txn *txn, MDBX_dbi dbi) {
MDBX_xcursor *mx) {
mc->mc_signature = MDBX_MC_SIGNATURE; mc->mc_signature = MDBX_MC_SIGNATURE;
mc->mc_next = NULL; mc->mc_next = NULL;
mc->mc_backup = NULL; mc->mc_backup = NULL;
@ -8869,6 +8866,8 @@ static int mdbx_cursor_init(MDBX_cursor *mc, MDBX_txn *txn, MDBX_dbi dbi,
mc->mc_xcursor = NULL; mc->mc_xcursor = NULL;
if (txn->mt_dbs[dbi].md_flags & MDBX_DUPSORT) { if (txn->mt_dbs[dbi].md_flags & MDBX_DUPSORT) {
STATIC_ASSERT(offsetof(MDBX_cursor_couple, outer) == 0);
MDBX_xcursor *mx = &container_of(mc, MDBX_cursor_couple, outer)->inner;
mdbx_tassert(txn, mx != NULL); mdbx_tassert(txn, mx != NULL);
mx->mx_cursor.mc_signature = MDBX_MC_SIGNATURE; mx->mx_cursor.mc_signature = MDBX_MC_SIGNATURE;
mc->mc_xcursor = mx; mc->mc_xcursor = mx;
@ -8887,9 +8886,6 @@ static int mdbx_cursor_init(MDBX_cursor *mc, MDBX_txn *txn, MDBX_dbi dbi,
} }
int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) { int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) {
MDBX_cursor *mc;
size_t size = sizeof(MDBX_cursor);
if (unlikely(!ret || !txn)) if (unlikely(!ret || !txn))
return MDBX_EINVAL; return MDBX_EINVAL;
@ -8908,11 +8904,13 @@ int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) {
if (unlikely(dbi == FREE_DBI && !F_ISSET(txn->mt_flags, MDBX_TXN_RDONLY))) if (unlikely(dbi == FREE_DBI && !F_ISSET(txn->mt_flags, MDBX_TXN_RDONLY)))
return MDBX_EINVAL; return MDBX_EINVAL;
if (txn->mt_dbs[dbi].md_flags & MDBX_DUPSORT) const size_t size = (txn->mt_dbs[dbi].md_flags & MDBX_DUPSORT)
size += sizeof(MDBX_xcursor); ? sizeof(MDBX_cursor_couple)
: sizeof(MDBX_cursor);
MDBX_cursor *mc;
if (likely((mc = malloc(size)) != NULL)) { if (likely((mc = malloc(size)) != NULL)) {
int rc = mdbx_cursor_init(mc, txn, dbi, (MDBX_xcursor *)(mc + 1)); int rc = mdbx_cursor_init(mc, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(rc != MDBX_SUCCESS)) {
free(mc); free(mc);
return rc; return rc;
@ -8965,7 +8963,7 @@ int mdbx_cursor_renew(MDBX_txn *txn, MDBX_cursor *mc) {
if (unlikely(txn->mt_flags & MDBX_TXN_BLOCKED)) if (unlikely(txn->mt_flags & MDBX_TXN_BLOCKED))
return MDBX_BAD_TXN; return MDBX_BAD_TXN;
return mdbx_cursor_init(mc, txn, mc->mc_dbi, mc->mc_xcursor); return mdbx_cursor_init(mc, txn, mc->mc_dbi);
} }
/* Return the count of duplicate data items for the current key */ /* Return the count of duplicate data items for the current key */
@ -9840,8 +9838,7 @@ int mdbx_del(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data) {
static int mdbx_del0(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, static int mdbx_del0(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data,
unsigned flags) { unsigned flags) {
MDBX_cursor mc; MDBX_cursor_couple cx;
MDBX_xcursor mx;
MDBX_cursor_op op; MDBX_cursor_op op;
MDBX_val rdata; MDBX_val rdata;
int rc, exact = 0; int rc, exact = 0;
@ -9850,7 +9847,7 @@ static int mdbx_del0(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data,
mdbx_debug("====> delete db %u key [%s], data [%s]", dbi, DKEY(key), mdbx_debug("====> delete db %u key [%s], data [%s]", dbi, DKEY(key),
DVAL(data)); DVAL(data));
rc = mdbx_cursor_init(&mc, txn, dbi, &mx); rc = mdbx_cursor_init(&cx.outer, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(rc != MDBX_SUCCESS))
return rc; return rc;
@ -9862,7 +9859,7 @@ static int mdbx_del0(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data,
op = MDBX_SET; op = MDBX_SET;
flags |= MDBX_NODUPDATA; flags |= MDBX_NODUPDATA;
} }
rc = mdbx_cursor_set(&mc, key, data, op, &exact); rc = mdbx_cursor_set(&cx.outer, key, data, op, &exact);
if (likely(rc == MDBX_SUCCESS)) { if (likely(rc == MDBX_SUCCESS)) {
/* let mdbx_page_split know about this cursor if needed: /* let mdbx_page_split know about this cursor if needed:
* delete will trigger a rebalance; if it needs to move * delete will trigger a rebalance; if it needs to move
@ -9871,10 +9868,10 @@ static int mdbx_del0(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data,
* is larger than the current one, the parent page may * is larger than the current one, the parent page may
* run out of space, triggering a split. We need this * run out of space, triggering a split. We need this
* cursor to be consistent until the end of the rebalance. */ * cursor to be consistent until the end of the rebalance. */
mc.mc_next = txn->mt_cursors[dbi]; cx.outer.mc_next = txn->mt_cursors[dbi];
txn->mt_cursors[dbi] = &mc; txn->mt_cursors[dbi] = &cx.outer;
rc = mdbx_cursor_del(&mc, flags); rc = mdbx_cursor_del(&cx.outer, flags);
txn->mt_cursors[dbi] = mc.mc_next; txn->mt_cursors[dbi] = cx.outer.mc_next;
} }
return rc; return rc;
} }
@ -10318,8 +10315,6 @@ done:
int mdbx_put(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, int mdbx_put(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data,
unsigned flags) { unsigned flags) {
MDBX_cursor mc;
MDBX_xcursor mx;
if (unlikely(!key || !data || !txn)) if (unlikely(!key || !data || !txn))
return MDBX_EINVAL; return MDBX_EINVAL;
@ -10340,30 +10335,32 @@ int mdbx_put(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data,
if (unlikely(txn->mt_flags & (MDBX_TXN_RDONLY | MDBX_TXN_BLOCKED))) if (unlikely(txn->mt_flags & (MDBX_TXN_RDONLY | MDBX_TXN_BLOCKED)))
return (txn->mt_flags & MDBX_TXN_RDONLY) ? MDBX_EACCESS : MDBX_BAD_TXN; return (txn->mt_flags & MDBX_TXN_RDONLY) ? MDBX_EACCESS : MDBX_BAD_TXN;
int rc = mdbx_cursor_init(&mc, txn, dbi, &mx); MDBX_cursor_couple cx;
int rc = mdbx_cursor_init(&cx.outer, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(rc != MDBX_SUCCESS))
return rc; return rc;
mc.mc_next = txn->mt_cursors[dbi]; cx.outer.mc_next = txn->mt_cursors[dbi];
txn->mt_cursors[dbi] = &mc; txn->mt_cursors[dbi] = &cx.outer;
/* LY: support for update (explicit overwrite) */ /* LY: support for update (explicit overwrite) */
if (flags & MDBX_CURRENT) { if (flags & MDBX_CURRENT) {
rc = mdbx_cursor_get(&mc, key, NULL, MDBX_SET); rc = mdbx_cursor_get(&cx.outer, key, NULL, MDBX_SET);
if (likely(rc == MDBX_SUCCESS) && if (likely(rc == MDBX_SUCCESS) &&
(txn->mt_dbs[dbi].md_flags & MDBX_DUPSORT)) { (txn->mt_dbs[dbi].md_flags & MDBX_DUPSORT)) {
/* LY: allows update (explicit overwrite) only for unique keys */ /* LY: allows update (explicit overwrite) only for unique keys */
MDBX_node *leaf = NODEPTR(mc.mc_pg[mc.mc_top], mc.mc_ki[mc.mc_top]); MDBX_node *leaf = NODEPTR(cx.outer.mc_pg[cx.outer.mc_top],
cx.outer.mc_ki[cx.outer.mc_top]);
if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
mdbx_tassert(txn, XCURSOR_INITED(&mc) && mdbx_tassert(txn, XCURSOR_INITED(&cx.outer) &&
mc.mc_xcursor->mx_db.md_entries > 1); cx.outer.mc_xcursor->mx_db.md_entries > 1);
rc = MDBX_EMULTIVAL; rc = MDBX_EMULTIVAL;
} }
} }
} }
if (likely(rc == MDBX_SUCCESS)) if (likely(rc == MDBX_SUCCESS))
rc = mdbx_cursor_put(&mc, key, data, flags); rc = mdbx_cursor_put(&cx.outer, key, data, flags);
txn->mt_cursors[dbi] = mc.mc_next; txn->mt_cursors[dbi] = cx.outer.mc_next;
return rc; return rc;
} }
@ -10673,7 +10670,7 @@ static int __cold mdbx_env_compact(MDBX_env *env, mdbx_filehandle_t fd) {
MDBX_cursor mc; MDBX_cursor mc;
MDBX_val key, data; MDBX_val key, data;
rc = mdbx_cursor_init(&mc, txn, FREE_DBI, NULL); rc = mdbx_cursor_init(&mc, txn, FREE_DBI);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(rc != MDBX_SUCCESS))
return rc; return rc;
while ((rc = mdbx_cursor_get(&mc, &key, &data, MDBX_NEXT)) == 0) while ((rc = mdbx_cursor_get(&mc, &key, &data, MDBX_NEXT)) == 0)
@ -11111,7 +11108,7 @@ int mdbx_dbi_open_ex(MDBX_txn *txn, const char *table_name, unsigned user_flags,
key.iov_len = len; key.iov_len = len;
key.iov_base = (void *)table_name; key.iov_base = (void *)table_name;
MDBX_cursor mc; MDBX_cursor mc;
int rc = mdbx_cursor_init(&mc, txn, MAIN_DBI, NULL); int rc = mdbx_cursor_init(&mc, txn, MAIN_DBI);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(rc != MDBX_SUCCESS))
return rc; return rc;
rc = mdbx_cursor_set(&mc, &key, &data, MDBX_SET, &exact); rc = mdbx_cursor_set(&mc, &key, &data, MDBX_SET, &exact);
@ -11247,10 +11244,9 @@ int __cold mdbx_dbi_stat(MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *arg,
return MDBX_BAD_TXN; return MDBX_BAD_TXN;
if (unlikely(txn->mt_dbflags[dbi] & DB_STALE)) { if (unlikely(txn->mt_dbflags[dbi] & DB_STALE)) {
MDBX_cursor mc; MDBX_cursor_couple cx;
MDBX_xcursor mx;
/* Stale, must read the DB's root. cursor_init does it for us. */ /* Stale, must read the DB's root. cursor_init does it for us. */
int rc = mdbx_cursor_init(&mc, txn, dbi, &mx); int rc = mdbx_cursor_init(&cx.outer, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(rc != MDBX_SUCCESS))
return rc; return rc;
} }
@ -12254,13 +12250,12 @@ int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *new_data,
if (unlikely(txn->mt_flags & (MDBX_TXN_RDONLY | MDBX_TXN_BLOCKED))) if (unlikely(txn->mt_flags & (MDBX_TXN_RDONLY | MDBX_TXN_BLOCKED)))
return (txn->mt_flags & MDBX_TXN_RDONLY) ? MDBX_EACCESS : MDBX_BAD_TXN; return (txn->mt_flags & MDBX_TXN_RDONLY) ? MDBX_EACCESS : MDBX_BAD_TXN;
MDBX_cursor mc; MDBX_cursor_couple cx;
MDBX_xcursor mx; int rc = mdbx_cursor_init(&cx.outer, txn, dbi);
int rc = mdbx_cursor_init(&mc, txn, dbi, &mx);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(rc != MDBX_SUCCESS))
return rc; return rc;
mc.mc_next = txn->mt_cursors[dbi]; cx.outer.mc_next = txn->mt_cursors[dbi];
txn->mt_cursors[dbi] = &mc; txn->mt_cursors[dbi] = &cx.outer;
MDBX_val present_key = *key; MDBX_val present_key = *key;
if (F_ISSET(flags, MDBX_CURRENT | MDBX_NOOVERWRITE)) { if (F_ISSET(flags, MDBX_CURRENT | MDBX_NOOVERWRITE)) {
@ -12273,7 +12268,7 @@ int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *new_data,
/* убираем лишний бит, он был признаком запрошенного режима */ /* убираем лишний бит, он был признаком запрошенного режима */
flags -= MDBX_NOOVERWRITE; flags -= MDBX_NOOVERWRITE;
rc = mdbx_cursor_get(&mc, &present_key, old_data, MDBX_GET_BOTH); rc = mdbx_cursor_get(&cx.outer, &present_key, old_data, MDBX_GET_BOTH);
if (rc != MDBX_SUCCESS) if (rc != MDBX_SUCCESS)
goto bailout; goto bailout;
@ -12288,7 +12283,7 @@ int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *new_data,
if (unlikely(new_data && old_data->iov_base == new_data->iov_base)) if (unlikely(new_data && old_data->iov_base == new_data->iov_base))
return MDBX_EINVAL; return MDBX_EINVAL;
MDBX_val present_data; MDBX_val present_data;
rc = mdbx_cursor_get(&mc, &present_key, &present_data, MDBX_SET_KEY); rc = mdbx_cursor_get(&cx.outer, &present_key, &present_data, MDBX_SET_KEY);
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(rc != MDBX_SUCCESS)) {
old_data->iov_base = NULL; old_data->iov_base = NULL;
old_data->iov_len = rc; old_data->iov_len = rc;
@ -12299,16 +12294,16 @@ int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *new_data,
*old_data = present_data; *old_data = present_data;
goto bailout; goto bailout;
} else { } else {
MDBX_page *page = mc.mc_pg[mc.mc_top]; MDBX_page *page = cx.outer.mc_pg[cx.outer.mc_top];
if (txn->mt_dbs[dbi].md_flags & MDBX_DUPSORT) { if (txn->mt_dbs[dbi].md_flags & MDBX_DUPSORT) {
if (flags & MDBX_CURRENT) { if (flags & MDBX_CURRENT) {
/* для не-уникальных ключей позволяем update/delete только если ключ /* для не-уникальных ключей позволяем update/delete только если ключ
* один */ * один */
MDBX_node *leaf = NODEPTR(page, mc.mc_ki[mc.mc_top]); MDBX_node *leaf = NODEPTR(page, cx.outer.mc_ki[cx.outer.mc_top]);
if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
mdbx_tassert(txn, XCURSOR_INITED(&mc) && mdbx_tassert(txn, XCURSOR_INITED(&cx.outer) &&
mc.mc_xcursor->mx_db.md_entries > 1); cx.outer.mc_xcursor->mx_db.md_entries > 1);
if (mc.mc_xcursor->mx_db.md_entries > 1) { if (cx.outer.mc_xcursor->mx_db.md_entries > 1) {
rc = MDBX_EMULTIVAL; rc = MDBX_EMULTIVAL;
goto bailout; goto bailout;
} }
@ -12353,12 +12348,12 @@ int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *new_data,
} }
if (likely(new_data)) if (likely(new_data))
rc = mdbx_cursor_put(&mc, key, new_data, flags); rc = mdbx_cursor_put(&cx.outer, key, new_data, flags);
else else
rc = mdbx_cursor_del(&mc, 0); rc = mdbx_cursor_del(&cx.outer, 0);
bailout: bailout:
txn->mt_cursors[dbi] = mc.mc_next; txn->mt_cursors[dbi] = cx.outer.mc_next;
return rc; return rc;
} }
@ -12382,14 +12377,13 @@ int mdbx_get_ex(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data,
if (unlikely(txn->mt_flags & MDBX_TXN_BLOCKED)) if (unlikely(txn->mt_flags & MDBX_TXN_BLOCKED))
return MDBX_BAD_TXN; return MDBX_BAD_TXN;
MDBX_cursor mc; MDBX_cursor_couple cx;
MDBX_xcursor mx; int rc = mdbx_cursor_init(&cx.outer, txn, dbi);
int rc = mdbx_cursor_init(&mc, txn, dbi, &mx);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(rc != MDBX_SUCCESS))
return rc; return rc;
int exact = 0; int exact = 0;
rc = mdbx_cursor_set(&mc, key, data, MDBX_SET_KEY, &exact); rc = mdbx_cursor_set(&cx.outer, key, data, MDBX_SET_KEY, &exact);
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(rc != MDBX_SUCCESS)) {
if (rc == MDBX_NOTFOUND && values_count) if (rc == MDBX_NOTFOUND && values_count)
*values_count = 0; *values_count = 0;
@ -12398,15 +12392,17 @@ int mdbx_get_ex(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data,
if (values_count) { if (values_count) {
*values_count = 1; *values_count = 1;
if (mc.mc_xcursor != NULL) { if (cx.outer.mc_xcursor != NULL) {
MDBX_node *leaf = NODEPTR(mc.mc_pg[mc.mc_top], mc.mc_ki[mc.mc_top]); MDBX_node *leaf = NODEPTR(cx.outer.mc_pg[cx.outer.mc_top],
cx.outer.mc_ki[cx.outer.mc_top]);
if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
mdbx_tassert(txn, mc.mc_xcursor == &mx && mdbx_tassert(txn, cx.outer.mc_xcursor == &cx.inner &&
(mx.mx_cursor.mc_flags & C_INITIALIZED)); (cx.inner.mx_cursor.mc_flags & C_INITIALIZED));
*values_count = (sizeof(*values_count) >= sizeof(mx.mx_db.md_entries) || *values_count =
mx.mx_db.md_entries <= SIZE_MAX) (sizeof(*values_count) >= sizeof(cx.inner.mx_db.md_entries) ||
? (size_t)mx.mx_db.md_entries cx.inner.mx_db.md_entries <= SIZE_MAX)
: SIZE_MAX; ? (size_t)cx.inner.mx_db.md_entries
: SIZE_MAX;
} }
} }
} }
@ -12682,19 +12678,18 @@ int mdbx_set_attr(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data,
if (unlikely(txn->mt_flags & (MDBX_TXN_RDONLY | MDBX_TXN_BLOCKED))) if (unlikely(txn->mt_flags & (MDBX_TXN_RDONLY | MDBX_TXN_BLOCKED)))
return (txn->mt_flags & MDBX_TXN_RDONLY) ? MDBX_EACCESS : MDBX_BAD_TXN; return (txn->mt_flags & MDBX_TXN_RDONLY) ? MDBX_EACCESS : MDBX_BAD_TXN;
MDBX_cursor mc; MDBX_cursor_couple cx;
MDBX_xcursor mx;
MDBX_val old_data; MDBX_val old_data;
int rc = mdbx_cursor_init(&mc, txn, dbi, &mx); int rc = mdbx_cursor_init(&cx.outer, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(rc != MDBX_SUCCESS))
return rc; return rc;
rc = mdbx_cursor_set(&mc, key, &old_data, MDBX_SET, NULL); rc = mdbx_cursor_set(&cx.outer, key, &old_data, MDBX_SET, NULL);
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(rc != MDBX_SUCCESS)) {
if (rc == MDBX_NOTFOUND && data) { if (rc == MDBX_NOTFOUND && data) {
mc.mc_next = txn->mt_cursors[dbi]; cx.outer.mc_next = txn->mt_cursors[dbi];
txn->mt_cursors[dbi] = &mc; txn->mt_cursors[dbi] = &cx.outer;
rc = mdbx_cursor_put_attr(&mc, key, data, attr, 0); rc = mdbx_cursor_put_attr(&cx.outer, key, data, attr, 0);
txn->mt_cursors[dbi] = mc.mc_next; txn->mt_cursors[dbi] = cx.outer.mc_next;
} }
return rc; return rc;
} }
@ -12709,11 +12704,11 @@ int mdbx_set_attr(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data,
old_data.iov_len) == 0))) old_data.iov_len) == 0)))
return MDBX_SUCCESS; return MDBX_SUCCESS;
mc.mc_next = txn->mt_cursors[dbi]; cx.outer.mc_next = txn->mt_cursors[dbi];
txn->mt_cursors[dbi] = &mc; txn->mt_cursors[dbi] = &cx.outer;
rc = mdbx_cursor_put_attr(&mc, key, data ? data : &old_data, attr, rc = mdbx_cursor_put_attr(&cx.outer, key, data ? data : &old_data, attr,
MDBX_CURRENT); MDBX_CURRENT);
txn->mt_cursors[dbi] = mc.mc_next; txn->mt_cursors[dbi] = cx.outer.mc_next;
return rc; return rc;
} }