mirror of
https://github.com/isar/libmdbx.git
synced 2025-01-08 05:34:14 +08:00
148 lines
4.8 KiB
C
148 lines
4.8 KiB
C
/// \copyright SPDX-License-Identifier: Apache-2.0
|
|
/// \note Please refer to the COPYRIGHT file for explanations license change,
|
|
/// credits and acknowledgments.
|
|
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
|
|
|
|
#include "internals.h"
|
|
|
|
/* Search for the lowest key under the current branch page.
|
|
* This just bypasses a numkeys check in the current page
|
|
* before calling tree_search_finalize(), because the callers
|
|
* are all in situations where the current page is known to
|
|
* be underfilled. */
|
|
__hot int tree_search_lowest(MDBX_cursor *mc) {
|
|
cASSERT(mc, mc->top >= 0);
|
|
page_t *mp = mc->pg[mc->top];
|
|
cASSERT(mc, is_branch(mp));
|
|
|
|
node_t *node = page_node(mp, 0);
|
|
int err = page_get(mc, node_pgno(node), &mp, mp->txnid);
|
|
if (unlikely(err != MDBX_SUCCESS))
|
|
return err;
|
|
|
|
mc->ki[mc->top] = 0;
|
|
err = cursor_push(mc, mp, 0);
|
|
if (unlikely(err != MDBX_SUCCESS))
|
|
return err;
|
|
return tree_search_finalize(mc, nullptr, Z_FIRST);
|
|
}
|
|
|
|
__hot int tree_search(MDBX_cursor *mc, const MDBX_val *key, int flags) {
|
|
int err;
|
|
if (unlikely(mc->txn->flags & MDBX_TXN_BLOCKED)) {
|
|
DEBUG("%s", "transaction has failed, must abort");
|
|
err = MDBX_BAD_TXN;
|
|
bailout:
|
|
be_poor(mc);
|
|
return err;
|
|
}
|
|
|
|
const size_t dbi = cursor_dbi(mc);
|
|
if (unlikely(*cursor_dbi_state(mc) & DBI_STALE)) {
|
|
err = tbl_fetch(mc->txn, dbi);
|
|
if (unlikely(err != MDBX_SUCCESS))
|
|
goto bailout;
|
|
}
|
|
|
|
const pgno_t root = mc->tree->root;
|
|
if (unlikely(root == P_INVALID)) {
|
|
DEBUG("%s", "tree is empty");
|
|
cASSERT(mc, is_poor(mc));
|
|
return MDBX_NOTFOUND;
|
|
}
|
|
|
|
cASSERT(mc, root >= NUM_METAS && root < mc->txn->geo.first_unallocated);
|
|
if (mc->top < 0 || mc->pg[0]->pgno != root) {
|
|
txnid_t pp_txnid = mc->tree->mod_txnid;
|
|
pp_txnid = /* tree->mod_txnid maybe zero in a legacy DB */ pp_txnid
|
|
? pp_txnid
|
|
: mc->txn->txnid;
|
|
if ((mc->txn->flags & MDBX_TXN_RDONLY) == 0) {
|
|
MDBX_txn *scan = mc->txn;
|
|
do
|
|
if ((scan->flags & MDBX_TXN_DIRTY) &&
|
|
(dbi == MAIN_DBI || (scan->dbi_state[dbi] & DBI_DIRTY))) {
|
|
/* После коммита вложенных тразакций может быть mod_txnid > front */
|
|
pp_txnid = scan->front_txnid;
|
|
break;
|
|
}
|
|
while (unlikely((scan = scan->parent) != nullptr));
|
|
}
|
|
err = page_get(mc, root, &mc->pg[0], pp_txnid);
|
|
if (unlikely(err != MDBX_SUCCESS))
|
|
goto bailout;
|
|
}
|
|
|
|
mc->top = 0;
|
|
mc->ki[0] = (flags & Z_LAST) ? page_numkeys(mc->pg[0]) - 1 : 0;
|
|
DEBUG("db %d root page %" PRIaPGNO " has flags 0x%X", cursor_dbi_dbg(mc),
|
|
root, mc->pg[0]->flags);
|
|
|
|
if (flags & Z_MODIFY) {
|
|
err = page_touch(mc);
|
|
if (unlikely(err != MDBX_SUCCESS))
|
|
goto bailout;
|
|
}
|
|
|
|
if (flags & Z_ROOTONLY)
|
|
return MDBX_SUCCESS;
|
|
|
|
return tree_search_finalize(mc, key, flags);
|
|
}
|
|
|
|
__hot __noinline int tree_search_finalize(MDBX_cursor *mc, const MDBX_val *key,
|
|
int flags) {
|
|
cASSERT(mc, !is_poor(mc));
|
|
DKBUF_DEBUG;
|
|
int err;
|
|
page_t *mp = mc->pg[mc->top];
|
|
intptr_t ki = (flags & Z_FIRST) ? 0 : page_numkeys(mp) - 1;
|
|
while (is_branch(mp)) {
|
|
DEBUG("branch page %" PRIaPGNO " has %zu keys", mp->pgno, page_numkeys(mp));
|
|
cASSERT(mc, page_numkeys(mp) > 1);
|
|
DEBUG("found index 0 to page %" PRIaPGNO, node_pgno(page_node(mp, 0)));
|
|
|
|
if ((flags & (Z_FIRST | Z_LAST)) == 0) {
|
|
const struct node_search_result nsr = node_search(mc, key);
|
|
if (likely(nsr.node))
|
|
ki = mc->ki[mc->top] + (intptr_t)nsr.exact - 1;
|
|
DEBUG("following index %zu for key [%s]", ki, DKEY_DEBUG(key));
|
|
}
|
|
|
|
err = page_get(mc, node_pgno(page_node(mp, ki)), &mp, mp->txnid);
|
|
if (unlikely(err != MDBX_SUCCESS))
|
|
goto bailout;
|
|
|
|
mc->ki[mc->top] = (indx_t)ki;
|
|
ki = (flags & Z_FIRST) ? 0 : page_numkeys(mp) - 1;
|
|
err = cursor_push(mc, mp, ki);
|
|
if (unlikely(err != MDBX_SUCCESS))
|
|
goto bailout;
|
|
|
|
if (flags & Z_MODIFY) {
|
|
err = page_touch(mc);
|
|
if (unlikely(err != MDBX_SUCCESS))
|
|
goto bailout;
|
|
mp = mc->pg[mc->top];
|
|
}
|
|
}
|
|
|
|
if (!MDBX_DISABLE_VALIDATION && unlikely(!check_leaf_type(mc, mp))) {
|
|
ERROR("unexpected leaf-page #%" PRIaPGNO " type 0x%x seen by cursor",
|
|
mp->pgno, mp->flags);
|
|
err = MDBX_CORRUPTED;
|
|
bailout:
|
|
be_poor(mc);
|
|
return err;
|
|
}
|
|
|
|
DEBUG("found leaf page %" PRIaPGNO " for key [%s]", mp->pgno,
|
|
DKEY_DEBUG(key));
|
|
/* Логически верно, но (в текущем понимании) нет необходимости.
|
|
Однако, стоит ещё по-проверять/по-тестировать.
|
|
Возможно есть сценарий, в котором очистка флагов всё-таки требуется.
|
|
|
|
be_filled(mc); */
|
|
return MDBX_SUCCESS;
|
|
}
|