mirror of
https://github.com/isar/libmdbx.git
synced 2025-01-04 17:44:13 +08:00
mdbx: support for txnid of the last modification for sub-dbs/kv-spaces.
Change-Id: Ifb6684df57608cda88aa9134b275f442358ff46d
This commit is contained in:
parent
64ecfe171a
commit
3ee269ddb6
3
mdbx.h
3
mdbx.h
@ -1571,12 +1571,13 @@ LIBMDBX_API int mdbx_env_copy2fd(MDBX_env *env, mdbx_filehandle_t fd,
|
|||||||
/* Statistics for a database in the environment */
|
/* Statistics for a database in the environment */
|
||||||
typedef struct MDBX_stat {
|
typedef struct MDBX_stat {
|
||||||
uint32_t ms_psize; /* Size of a database page.
|
uint32_t ms_psize; /* Size of a database page.
|
||||||
* This is currently the same for all databases. */
|
* This is the same for all databases. */
|
||||||
uint32_t ms_depth; /* Depth (height) of the B-tree */
|
uint32_t ms_depth; /* Depth (height) of the B-tree */
|
||||||
uint64_t ms_branch_pages; /* Number of internal (non-leaf) pages */
|
uint64_t ms_branch_pages; /* Number of internal (non-leaf) pages */
|
||||||
uint64_t ms_leaf_pages; /* Number of leaf pages */
|
uint64_t ms_leaf_pages; /* Number of leaf pages */
|
||||||
uint64_t ms_overflow_pages; /* Number of overflow pages */
|
uint64_t ms_overflow_pages; /* Number of overflow pages */
|
||||||
uint64_t ms_entries; /* Number of data items */
|
uint64_t ms_entries; /* Number of data items */
|
||||||
|
uint64_t ms_mod_txnid; /* Transaction ID of commited last modification */
|
||||||
} MDBX_stat;
|
} MDBX_stat;
|
||||||
|
|
||||||
/* Return statistics about the MDBX environment.
|
/* Return statistics about the MDBX environment.
|
||||||
|
@ -2024,6 +2024,7 @@ static int __must_check_result mdbx_xcursor_init2(MDBX_cursor *mc,
|
|||||||
int force);
|
int force);
|
||||||
|
|
||||||
static int __must_check_result mdbx_drop0(MDBX_cursor *mc, int subs);
|
static int __must_check_result mdbx_drop0(MDBX_cursor *mc, int subs);
|
||||||
|
static int __must_check_result mdbx_fetch_sdb(MDBX_txn *txn, MDBX_dbi dbi);
|
||||||
|
|
||||||
static MDBX_cmp_func mdbx_cmp_memn, mdbx_cmp_memnr, mdbx_cmp_int_align4,
|
static MDBX_cmp_func mdbx_cmp_memn, mdbx_cmp_memnr, mdbx_cmp_int_align4,
|
||||||
mdbx_cmp_int_align2, mdbx_cmp_int_unaligned;
|
mdbx_cmp_int_align2, mdbx_cmp_int_unaligned;
|
||||||
@ -5342,6 +5343,8 @@ static __cold int mdbx_audit_ex(MDBX_txn *txn, unsigned retired_stored,
|
|||||||
for (unsigned j = 0; j < page_numkeys(mp); j++) {
|
for (unsigned j = 0; j < page_numkeys(mp); j++) {
|
||||||
MDBX_node *node = page_node(mp, j);
|
MDBX_node *node = page_node(mp, j);
|
||||||
if (node_flags(node) == F_SUBDATA) {
|
if (node_flags(node) == F_SUBDATA) {
|
||||||
|
if (unlikely(node_ds(node) < sizeof(MDBX_db)))
|
||||||
|
return MDBX_CORRUPTED;
|
||||||
MDBX_db db_copy, *db;
|
MDBX_db db_copy, *db;
|
||||||
memcpy(db = &db_copy, node_data(node), sizeof(db_copy));
|
memcpy(db = &db_copy, node_data(node), sizeof(db_copy));
|
||||||
if ((txn->mt_flags & MDBX_RDONLY) == 0) {
|
if ((txn->mt_flags & MDBX_RDONLY) == 0) {
|
||||||
@ -6500,7 +6503,9 @@ int mdbx_txn_commit(MDBX_txn *txn) {
|
|||||||
rc = MDBX_BAD_DBI;
|
rc = MDBX_BAD_DBI;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
data.iov_base = &txn->mt_dbs[i];
|
MDBX_db *db = &txn->mt_dbs[i];
|
||||||
|
db->md_mod_txnid = txn->mt_txnid;
|
||||||
|
data.iov_base = db;
|
||||||
WITH_CURSOR_TRACKING(mc,
|
WITH_CURSOR_TRACKING(mc,
|
||||||
rc = mdbx_cursor_put(&mc, &txn->mt_dbxs[i].md_name,
|
rc = mdbx_cursor_put(&mc, &txn->mt_dbxs[i].md_name,
|
||||||
&data, F_SUBDATA));
|
&data, F_SUBDATA));
|
||||||
@ -6522,8 +6527,10 @@ int mdbx_txn_commit(MDBX_txn *txn) {
|
|||||||
|
|
||||||
rc = mdbx_page_flush(txn, 0);
|
rc = mdbx_page_flush(txn, 0);
|
||||||
if (likely(rc == MDBX_SUCCESS)) {
|
if (likely(rc == MDBX_SUCCESS)) {
|
||||||
MDBX_meta meta, *head = mdbx_meta_head(env);
|
if (txn->mt_dbs[MAIN_DBI].md_flags & DB_DIRTY)
|
||||||
|
txn->mt_dbs[MAIN_DBI].md_mod_txnid = txn->mt_txnid;
|
||||||
|
|
||||||
|
MDBX_meta meta, *head = mdbx_meta_head(env);
|
||||||
meta.mm_magic_and_version = head->mm_magic_and_version;
|
meta.mm_magic_and_version = head->mm_magic_and_version;
|
||||||
meta.mm_extra_flags = head->mm_extra_flags;
|
meta.mm_extra_flags = head->mm_extra_flags;
|
||||||
meta.mm_validator_id = head->mm_validator_id;
|
meta.mm_validator_id = head->mm_validator_id;
|
||||||
@ -8578,6 +8585,7 @@ int __cold mdbx_env_open(MDBX_env *env, const char *path, unsigned flags,
|
|||||||
mdbx_debug("leaf pages: %" PRIaPGNO, db->md_leaf_pages);
|
mdbx_debug("leaf pages: %" PRIaPGNO, db->md_leaf_pages);
|
||||||
mdbx_debug("overflow pages: %" PRIaPGNO, db->md_overflow_pages);
|
mdbx_debug("overflow pages: %" PRIaPGNO, db->md_overflow_pages);
|
||||||
mdbx_debug("root: %" PRIaPGNO, db->md_root);
|
mdbx_debug("root: %" PRIaPGNO, db->md_root);
|
||||||
|
mdbx_debug("schema_altered: %" PRIaTXN, db->md_mod_txnid);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -9101,6 +9109,42 @@ __hot static int mdbx_page_search_root(MDBX_cursor *mc, MDBX_val *key,
|
|||||||
return MDBX_SUCCESS;
|
return MDBX_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mdbx_fetch_sdb(MDBX_txn *txn, MDBX_dbi dbi) {
|
||||||
|
MDBX_cursor mc;
|
||||||
|
if (unlikely(TXN_DBI_CHANGED(txn, dbi)))
|
||||||
|
return MDBX_BAD_DBI;
|
||||||
|
int rc = mdbx_cursor_init(&mc, txn, MAIN_DBI);
|
||||||
|
if (unlikely(rc != MDBX_SUCCESS))
|
||||||
|
return rc;
|
||||||
|
rc = mdbx_page_search(&mc, &txn->mt_dbxs[dbi].md_name, 0);
|
||||||
|
if (unlikely(rc != MDBX_SUCCESS))
|
||||||
|
return (rc == MDBX_NOTFOUND) ? MDBX_BAD_DBI : rc;
|
||||||
|
|
||||||
|
MDBX_val data;
|
||||||
|
int exact = 0;
|
||||||
|
MDBX_node *node = mdbx_node_search(&mc, &txn->mt_dbxs[dbi].md_name, &exact);
|
||||||
|
if (unlikely(!exact))
|
||||||
|
return MDBX_BAD_DBI;
|
||||||
|
if (unlikely((node_flags(node) & (F_DUPDATA | F_SUBDATA)) != F_SUBDATA))
|
||||||
|
return MDBX_INCOMPATIBLE; /* not a named DB */
|
||||||
|
rc = mdbx_node_read(&mc, node, &data);
|
||||||
|
if (unlikely(rc != MDBX_SUCCESS))
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
if (unlikely(data.iov_len < sizeof(MDBX_db)))
|
||||||
|
return MDBX_INCOMPATIBLE; /* not a named DB */
|
||||||
|
|
||||||
|
uint16_t md_flags = UNALIGNED_PEEK_16(data.iov_base, MDBX_db, md_flags);
|
||||||
|
/* The txn may not know this DBI, or another process may
|
||||||
|
* have dropped and recreated the DB with other flags. */
|
||||||
|
if (unlikely((txn->mt_dbs[dbi].md_flags & PERSISTENT_FLAGS) != md_flags))
|
||||||
|
return MDBX_INCOMPATIBLE;
|
||||||
|
|
||||||
|
memcpy(&txn->mt_dbs[dbi], data.iov_base, sizeof(MDBX_db));
|
||||||
|
txn->mt_dbflags[dbi] &= ~DB_STALE;
|
||||||
|
return MDBX_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
/* Search for the lowest key under the current branch page.
|
/* Search for the lowest key under the current branch page.
|
||||||
* This just bypasses a numkeys check in the current page
|
* This just bypasses a numkeys check in the current page
|
||||||
* before calling mdbx_page_search_root(), because the callers
|
* before calling mdbx_page_search_root(), because the callers
|
||||||
@ -9148,35 +9192,9 @@ __hot static int mdbx_page_search(MDBX_cursor *mc, MDBX_val *key, int flags) {
|
|||||||
|
|
||||||
/* Make sure we're using an up-to-date root */
|
/* Make sure we're using an up-to-date root */
|
||||||
if (unlikely(*mc->mc_dbflag & DB_STALE)) {
|
if (unlikely(*mc->mc_dbflag & DB_STALE)) {
|
||||||
MDBX_cursor mc2;
|
rc = mdbx_fetch_sdb(mc->mc_txn, mc->mc_dbi);
|
||||||
if (unlikely(TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi)))
|
|
||||||
return MDBX_BAD_DBI;
|
|
||||||
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);
|
|
||||||
if (unlikely(rc != MDBX_SUCCESS))
|
|
||||||
return (rc == MDBX_NOTFOUND) ? MDBX_BAD_DBI : rc;
|
|
||||||
{
|
|
||||||
MDBX_val data;
|
|
||||||
int exact = 0;
|
|
||||||
MDBX_node *node = mdbx_node_search(&mc2, &mc->mc_dbx->md_name, &exact);
|
|
||||||
if (!exact)
|
|
||||||
return MDBX_BAD_DBI;
|
|
||||||
if (unlikely((node_flags(node) & (F_DUPDATA | F_SUBDATA)) != F_SUBDATA))
|
|
||||||
return MDBX_INCOMPATIBLE; /* not a named DB */
|
|
||||||
rc = mdbx_node_read(&mc2, node, &data);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
uint16_t md_flags = UNALIGNED_PEEK_16(data.iov_base, MDBX_db, md_flags);
|
|
||||||
/* The txn may not know this DBI, or another process may
|
|
||||||
* have dropped and recreated the DB with other flags. */
|
|
||||||
if (unlikely((mc->mc_db->md_flags & PERSISTENT_FLAGS) != md_flags))
|
|
||||||
return MDBX_INCOMPATIBLE;
|
|
||||||
memcpy(mc->mc_db, data.iov_base, sizeof(MDBX_db));
|
|
||||||
}
|
|
||||||
*mc->mc_dbflag &= ~DB_STALE;
|
|
||||||
}
|
}
|
||||||
root = mc->mc_db->md_root;
|
root = mc->mc_db->md_root;
|
||||||
|
|
||||||
@ -10080,7 +10098,7 @@ int mdbx_cursor_put(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data,
|
|||||||
MDBX_page *fp, *sub_root = NULL;
|
MDBX_page *fp, *sub_root = NULL;
|
||||||
uint16_t fp_flags;
|
uint16_t fp_flags;
|
||||||
MDBX_val xdata, *rdata, dkey, olddata;
|
MDBX_val xdata, *rdata, dkey, olddata;
|
||||||
MDBX_db dummy;
|
MDBX_db nested_dupdb;
|
||||||
unsigned mcount = 0, dcount = 0, nospill;
|
unsigned mcount = 0, dcount = 0, nospill;
|
||||||
size_t nsize;
|
size_t nsize;
|
||||||
int rc2;
|
int rc2;
|
||||||
@ -10485,30 +10503,30 @@ int mdbx_cursor_put(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data,
|
|||||||
/* Too big for a sub-page, convert to sub-DB */
|
/* Too big for a sub-page, convert to sub-DB */
|
||||||
fp_flags &= ~P_SUBP;
|
fp_flags &= ~P_SUBP;
|
||||||
prep_subDB:
|
prep_subDB:
|
||||||
dummy.md_xsize = 0;
|
nested_dupdb.md_xsize = 0;
|
||||||
dummy.md_flags = 0;
|
nested_dupdb.md_flags = 0;
|
||||||
if (mc->mc_db->md_flags & MDBX_DUPFIXED) {
|
if (mc->mc_db->md_flags & MDBX_DUPFIXED) {
|
||||||
fp_flags |= P_LEAF2;
|
fp_flags |= P_LEAF2;
|
||||||
dummy.md_xsize = fp->mp_leaf2_ksize;
|
nested_dupdb.md_xsize = fp->mp_leaf2_ksize;
|
||||||
dummy.md_flags = MDBX_DUPFIXED;
|
nested_dupdb.md_flags = MDBX_DUPFIXED;
|
||||||
if (mc->mc_db->md_flags & MDBX_INTEGERDUP)
|
if (mc->mc_db->md_flags & MDBX_INTEGERDUP)
|
||||||
dummy.md_flags |= MDBX_INTEGERKEY;
|
nested_dupdb.md_flags |= MDBX_INTEGERKEY;
|
||||||
}
|
}
|
||||||
dummy.md_depth = 1;
|
nested_dupdb.md_depth = 1;
|
||||||
dummy.md_branch_pages = 0;
|
nested_dupdb.md_branch_pages = 0;
|
||||||
dummy.md_leaf_pages = 1;
|
nested_dupdb.md_leaf_pages = 1;
|
||||||
dummy.md_overflow_pages = 0;
|
nested_dupdb.md_overflow_pages = 0;
|
||||||
dummy.md_entries = page_numkeys(fp);
|
nested_dupdb.md_entries = page_numkeys(fp);
|
||||||
xdata.iov_len = sizeof(MDBX_db);
|
xdata.iov_len = sizeof(nested_dupdb);
|
||||||
xdata.iov_base = &dummy;
|
xdata.iov_base = &nested_dupdb;
|
||||||
if ((rc = mdbx_page_alloc(mc, 1, &mp, MDBX_ALLOC_ALL)))
|
if ((rc = mdbx_page_alloc(mc, 1, &mp, MDBX_ALLOC_ALL)))
|
||||||
return rc;
|
return rc;
|
||||||
mc->mc_db->md_leaf_pages += 1;
|
mc->mc_db->md_leaf_pages += 1;
|
||||||
mdbx_cassert(mc, env->me_psize > olddata.iov_len);
|
mdbx_cassert(mc, env->me_psize > olddata.iov_len);
|
||||||
offset = env->me_psize - (unsigned)olddata.iov_len;
|
offset = env->me_psize - (unsigned)olddata.iov_len;
|
||||||
flags |= F_DUPDATA | F_SUBDATA;
|
flags |= F_DUPDATA | F_SUBDATA;
|
||||||
dummy.md_root = mp->mp_pgno;
|
nested_dupdb.md_root = mp->mp_pgno;
|
||||||
dummy.md_seq = dummy.md_merkle = 0;
|
nested_dupdb.md_seq = nested_dupdb.md_mod_txnid = 0;
|
||||||
sub_root = mp;
|
sub_root = mp;
|
||||||
}
|
}
|
||||||
if (mp != fp) {
|
if (mp != fp) {
|
||||||
@ -11290,12 +11308,16 @@ static int mdbx_xcursor_init1(MDBX_cursor *mc, MDBX_node *node) {
|
|||||||
return MDBX_CORRUPTED;
|
return MDBX_CORRUPTED;
|
||||||
|
|
||||||
if (node_flags(node) & F_SUBDATA) {
|
if (node_flags(node) & F_SUBDATA) {
|
||||||
|
if (unlikely(node_ds(node) != sizeof(MDBX_db)))
|
||||||
|
return MDBX_CORRUPTED;
|
||||||
memcpy(&mx->mx_db, node_data(node), sizeof(MDBX_db));
|
memcpy(&mx->mx_db, node_data(node), sizeof(MDBX_db));
|
||||||
mx->mx_cursor.mc_pg[0] = 0;
|
mx->mx_cursor.mc_pg[0] = 0;
|
||||||
mx->mx_cursor.mc_snum = 0;
|
mx->mx_cursor.mc_snum = 0;
|
||||||
mx->mx_cursor.mc_top = 0;
|
mx->mx_cursor.mc_top = 0;
|
||||||
mx->mx_cursor.mc_flags = C_SUB;
|
mx->mx_cursor.mc_flags = C_SUB;
|
||||||
} else {
|
} else {
|
||||||
|
if (unlikely(node_ds(node) <= PAGEHDRSZ))
|
||||||
|
return MDBX_CORRUPTED;
|
||||||
MDBX_page *fp = node_data(node);
|
MDBX_page *fp = node_data(node);
|
||||||
mx->mx_db.md_xsize = 0;
|
mx->mx_db.md_xsize = 0;
|
||||||
mx->mx_db.md_flags = 0;
|
mx->mx_db.md_flags = 0;
|
||||||
@ -11320,12 +11342,6 @@ static int mdbx_xcursor_init1(MDBX_cursor *mc, MDBX_node *node) {
|
|||||||
mdbx_debug("Sub-db -%u root page %" PRIaPGNO, mx->mx_cursor.mc_dbi,
|
mdbx_debug("Sub-db -%u root page %" PRIaPGNO, mx->mx_cursor.mc_dbi,
|
||||||
mx->mx_db.md_root);
|
mx->mx_db.md_root);
|
||||||
mx->mx_dbflag = DB_VALID | DB_USRVALID | DB_DUPDATA;
|
mx->mx_dbflag = DB_VALID | DB_USRVALID | DB_DUPDATA;
|
||||||
/* FIXME: #if UINT_MAX < SIZE_MAX
|
|
||||||
if (mx->mx_dbx.md_cmp == mdbx_cmp_int && mx->mx_db.md_pad ==
|
|
||||||
sizeof(size_t))
|
|
||||||
mx->mx_dbx.md_cmp = mdbx_cmp_clong;
|
|
||||||
#endif */
|
|
||||||
|
|
||||||
return MDBX_SUCCESS;
|
return MDBX_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -12402,8 +12418,8 @@ static __cold int mdbx_page_check(MDBX_env *env, const MDBX_page *const mp,
|
|||||||
case 0 /* usual */:
|
case 0 /* usual */:
|
||||||
break;
|
break;
|
||||||
case F_SUBDATA /* sub-db */:
|
case F_SUBDATA /* sub-db */:
|
||||||
mdbx_assert(env, dsize == sizeof(MDBX_db));
|
mdbx_assert(env, dsize >= sizeof(MDBX_db));
|
||||||
if (unlikely(dsize != sizeof(MDBX_db)))
|
if (unlikely(dsize < sizeof(MDBX_db)))
|
||||||
return MDBX_CORRUPTED;
|
return MDBX_CORRUPTED;
|
||||||
break;
|
break;
|
||||||
case F_SUBDATA | F_DUPDATA /* dupsorted sub-tree */:
|
case F_SUBDATA | F_DUPDATA /* dupsorted sub-tree */:
|
||||||
@ -13433,7 +13449,10 @@ static int __cold mdbx_env_cwalk(mdbx_copy *my, pgno_t *pg, int flags) {
|
|||||||
toggle = my->mc_toggle;
|
toggle = my->mc_toggle;
|
||||||
}
|
}
|
||||||
} else if (node_flags(node) & F_SUBDATA) {
|
} else if (node_flags(node) & F_SUBDATA) {
|
||||||
MDBX_db db;
|
if (node_ds(node) < sizeof(MDBX_db)) {
|
||||||
|
rc = MDBX_CORRUPTED;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
/* Need writable leaf */
|
/* Need writable leaf */
|
||||||
if (mp != leaf) {
|
if (mp != leaf) {
|
||||||
@ -13443,13 +13462,14 @@ static int __cold mdbx_env_cwalk(mdbx_copy *my, pgno_t *pg, int flags) {
|
|||||||
node = page_node(mp, i);
|
node = page_node(mp, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(&db, node_data(node), sizeof(db));
|
MDBX_db db;
|
||||||
|
memcpy(&db, node_data(node), sizeof(MDBX_db));
|
||||||
my->mc_toggle = (short)toggle;
|
my->mc_toggle = (short)toggle;
|
||||||
rc = mdbx_env_cwalk(my, &db.md_root, node_flags(node) & F_DUPDATA);
|
rc = mdbx_env_cwalk(my, &db.md_root, node_flags(node) & F_DUPDATA);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto done;
|
goto done;
|
||||||
toggle = my->mc_toggle;
|
toggle = my->mc_toggle;
|
||||||
memcpy(node_data(node), &db, sizeof(db));
|
memcpy(node_data(node), &db, sizeof(MDBX_db));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -13978,24 +13998,26 @@ int __cold mdbx_env_get_fd(MDBX_env *env, mdbx_filehandle_t *arg) {
|
|||||||
* [in] db the MDBX_db record containing the stats to return.
|
* [in] db the MDBX_db record containing the stats to return.
|
||||||
* [out] arg the address of an MDBX_stat structure to receive the stats.
|
* [out] arg the address of an MDBX_stat structure to receive the stats.
|
||||||
* Returns 0, this function always succeeds. */
|
* Returns 0, this function always succeeds. */
|
||||||
static int __cold mdbx_stat0(const MDBX_env *env, const MDBX_db *db,
|
static void mdbx_stat0(const MDBX_env *env, const MDBX_db *db, MDBX_stat *dest,
|
||||||
MDBX_stat *arg) {
|
size_t bytes) {
|
||||||
arg->ms_psize = env->me_psize;
|
dest->ms_psize = env->me_psize;
|
||||||
arg->ms_depth = db->md_depth;
|
dest->ms_depth = db->md_depth;
|
||||||
arg->ms_branch_pages = db->md_branch_pages;
|
dest->ms_branch_pages = db->md_branch_pages;
|
||||||
arg->ms_leaf_pages = db->md_leaf_pages;
|
dest->ms_leaf_pages = db->md_leaf_pages;
|
||||||
arg->ms_overflow_pages = db->md_overflow_pages;
|
dest->ms_overflow_pages = db->md_overflow_pages;
|
||||||
arg->ms_entries = db->md_entries;
|
dest->ms_entries = db->md_entries;
|
||||||
return MDBX_SUCCESS;
|
if (likely(bytes >=
|
||||||
|
offsetof(MDBX_stat, ms_mod_txnid) + sizeof(dest->ms_mod_txnid)))
|
||||||
|
dest->ms_mod_txnid = db->md_mod_txnid;
|
||||||
}
|
}
|
||||||
|
|
||||||
int __cold mdbx_env_stat(MDBX_env *env, MDBX_stat *arg, size_t bytes) {
|
int __cold mdbx_env_stat(MDBX_env *env, MDBX_stat *dest, size_t bytes) {
|
||||||
return mdbx_env_stat_ex(env, NULL, arg, bytes);
|
return mdbx_env_stat_ex(env, NULL, dest, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
int __cold mdbx_env_stat_ex(const MDBX_env *env, const MDBX_txn *txn,
|
int __cold mdbx_env_stat_ex(const MDBX_env *env, const MDBX_txn *txn,
|
||||||
MDBX_stat *arg, size_t bytes) {
|
MDBX_stat *dest, size_t bytes) {
|
||||||
if (unlikely((env == NULL && txn == NULL) || arg == NULL))
|
if (unlikely((env == NULL && txn == NULL) || dest == NULL))
|
||||||
return MDBX_EINVAL;
|
return MDBX_EINVAL;
|
||||||
|
|
||||||
if (txn) {
|
if (txn) {
|
||||||
@ -14010,18 +14032,19 @@ int __cold mdbx_env_stat_ex(const MDBX_env *env, const MDBX_txn *txn,
|
|||||||
return MDBX_EINVAL;
|
return MDBX_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(bytes != sizeof(MDBX_stat)))
|
const size_t size_before_modtxnid = offsetof(MDBX_stat, ms_mod_txnid);
|
||||||
|
if (unlikely(bytes != sizeof(MDBX_stat)) && bytes != size_before_modtxnid)
|
||||||
return MDBX_EINVAL;
|
return MDBX_EINVAL;
|
||||||
|
|
||||||
if (txn)
|
if (txn) {
|
||||||
return mdbx_stat0(txn->mt_env, &txn->mt_dbs[MAIN_DBI], arg);
|
mdbx_stat0(txn->mt_env, &txn->mt_dbs[MAIN_DBI], dest, bytes);
|
||||||
|
return MDBX_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
const MDBX_meta *const recent_meta = mdbx_meta_head(env);
|
const MDBX_meta *const recent_meta = mdbx_meta_head(env);
|
||||||
const txnid_t txnid = mdbx_meta_txnid_fluid(env, recent_meta);
|
const txnid_t txnid = mdbx_meta_txnid_fluid(env, recent_meta);
|
||||||
const int err = mdbx_stat0(env, &recent_meta->mm_dbs[MAIN_DBI], arg);
|
mdbx_stat0(env, &recent_meta->mm_dbs[MAIN_DBI], dest, bytes);
|
||||||
if (unlikely(err != MDBX_SUCCESS))
|
|
||||||
return err;
|
|
||||||
mdbx_compiler_barrier();
|
mdbx_compiler_barrier();
|
||||||
if (likely(txnid == mdbx_meta_txnid_fluid(env, recent_meta) &&
|
if (likely(txnid == mdbx_meta_txnid_fluid(env, recent_meta) &&
|
||||||
recent_meta == mdbx_meta_head(env)))
|
recent_meta == mdbx_meta_head(env)))
|
||||||
@ -14292,6 +14315,8 @@ int mdbx_dbi_open_ex(MDBX_txn *txn, const char *table_name, unsigned user_flags,
|
|||||||
MDBX_node *node = page_node(mc.mc_pg[mc.mc_top], mc.mc_ki[mc.mc_top]);
|
MDBX_node *node = page_node(mc.mc_pg[mc.mc_top], mc.mc_ki[mc.mc_top]);
|
||||||
if (unlikely((node_flags(node) & (F_DUPDATA | F_SUBDATA)) != F_SUBDATA))
|
if (unlikely((node_flags(node) & (F_DUPDATA | F_SUBDATA)) != F_SUBDATA))
|
||||||
return MDBX_INCOMPATIBLE;
|
return MDBX_INCOMPATIBLE;
|
||||||
|
if (unlikely(data.iov_len < sizeof(MDBX_db)))
|
||||||
|
return MDBX_CORRUPTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rc != MDBX_SUCCESS && unlikely(txn->mt_flags & MDBX_RDONLY))
|
if (rc != MDBX_SUCCESS && unlikely(txn->mt_flags & MDBX_RDONLY))
|
||||||
@ -14395,32 +14420,32 @@ int mdbx_dbi_open(MDBX_txn *txn, const char *table_name, unsigned table_flags,
|
|||||||
return mdbx_dbi_open_ex(txn, table_name, table_flags, dbi, nullptr, nullptr);
|
return mdbx_dbi_open_ex(txn, table_name, table_flags, dbi, nullptr, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
int __cold mdbx_dbi_stat(MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *arg,
|
int __cold mdbx_dbi_stat(MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *dest,
|
||||||
size_t bytes) {
|
size_t bytes) {
|
||||||
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
|
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
|
||||||
if (unlikely(rc != MDBX_SUCCESS))
|
if (unlikely(rc != MDBX_SUCCESS))
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
if (unlikely(!arg))
|
if (unlikely(!dest))
|
||||||
return MDBX_EINVAL;
|
return MDBX_EINVAL;
|
||||||
|
|
||||||
if (unlikely(!TXN_DBI_EXIST(txn, dbi, DB_VALID)))
|
if (unlikely(!TXN_DBI_EXIST(txn, dbi, DB_VALID)))
|
||||||
return MDBX_EINVAL;
|
return MDBX_EINVAL;
|
||||||
|
|
||||||
if (unlikely(bytes != sizeof(MDBX_stat)))
|
const size_t size_before_modtxnid = offsetof(MDBX_stat, ms_mod_txnid);
|
||||||
|
if (unlikely(bytes != sizeof(MDBX_stat)) && bytes != size_before_modtxnid)
|
||||||
return MDBX_EINVAL;
|
return MDBX_EINVAL;
|
||||||
|
|
||||||
if (unlikely(txn->mt_flags & MDBX_TXN_BLOCKED))
|
if (unlikely(txn->mt_flags & MDBX_TXN_BLOCKED))
|
||||||
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_couple cx;
|
rc = mdbx_fetch_sdb(txn, dbi);
|
||||||
/* Stale, must read the DB's root. cursor_init does it for us. */
|
|
||||||
rc = mdbx_cursor_init(&cx.outer, txn, dbi);
|
|
||||||
if (unlikely(rc != MDBX_SUCCESS))
|
if (unlikely(rc != MDBX_SUCCESS))
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
return mdbx_stat0(txn->mt_env, &txn->mt_dbs[dbi], arg);
|
mdbx_stat0(txn->mt_env, &txn->mt_dbs[dbi], dest, bytes);
|
||||||
|
return MDBX_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mdbx_dbi_close_locked(MDBX_env *env, MDBX_dbi dbi) {
|
static int mdbx_dbi_close_locked(MDBX_env *env, MDBX_dbi dbi) {
|
||||||
@ -15232,9 +15257,9 @@ static int __cold mdbx_env_walk(mdbx_walk_ctx_t *ctx, const char *dbi,
|
|||||||
|
|
||||||
case F_SUBDATA /* sub-db */: {
|
case F_SUBDATA /* sub-db */: {
|
||||||
const size_t namelen = node_ks(node);
|
const size_t namelen = node_ks(node);
|
||||||
if (unlikely(namelen == 0 || node_ds(node) != sizeof(MDBX_db)))
|
if (unlikely(namelen == 0 || node_ds(node) < sizeof(MDBX_db)))
|
||||||
return MDBX_CORRUPTED;
|
return MDBX_CORRUPTED;
|
||||||
payload_size += sizeof(MDBX_db);
|
payload_size += node_ds(node);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case F_SUBDATA | F_DUPDATA /* dupsorted sub-tree */: {
|
case F_SUBDATA | F_DUPDATA /* dupsorted sub-tree */: {
|
||||||
@ -16114,9 +16139,7 @@ int mdbx_dbi_sequence(MDBX_txn *txn, MDBX_dbi dbi, uint64_t *result,
|
|||||||
return MDBX_BAD_DBI;
|
return MDBX_BAD_DBI;
|
||||||
|
|
||||||
if (unlikely(txn->mt_dbflags[dbi] & DB_STALE)) {
|
if (unlikely(txn->mt_dbflags[dbi] & DB_STALE)) {
|
||||||
MDBX_cursor_couple cx;
|
rc = mdbx_fetch_sdb(txn, dbi);
|
||||||
/* Stale, must read the DB's root. cursor_init does it for us. */
|
|
||||||
rc = mdbx_cursor_init(&cx.outer, txn, dbi);
|
|
||||||
if (unlikely(rc != MDBX_SUCCESS))
|
if (unlikely(rc != MDBX_SUCCESS))
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -365,7 +365,7 @@ typedef struct MDBX_db {
|
|||||||
pgno_t md_overflow_pages; /* number of overflow pages */
|
pgno_t md_overflow_pages; /* number of overflow pages */
|
||||||
uint64_t md_seq; /* table sequence counter */
|
uint64_t md_seq; /* table sequence counter */
|
||||||
uint64_t md_entries; /* number of data items */
|
uint64_t md_entries; /* number of data items */
|
||||||
uint64_t md_merkle; /* Merkle tree checksum */
|
uint64_t md_mod_txnid; /* txnid of last commited modification */
|
||||||
} MDBX_db;
|
} MDBX_db;
|
||||||
|
|
||||||
/* database size-related parameters */
|
/* database size-related parameters */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user