mirror of
https://github.com/isar/libmdbx.git
synced 2025-01-02 00:34:13 +08:00
mdbx: merge branch 'devel'.
Change-Id: I20c41204adaac9ccb13e59e63e829246158b6d41
This commit is contained in:
commit
b47a44582a
1
.github/actions/spelling/expect.txt
vendored
1
.github/actions/spelling/expect.txt
vendored
@ -239,6 +239,7 @@ datalen
|
||||
DATANAME
|
||||
DATASIGN
|
||||
datasync
|
||||
dataview
|
||||
datetime
|
||||
DBC
|
||||
dbenv
|
||||
|
@ -340,10 +340,12 @@ if(NOT DEFINED MDBX_CXX_STANDARD)
|
||||
set(MDBX_CXX_STANDARD 98)
|
||||
endif()
|
||||
endif()
|
||||
if(NOT HAS_C11 LESS 0)
|
||||
set(MDBX_C_STANDARD 11)
|
||||
else()
|
||||
# MSVC >= 19.28 (Microsoft Visual Studio 16.8) is mad!
|
||||
# It unable process Windows SDK headers in the C11 mode!
|
||||
if(HAS_C11 LESS 0 OR (MSVC AND MSVC_VERSION GREATER 1927))
|
||||
set(MDBX_C_STANDARD 99)
|
||||
else()
|
||||
set(MDBX_C_STANDARD 11)
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows" AND EXISTS "${MDBX_SOURCE_DIR}/ntdll.def")
|
||||
@ -756,14 +758,21 @@ if(NOT CMAKE_CONFIGURATION_TYPES)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# choice target to fetch definitions and options
|
||||
if(MDBX_BUILD_SHARED_LIBRARY)
|
||||
set(target4fetch mdbx)
|
||||
else()
|
||||
set(target4fetch mdbx-static)
|
||||
endif()
|
||||
|
||||
# get definitions
|
||||
get_target_property(defs_list mdbx-static COMPILE_DEFINITIONS)
|
||||
get_target_property(defs_list ${target4fetch} COMPILE_DEFINITIONS)
|
||||
if(defs_list)
|
||||
list(APPEND MDBX_BUILD_FLAGS ${defs_list})
|
||||
endif()
|
||||
|
||||
# get target compile options
|
||||
get_target_property(options_list mdbx-static COMPILE_OPTIONS)
|
||||
get_target_property(options_list ${target4fetch} COMPILE_OPTIONS)
|
||||
if(options_list)
|
||||
list(APPEND MDBX_BUILD_FLAGS ${options_list})
|
||||
endif()
|
||||
|
40
mdbx.h
40
mdbx.h
@ -483,9 +483,18 @@ typedef mode_t mdbx_mode_t;
|
||||
MDBX_CXX01_CONSTEXPR ENUM operator&(ENUM a, ENUM b) { \
|
||||
return ENUM(std::size_t(a) & std::size_t(b)); \
|
||||
} \
|
||||
MDBX_CXX01_CONSTEXPR ENUM operator&(ENUM a, size_t b) { \
|
||||
return ENUM(std::size_t(a) & b); \
|
||||
} \
|
||||
MDBX_CXX01_CONSTEXPR ENUM operator&(size_t a, ENUM b) { \
|
||||
return ENUM(a & std::size_t(b)); \
|
||||
} \
|
||||
MDBX_CXX14_CONSTEXPR ENUM &operator&=(ENUM &a, ENUM b) { return a = a & b; } \
|
||||
MDBX_CXX01_CONSTEXPR ENUM operator~(ENUM a) { \
|
||||
return ENUM(~std::size_t(a)); \
|
||||
MDBX_CXX14_CONSTEXPR ENUM &operator&=(ENUM &a, size_t b) { \
|
||||
return a = a & b; \
|
||||
} \
|
||||
MDBX_CXX01_CONSTEXPR std::size_t operator~(ENUM a) { \
|
||||
return ~std::size_t(a); \
|
||||
} \
|
||||
MDBX_CXX01_CONSTEXPR ENUM operator^(ENUM a, ENUM b) { \
|
||||
return ENUM(std::size_t(a) ^ std::size_t(b)); \
|
||||
@ -1505,7 +1514,20 @@ enum MDBX_cursor_op {
|
||||
|
||||
/** \ref MDBX_DUPFIXED -only: Position at previous page and return up to
|
||||
* a page of duplicate data items. */
|
||||
MDBX_PREV_MULTIPLE
|
||||
MDBX_PREV_MULTIPLE,
|
||||
|
||||
/** Position at first key-value pair greater than or equal to specified,
|
||||
* return both key and data, and the return code depends on a exact match.
|
||||
*
|
||||
* For non DUPSORT-ed collections this work the same to \ref MDBX_SET_RANGE,
|
||||
* but returns \ref MDBX_SUCCESS if key found exactly and
|
||||
* \ref MDBX_RESULT_TRUE if greater key was found.
|
||||
*
|
||||
* For DUPSORT-ed a data value is taken into account for duplicates,
|
||||
* i.e. for a pairs/tuples of a key and an each data value of duplicates.
|
||||
* Returns \ref MDBX_SUCCESS if key-value pair found exactly and
|
||||
* \ref MDBX_RESULT_TRUE if the next pair was returned. */
|
||||
MDBX_SET_LOWERBOUND
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
/** \ingroup c_cursors */
|
||||
@ -3861,6 +3883,18 @@ mdbx_cursor_txn(const MDBX_cursor *cursor);
|
||||
* \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open(). */
|
||||
LIBMDBX_API MDBX_dbi mdbx_cursor_dbi(const MDBX_cursor *cursor);
|
||||
|
||||
/** \brief Copy cursor position and state.
|
||||
* \ingroup c_cursors
|
||||
*
|
||||
* \param [in] src A source cursor handle returned
|
||||
* by \ref mdbx_cursor_create() or \ref mdbx_cursor_open().
|
||||
*
|
||||
* \param [in,out] dest A destination cursor handle returned
|
||||
* by \ref mdbx_cursor_create() or \ref mdbx_cursor_open().
|
||||
*
|
||||
* \returns A non-zero error value on failure and 0 on success. */
|
||||
LIBMDBX_API int mdbx_cursor_copy(const MDBX_cursor *src, MDBX_cursor *dest);
|
||||
|
||||
/** \brief Retrieve by cursor.
|
||||
* \ingroup c_crud
|
||||
*
|
||||
|
634
src/core.c
634
src/core.c
File diff suppressed because it is too large
Load Diff
@ -778,8 +778,6 @@ struct MDBX_txn {
|
||||
MDBX_db *mt_dbs;
|
||||
/* Array of sequence numbers for each DB handle */
|
||||
unsigned *mt_dbiseqs;
|
||||
/* In write txns, array of cursors for each DB */
|
||||
MDBX_cursor **mt_cursors;
|
||||
|
||||
/* Transaction DBI Flags */
|
||||
#define DBI_DIRTY MDBX_DBI_DIRTY /* DB was written in this txn */
|
||||
@ -806,6 +804,8 @@ struct MDBX_txn {
|
||||
MDBX_reader *reader;
|
||||
} to;
|
||||
struct {
|
||||
/* In write txns, array of cursors for each DB */
|
||||
MDBX_cursor **cursors;
|
||||
pgno_t *reclaimed_pglist; /* Reclaimed GC pages */
|
||||
txnid_t last_reclaimed; /* ID of last used record */
|
||||
pgno_t loose_refund_wl /* FIXME: describe */;
|
||||
|
@ -115,7 +115,7 @@ void __hot maker::pair(serial_t serial, const buffer &key, buffer &value,
|
||||
serial_t key_serial = serial;
|
||||
serial_t value_serial = value_age << mapping.split;
|
||||
if (mapping.split) {
|
||||
if (key_essentials.flags & MDBX_DUPSORT) {
|
||||
if (MDBX_db_flags_t(key_essentials.flags) & MDBX_DUPSORT) {
|
||||
key_serial >>= mapping.split;
|
||||
value_serial += serial & mask(mapping.split);
|
||||
} else {
|
||||
@ -203,7 +203,7 @@ void maker::setup(const config::actor_params_pod &actor, unsigned actor_id,
|
||||
#endif
|
||||
key_essentials.flags =
|
||||
actor.table_flags &
|
||||
uint16_t(MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT);
|
||||
MDBX_db_flags_t(MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT);
|
||||
assert(actor.keylen_min <= UINT16_MAX);
|
||||
key_essentials.minlen = (uint16_t)actor.keylen_min;
|
||||
assert(actor.keylen_max <= UINT32_MAX);
|
||||
@ -213,7 +213,7 @@ void maker::setup(const config::actor_params_pod &actor, unsigned actor_id,
|
||||
actor.pagesize, MDBX_db_flags_t(key_essentials.flags))));
|
||||
|
||||
value_essentials.flags =
|
||||
actor.table_flags & uint16_t(MDBX_INTEGERDUP | MDBX_REVERSEDUP);
|
||||
actor.table_flags & MDBX_db_flags_t(MDBX_INTEGERDUP | MDBX_REVERSEDUP);
|
||||
assert(actor.datalen_min <= UINT16_MAX);
|
||||
value_essentials.minlen = (uint16_t)actor.datalen_min;
|
||||
assert(actor.datalen_max <= UINT32_MAX);
|
||||
@ -236,35 +236,41 @@ void maker::setup(const config::actor_params_pod &actor, unsigned actor_id,
|
||||
}
|
||||
|
||||
void maker::make_linear() {
|
||||
mapping.mesh = (key_essentials.flags & MDBX_DUPSORT) ? 0 : mapping.split;
|
||||
mapping.mesh = (MDBX_db_flags_t(key_essentials.flags) & MDBX_DUPSORT)
|
||||
? 0
|
||||
: mapping.split;
|
||||
mapping.rotate = 0;
|
||||
mapping.offset = 0;
|
||||
const auto max_serial = mask(mapping.width) + base;
|
||||
const auto max_key_serial =
|
||||
(mapping.split && (key_essentials.flags & MDBX_DUPSORT))
|
||||
(mapping.split && (MDBX_db_flags_t(key_essentials.flags) & MDBX_DUPSORT))
|
||||
? max_serial >> mapping.split
|
||||
: max_serial;
|
||||
const auto max_value_serial =
|
||||
(mapping.split && (key_essentials.flags & MDBX_DUPSORT))
|
||||
(mapping.split && (MDBX_db_flags_t(key_essentials.flags) & MDBX_DUPSORT))
|
||||
? mask(mapping.split)
|
||||
: 0;
|
||||
|
||||
while (key_essentials.minlen < 8 &&
|
||||
(key_essentials.minlen == 0 ||
|
||||
mask(key_essentials.minlen * 8) < max_key_serial)) {
|
||||
key_essentials.minlen +=
|
||||
(key_essentials.flags & (MDBX_INTEGERKEY | MDBX_INTEGERDUP)) ? 4 : 1;
|
||||
key_essentials.minlen += (MDBX_db_flags_t(key_essentials.flags) &
|
||||
(MDBX_INTEGERKEY | MDBX_INTEGERDUP))
|
||||
? 4
|
||||
: 1;
|
||||
if (key_essentials.maxlen < key_essentials.minlen)
|
||||
key_essentials.maxlen = key_essentials.minlen;
|
||||
}
|
||||
|
||||
if ((key_essentials.flags | value_essentials.flags) & MDBX_DUPSORT)
|
||||
if (MDBX_db_flags_t(key_essentials.flags | value_essentials.flags) &
|
||||
MDBX_DUPSORT)
|
||||
while (value_essentials.minlen < 8 &&
|
||||
(value_essentials.minlen == 0 ||
|
||||
mask(value_essentials.minlen * 8) < max_value_serial)) {
|
||||
value_essentials.minlen +=
|
||||
(value_essentials.flags & (MDBX_INTEGERKEY | MDBX_INTEGERDUP)) ? 4
|
||||
: 1;
|
||||
value_essentials.minlen += (MDBX_db_flags_t(value_essentials.flags) &
|
||||
(MDBX_INTEGERKEY | MDBX_INTEGERDUP))
|
||||
? 4
|
||||
: 1;
|
||||
if (value_essentials.maxlen < value_essentials.minlen)
|
||||
value_essentials.maxlen = value_essentials.minlen;
|
||||
}
|
||||
@ -272,8 +278,9 @@ void maker::make_linear() {
|
||||
|
||||
bool maker::is_unordered() const {
|
||||
return mapping.rotate ||
|
||||
mapping.mesh >
|
||||
((key_essentials.flags & MDBX_DUPSORT) ? 0 : mapping.split);
|
||||
mapping.mesh > ((MDBX_db_flags_t(key_essentials.flags) & MDBX_DUPSORT)
|
||||
? 0
|
||||
: mapping.split);
|
||||
}
|
||||
|
||||
bool maker::increment(serial_t &serial, int delta) const {
|
||||
@ -374,9 +381,9 @@ void __hot maker::mk_continue(const serial_t serial, const essentials ¶ms,
|
||||
#endif
|
||||
assert(length(serial) <= out.value.iov_len);
|
||||
out.value.iov_base = out.bytes;
|
||||
if (params.flags & (MDBX_INTEGERKEY | MDBX_INTEGERDUP)) {
|
||||
if (MDBX_db_flags_t(params.flags) & (MDBX_INTEGERKEY | MDBX_INTEGERDUP)) {
|
||||
assert(params.maxlen == params.minlen);
|
||||
if (params.flags & (MDBX_INTEGERKEY | MDBX_INTEGERDUP))
|
||||
if (MDBX_db_flags_t(params.flags) & (MDBX_INTEGERKEY | MDBX_INTEGERDUP))
|
||||
assert(params.minlen == 4 || params.minlen == 8);
|
||||
out.u64 = serial;
|
||||
if (!is_byteorder_le() && out.value.iov_len != 8)
|
||||
@ -393,7 +400,8 @@ void __hot maker::mk_continue(const serial_t serial, const essentials ¶ms,
|
||||
} else
|
||||
memset(out.bytes + 8, '\0', out.value.iov_len - prefix);
|
||||
}
|
||||
if (unlikely(params.flags & (MDBX_REVERSEKEY | MDBX_REVERSEDUP)))
|
||||
if (unlikely(MDBX_db_flags_t(params.flags) &
|
||||
(MDBX_REVERSEKEY | MDBX_REVERSEDUP)))
|
||||
std::reverse((char *)out.value.iov_base,
|
||||
(char *)out.value.iov_base + out.value.iov_len);
|
||||
}
|
||||
|
@ -80,8 +80,8 @@ bool testcase_nested::teardown() {
|
||||
|
||||
void testcase_nested::push_txn() {
|
||||
MDBX_txn *txn;
|
||||
MDBX_txn_flags_t flags =
|
||||
MDBX_txn_flags_t(prng32() & (MDBX_TXN_NOSYNC | MDBX_TXN_NOMETASYNC));
|
||||
MDBX_txn_flags_t flags = MDBX_txn_flags_t(
|
||||
prng32() & uint32_t(MDBX_TXN_NOSYNC | MDBX_TXN_NOMETASYNC));
|
||||
int err = mdbx_txn_begin(db_guard.get(), txn_guard.get(), flags, &txn);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
failure_perror("mdbx_txn_begin(nested)", err);
|
||||
|
@ -414,6 +414,7 @@ int osal_actor_poll(mdbx_pid_t &pid, unsigned timeout) {
|
||||
while (sigalarm_tail == sigalarm_head) {
|
||||
int status;
|
||||
pid = waitpid(0, &status, options);
|
||||
const int err = errno;
|
||||
|
||||
if (pid > 0) {
|
||||
if (WIFEXITED(status))
|
||||
@ -437,20 +438,19 @@ int osal_actor_poll(mdbx_pid_t &pid, unsigned timeout) {
|
||||
if (sigusr1_tail != sigusr1_head) {
|
||||
sigusr1_tail = sigusr1_head;
|
||||
logging::progress_canary(true);
|
||||
if (pid < 0 && errno == EINTR)
|
||||
if (pid < 0 && err == EINTR)
|
||||
continue;
|
||||
}
|
||||
if (sigusr2_tail != sigusr2_head) {
|
||||
sigusr2_tail = sigusr2_head;
|
||||
logging::progress_canary(false);
|
||||
if (pid < 0 && errno == EINTR)
|
||||
if (pid < 0 && err == EINTR)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pid == 0)
|
||||
break;
|
||||
|
||||
int err = errno;
|
||||
if (err != EINTR)
|
||||
return err;
|
||||
}
|
||||
|
393
test/test.cc
393
test/test.cc
@ -310,7 +310,7 @@ void testcase::txn_inject_writefault(void) {
|
||||
void testcase::txn_inject_writefault(MDBX_txn *txn) {
|
||||
if (config.params.inject_writefaultn && txn) {
|
||||
if (config.params.inject_writefaultn <= nops_completed &&
|
||||
(mdbx_txn_flags(txn) & MDBX_RDONLY) == 0) {
|
||||
(MDBX_txn_flags_t(mdbx_txn_flags(txn)) & MDBX_TXN_RDONLY) == 0) {
|
||||
log_verbose(
|
||||
"== txn_inject_writefault(): got %u nops or more, inject FAULT",
|
||||
config.params.inject_writefaultn);
|
||||
@ -645,46 +645,389 @@ bool test_execute(const actor_config &config_const) {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
enum speculum_cursors : int {
|
||||
lowerbound = 0,
|
||||
prev = 1,
|
||||
prev_prev = 2,
|
||||
next = 3,
|
||||
next_next = 4
|
||||
};
|
||||
|
||||
bool testcase::is_same(const Item &a, const Item &b) const {
|
||||
if (!is_samedata(dataview2iov(a.first), dataview2iov(b.first)))
|
||||
return false;
|
||||
if ((config.params.table_flags & MDBX_DUPSORT) &&
|
||||
!is_samedata(dataview2iov(a.second), dataview2iov(b.second)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool testcase::is_same(const testcase::SET::const_iterator &it,
|
||||
const MDBX_val &k, const MDBX_val &v) const {
|
||||
|
||||
return is_samedata(dataview2iov(it->first), k) &&
|
||||
is_samedata(dataview2iov(it->second), v);
|
||||
}
|
||||
|
||||
void testcase::verbose(const char *where, const char *stage,
|
||||
const testcase::SET::const_iterator &it) const {
|
||||
if (it == speculum.end())
|
||||
log_verbose("speculum-%s: %s expect END", where, stage);
|
||||
else {
|
||||
char dump_key[32], dump_value[32];
|
||||
MDBX_val it_key = dataview2iov(it->first);
|
||||
MDBX_val it_data = dataview2iov(it->second);
|
||||
log_verbose("speculum-%s: %s expect {%s, %s}", where, stage,
|
||||
mdbx_dump_val(&it_key, dump_key, sizeof(dump_key)),
|
||||
mdbx_dump_val(&it_data, dump_value, sizeof(dump_value)));
|
||||
}
|
||||
}
|
||||
|
||||
void testcase::verbose(const char *where, const char *stage,
|
||||
const MDBX_val &key, const MDBX_val &data,
|
||||
int err) const {
|
||||
char dump_key[32], dump_value[32];
|
||||
if (err != MDBX_SUCCESS && err != MDBX_RESULT_TRUE)
|
||||
log_verbose("speculum-%s: %s cursor {%d, %s}", where, stage, err,
|
||||
mdbx_strerror(err));
|
||||
else
|
||||
log_verbose("speculum-%s: %s cursor {%s, %s}", where, stage,
|
||||
mdbx_dump_val(&key, dump_key, sizeof(dump_key)),
|
||||
mdbx_dump_val(&data, dump_value, sizeof(dump_value)));
|
||||
}
|
||||
|
||||
void testcase::speculum_check_iterator(const char *where, const char *stage,
|
||||
const testcase::SET::const_iterator &it,
|
||||
const MDBX_val &key,
|
||||
const MDBX_val &data) const {
|
||||
char dump_key[32], dump_value[32];
|
||||
MDBX_val it_key = dataview2iov(it->first);
|
||||
MDBX_val it_data = dataview2iov(it->second);
|
||||
// log_verbose("speculum-%s: %s expect {%s, %s}", where, stage,
|
||||
// mdbx_dump_val(&it_key, dump_key, sizeof(dump_key)),
|
||||
// mdbx_dump_val(&it_data, dump_value, sizeof(dump_value)));
|
||||
if (!is_samedata(it_key, key))
|
||||
failure("speculum-%s: %s key mismatch %s (must) != %s", where, stage,
|
||||
mdbx_dump_val(&it_key, dump_key, sizeof(dump_key)),
|
||||
mdbx_dump_val(&key, dump_value, sizeof(dump_value)));
|
||||
if (!is_samedata(it_data, data))
|
||||
failure("speculum-%s: %s data mismatch %s (must) != %s", where, stage,
|
||||
mdbx_dump_val(&it_data, dump_key, sizeof(dump_key)),
|
||||
mdbx_dump_val(&data, dump_value, sizeof(dump_value)));
|
||||
}
|
||||
|
||||
void testcase::speculum_check_cursor(const char *where, const char *stage,
|
||||
const testcase::SET::const_iterator &it,
|
||||
int cursor_err, const MDBX_val &cursor_key,
|
||||
const MDBX_val &cursor_data) const {
|
||||
// verbose(where, stage, cursor_key, cursor_data, cursor_err);
|
||||
// verbose(where, stage, it);
|
||||
if (cursor_err != MDBX_SUCCESS && cursor_err != MDBX_NOTFOUND &&
|
||||
cursor_err != MDBX_RESULT_TRUE)
|
||||
failure("speculum-%s: %s %s %d %s", where, stage, "cursor-get", cursor_err,
|
||||
mdbx_strerror(cursor_err));
|
||||
|
||||
char dump_key[32], dump_value[32];
|
||||
if (it == speculum.end() && cursor_err != MDBX_NOTFOUND)
|
||||
failure("speculum-%s: %s extra pair {%s, %s}", where, stage,
|
||||
mdbx_dump_val(&cursor_key, dump_key, sizeof(dump_key)),
|
||||
mdbx_dump_val(&cursor_data, dump_value, sizeof(dump_value)));
|
||||
else if (it != speculum.end() && cursor_err == MDBX_NOTFOUND) {
|
||||
MDBX_val it_key = dataview2iov(it->first);
|
||||
MDBX_val it_data = dataview2iov(it->second);
|
||||
failure("speculum-%s: %s lack pair {%s, %s}", where, stage,
|
||||
mdbx_dump_val(&it_key, dump_key, sizeof(dump_key)),
|
||||
mdbx_dump_val(&it_data, dump_value, sizeof(dump_value)));
|
||||
} else if (cursor_err == MDBX_SUCCESS || cursor_err == MDBX_RESULT_TRUE)
|
||||
speculum_check_iterator(where, stage, it, cursor_key, cursor_data);
|
||||
}
|
||||
|
||||
void testcase::speculum_check_cursor(const char *where, const char *stage,
|
||||
const testcase::SET::const_iterator &it,
|
||||
MDBX_cursor *cursor,
|
||||
const MDBX_cursor_op op) const {
|
||||
MDBX_val cursor_key = {};
|
||||
MDBX_val cursor_data = {};
|
||||
int err;
|
||||
if (std::next(it) == speculum.end() && op == MDBX_PREV &&
|
||||
(config.params.table_flags & MDBX_DUPSORT)) {
|
||||
/* Workaround for MDBX/LMDB flaw */
|
||||
err = mdbx_cursor_get(cursor, &cursor_key, &cursor_data, MDBX_LAST);
|
||||
if (err == MDBX_SUCCESS)
|
||||
err = mdbx_cursor_get(cursor, &cursor_key, &cursor_data, MDBX_LAST_DUP);
|
||||
} else
|
||||
err = mdbx_cursor_get(cursor, &cursor_key, &cursor_data, op);
|
||||
return speculum_check_cursor(where, stage, it, err, cursor_key, cursor_data);
|
||||
}
|
||||
|
||||
void testcase::speculum_prepare_cursors(const Item &item) {
|
||||
int err;
|
||||
assert(config.params.speculum);
|
||||
if (speculum_cursors[lowerbound])
|
||||
for (auto &guard : speculum_cursors) {
|
||||
if (txn_guard.get() != mdbx_cursor_txn(guard.get()) ||
|
||||
dbi != mdbx_cursor_dbi(guard.get())) {
|
||||
err = mdbx_cursor_bind(txn_guard.get(), guard.get(), dbi);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
failure_perror("mdbx_cursor_bind()", err);
|
||||
}
|
||||
}
|
||||
else
|
||||
for (auto &guard : speculum_cursors) {
|
||||
MDBX_cursor *cursor = nullptr;
|
||||
err = mdbx_cursor_open(txn_guard.get(), dbi, &cursor);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
failure_perror("mdbx_cursor_open()", err);
|
||||
guard.reset(cursor);
|
||||
}
|
||||
|
||||
const auto cursor_lowerbound = speculum_cursors[lowerbound].get();
|
||||
const MDBX_val item_key = dataview2iov(item.first),
|
||||
item_data = dataview2iov(item.second);
|
||||
MDBX_val lowerbound_key = item_key;
|
||||
MDBX_val lowerbound_data = item_data;
|
||||
// verbose("prepare-cursors", "item", item_key, item_data);
|
||||
err = mdbx_cursor_get(cursor_lowerbound, &lowerbound_key, &lowerbound_data,
|
||||
MDBX_SET_LOWERBOUND);
|
||||
// verbose("prepare-cursors", "lowerbound", lowerbound_key, lowerbound_data,
|
||||
// err);
|
||||
if (unlikely(err != MDBX_SUCCESS && err != MDBX_RESULT_TRUE &&
|
||||
err != MDBX_NOTFOUND))
|
||||
failure("speculum-%s: %s %s %d %s", "prepare-cursors", "lowerbound",
|
||||
"cursor-get", err, mdbx_strerror(err));
|
||||
|
||||
auto it_lowerbound = speculum.lower_bound(item);
|
||||
// verbose("prepare-cursors", "lowerbound", it_lowerbound);
|
||||
speculum_check_cursor("prepare-cursors", "lowerbound", it_lowerbound, err,
|
||||
lowerbound_key, lowerbound_data);
|
||||
|
||||
const auto cursor_prev = speculum_cursors[prev].get();
|
||||
err = mdbx_cursor_copy(cursor_lowerbound, cursor_prev);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
failure("speculum-%s: %s %s %d %s", "prepare-cursors", "prev",
|
||||
"cursor-copy", err, mdbx_strerror(err));
|
||||
auto it_prev = it_lowerbound;
|
||||
if (it_prev != speculum.begin()) {
|
||||
speculum_check_cursor("prepare-cursors", "prev", --it_prev, cursor_prev,
|
||||
MDBX_PREV);
|
||||
} else if ((err = mdbx_cursor_on_first(cursor_prev)) != MDBX_RESULT_TRUE)
|
||||
failure("speculum-%s: %s on-first %d %s", "prepare-cursors", "prev", err,
|
||||
mdbx_strerror(err));
|
||||
|
||||
const auto cursor_prev_prev = speculum_cursors[prev_prev].get();
|
||||
err = mdbx_cursor_copy(cursor_prev, cursor_prev_prev);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
failure("speculum-%s: %s %s %d %s", "prepare-cursors", "prev-prev",
|
||||
"cursor-copy", err, mdbx_strerror(err));
|
||||
auto it_prev_prev = it_prev;
|
||||
if (it_prev_prev != speculum.begin()) {
|
||||
speculum_check_cursor("prepare-cursors", "prev-prev", --it_prev_prev,
|
||||
cursor_prev_prev, MDBX_PREV);
|
||||
} else if ((err = mdbx_cursor_on_first(cursor_prev_prev)) != MDBX_RESULT_TRUE)
|
||||
failure("speculum-%s: %s on-first %d %s", "prepare-cursors", "prev-prev",
|
||||
err, mdbx_strerror(err));
|
||||
|
||||
const auto cursor_next = speculum_cursors[next].get();
|
||||
err = mdbx_cursor_copy(cursor_lowerbound, cursor_next);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
failure("speculum-%s: %s %s %d %s", "prepare-cursors", "next",
|
||||
"cursor-copy", err, mdbx_strerror(err));
|
||||
auto it_next = it_lowerbound;
|
||||
if (it_next != speculum.end()) {
|
||||
speculum_check_cursor("prepare-cursors", "next", ++it_next, cursor_next,
|
||||
MDBX_NEXT);
|
||||
} else if ((err = mdbx_cursor_on_last(cursor_next)) != MDBX_RESULT_TRUE)
|
||||
failure("speculum-%s: %s on-last %d %s", "prepare-cursors", "next", err,
|
||||
mdbx_strerror(err));
|
||||
|
||||
const auto cursor_next_next = speculum_cursors[next_next].get();
|
||||
err = mdbx_cursor_copy(cursor_next, cursor_next_next);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
failure("speculum-%s: %s %s %d %s", "prepare-cursors", "next-next",
|
||||
"cursor-copy", err, mdbx_strerror(err));
|
||||
auto it_next_next = it_next;
|
||||
if (it_next_next != speculum.end()) {
|
||||
speculum_check_cursor("prepare-cursors", "next-next", ++it_next_next,
|
||||
cursor_next_next, MDBX_NEXT);
|
||||
} else if ((err = mdbx_cursor_on_last(cursor_next_next)) != MDBX_RESULT_TRUE)
|
||||
failure("speculum-%s: %s on-last %d %s", "prepare-cursors", "next-next",
|
||||
err, mdbx_strerror(err));
|
||||
}
|
||||
|
||||
int testcase::insert(const keygen::buffer &akey, const keygen::buffer &adata,
|
||||
MDBX_put_flags_t flags) {
|
||||
int err = mdbx_put(txn_guard.get(), dbi, &akey->value, &adata->value, flags);
|
||||
if (err == MDBX_SUCCESS && config.params.speculum) {
|
||||
const auto S_key = S(akey);
|
||||
const auto S_data = S(adata);
|
||||
const bool inserted = speculum.emplace(S_key, S_data).second;
|
||||
assert(inserted);
|
||||
(void)inserted;
|
||||
int err;
|
||||
bool rc = true;
|
||||
Item item;
|
||||
if (config.params.speculum) {
|
||||
item.first = iov2dataview(akey);
|
||||
item.second = iov2dataview(adata);
|
||||
speculum_prepare_cursors(item);
|
||||
}
|
||||
return err;
|
||||
|
||||
err = mdbx_put(txn_guard.get(), dbi, &akey->value, &adata->value, flags);
|
||||
if (err != MDBX_SUCCESS && err != MDBX_KEYEXIST)
|
||||
return err;
|
||||
|
||||
if (config.params.speculum) {
|
||||
char dump_key[32], dump_value[32];
|
||||
const auto insertion_result = speculum.insert(item);
|
||||
if (err == MDBX_KEYEXIST && insertion_result.second) {
|
||||
log_error("speculum.insert: unexpected %s {%s, %s}", "MDBX_KEYEXIST",
|
||||
mdbx_dump_val(&akey->value, dump_key, sizeof(dump_key)),
|
||||
mdbx_dump_val(&adata->value, dump_value, sizeof(dump_value)));
|
||||
rc = false;
|
||||
}
|
||||
if (err == MDBX_SUCCESS && !insertion_result.second) {
|
||||
log_error("speculum.insert: unexpected %s {%s, %s}", "MDBX_SUCCESS",
|
||||
mdbx_dump_val(&akey->value, dump_key, sizeof(dump_key)),
|
||||
mdbx_dump_val(&adata->value, dump_value, sizeof(dump_value)));
|
||||
rc = false;
|
||||
}
|
||||
|
||||
if (insertion_result.first != speculum.begin()) {
|
||||
const auto cursor_prev = speculum_cursors[prev].get();
|
||||
auto it_prev = insertion_result.first;
|
||||
speculum_check_cursor("after-insert", "prev", --it_prev, cursor_prev,
|
||||
MDBX_GET_CURRENT);
|
||||
if (it_prev != speculum.begin()) {
|
||||
const auto cursor_prev_prev = speculum_cursors[prev_prev].get();
|
||||
auto it_prev_prev = it_prev;
|
||||
speculum_check_cursor("after-insert", "prev-prev", --it_prev_prev,
|
||||
cursor_prev_prev, MDBX_GET_CURRENT);
|
||||
}
|
||||
}
|
||||
|
||||
auto it_lowerbound = insertion_result.first;
|
||||
if (++it_lowerbound != speculum.end()) {
|
||||
const auto cursor_lowerbound = speculum_cursors[lowerbound].get();
|
||||
speculum_check_cursor("after-insert", "lowerbound", it_lowerbound,
|
||||
cursor_lowerbound, MDBX_GET_CURRENT);
|
||||
|
||||
auto it_next = it_lowerbound;
|
||||
if (++it_next != speculum.end()) {
|
||||
const auto cursor_next = speculum_cursors[next].get();
|
||||
speculum_check_cursor("after-insert", "next", it_next, cursor_next,
|
||||
MDBX_GET_CURRENT);
|
||||
|
||||
auto it_next_next = it_next;
|
||||
if (++it_next_next != speculum.end()) {
|
||||
const auto cursor_next_next = speculum_cursors[next_next].get();
|
||||
speculum_check_cursor("after-insert", "next-next", it_next_next,
|
||||
cursor_next_next, MDBX_GET_CURRENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc ? MDBX_SUCCESS : MDBX_RESULT_TRUE;
|
||||
}
|
||||
|
||||
int testcase::replace(const keygen::buffer &akey,
|
||||
const keygen::buffer &new_data,
|
||||
const keygen::buffer &old_data, MDBX_put_flags_t flags) {
|
||||
if (config.params.speculum) {
|
||||
const auto S_key = S(akey);
|
||||
const auto S_old = S(old_data);
|
||||
const auto S_new = S(new_data);
|
||||
const auto S_key = iov2dataview(akey);
|
||||
const auto S_old = iov2dataview(old_data);
|
||||
const auto S_new = iov2dataview(new_data);
|
||||
const auto removed = speculum.erase(SET::key_type(S_key, S_old));
|
||||
assert(removed == 1);
|
||||
(void)removed;
|
||||
const bool inserted = speculum.emplace(S_key, S_new).second;
|
||||
assert(inserted);
|
||||
(void)inserted;
|
||||
if (unlikely(removed != 1)) {
|
||||
char dump_key[128], dump_value[128];
|
||||
log_error(
|
||||
"speculum-%s: %s old value {%s, %s}", "replace",
|
||||
(removed > 1) ? "multi" : "no",
|
||||
mdbx_dump_val(&akey->value, dump_key, sizeof(dump_key)),
|
||||
mdbx_dump_val(&old_data->value, dump_value, sizeof(dump_value)));
|
||||
}
|
||||
if (unlikely(!speculum.emplace(S_key, S_new).second)) {
|
||||
char dump_key[128], dump_value[128];
|
||||
log_error(
|
||||
"speculum-replace: new pair not inserted {%s, %s}",
|
||||
mdbx_dump_val(&akey->value, dump_key, sizeof(dump_key)),
|
||||
mdbx_dump_val(&new_data->value, dump_value, sizeof(dump_value)));
|
||||
}
|
||||
}
|
||||
return mdbx_replace(txn_guard.get(), dbi, &akey->value, &new_data->value,
|
||||
&old_data->value, flags);
|
||||
}
|
||||
|
||||
int testcase::remove(const keygen::buffer &akey, const keygen::buffer &adata) {
|
||||
int err;
|
||||
bool rc = true;
|
||||
Item item;
|
||||
if (config.params.speculum) {
|
||||
const auto S_key = S(akey);
|
||||
const auto S_data = S(adata);
|
||||
const auto removed = speculum.erase(SET::key_type(S_key, S_data));
|
||||
assert(removed == 1);
|
||||
(void)removed;
|
||||
item.first = iov2dataview(akey);
|
||||
item.second = iov2dataview(adata);
|
||||
speculum_prepare_cursors(item);
|
||||
}
|
||||
return mdbx_del(txn_guard.get(), dbi, &akey->value, &adata->value);
|
||||
|
||||
err = mdbx_del(txn_guard.get(), dbi, &akey->value, &adata->value);
|
||||
if (err != MDBX_NOTFOUND && err != MDBX_SUCCESS)
|
||||
return err;
|
||||
|
||||
if (config.params.speculum) {
|
||||
char dump_key[32], dump_value[32];
|
||||
const auto it_found = speculum.find(item);
|
||||
if (it_found == speculum.end()) {
|
||||
if (err != MDBX_NOTFOUND) {
|
||||
log_error("speculum.remove: unexpected %s {%s, %s}", "MDBX_SUCCESS",
|
||||
mdbx_dump_val(&akey->value, dump_key, sizeof(dump_key)),
|
||||
mdbx_dump_val(&adata->value, dump_value, sizeof(dump_value)));
|
||||
rc = false;
|
||||
}
|
||||
} else {
|
||||
if (err != MDBX_SUCCESS) {
|
||||
log_error("speculum.remove: unexpected %s {%s, %s}", "MDBX_NOTFOUND",
|
||||
mdbx_dump_val(&akey->value, dump_key, sizeof(dump_key)),
|
||||
mdbx_dump_val(&adata->value, dump_value, sizeof(dump_value)));
|
||||
rc = false;
|
||||
}
|
||||
|
||||
if (it_found != speculum.begin()) {
|
||||
const auto cursor_prev = speculum_cursors[prev].get();
|
||||
auto it_prev = it_found;
|
||||
speculum_check_cursor("after-remove", "prev", --it_prev, cursor_prev,
|
||||
MDBX_GET_CURRENT);
|
||||
if (it_prev != speculum.begin()) {
|
||||
const auto cursor_prev_prev = speculum_cursors[prev_prev].get();
|
||||
auto it_prev_prev = it_prev;
|
||||
speculum_check_cursor("after-remove", "prev-prev", --it_prev_prev,
|
||||
cursor_prev_prev, MDBX_GET_CURRENT);
|
||||
}
|
||||
}
|
||||
|
||||
auto it_next = it_found;
|
||||
const auto cursor_next = speculum_cursors[next].get();
|
||||
const auto cursor_lowerbound = speculum_cursors[lowerbound].get();
|
||||
if (++it_next != speculum.end()) {
|
||||
speculum_check_cursor("after-remove", "next", it_next, cursor_next,
|
||||
MDBX_GET_CURRENT);
|
||||
speculum_check_cursor("after-remove", "lowerbound", it_next,
|
||||
cursor_lowerbound, MDBX_NEXT);
|
||||
|
||||
auto it_next_next = it_next;
|
||||
const auto cursor_next_next = speculum_cursors[next_next].get();
|
||||
if (++it_next_next != speculum.end()) {
|
||||
speculum_check_cursor("after-remove", "next-next", it_next_next,
|
||||
cursor_next_next, MDBX_GET_CURRENT);
|
||||
} else if ((err = mdbx_cursor_on_last(cursor_next_next)) !=
|
||||
MDBX_RESULT_TRUE)
|
||||
failure("speculum-%s: %s on-last %d %s", "after-remove", "next-next",
|
||||
err, mdbx_strerror(err));
|
||||
} else {
|
||||
if ((err = mdbx_cursor_on_last(cursor_next)) != MDBX_RESULT_TRUE)
|
||||
failure("speculum-%s: %s on-last %d %s", "after-remove", "next", err,
|
||||
mdbx_strerror(err));
|
||||
if ((err = mdbx_cursor_on_last(cursor_lowerbound)) != MDBX_RESULT_TRUE)
|
||||
failure("speculum-%s: %s on-last %d %s", "after-remove", "lowerbound",
|
||||
err, mdbx_strerror(err));
|
||||
}
|
||||
|
||||
speculum.erase(it_found);
|
||||
}
|
||||
}
|
||||
|
||||
return rc ? MDBX_SUCCESS : MDBX_RESULT_TRUE;
|
||||
}
|
||||
|
||||
bool testcase::speculum_verify() {
|
||||
@ -715,8 +1058,8 @@ bool testcase::speculum_verify() {
|
||||
akey.iov_len = avalue.iov_len = 0;
|
||||
akey.iov_base = avalue.iov_base = nullptr;
|
||||
}
|
||||
const auto S_key = S(akey);
|
||||
const auto S_data = S(avalue);
|
||||
const auto S_key = iov2dataview(akey);
|
||||
const auto S_data = iov2dataview(avalue);
|
||||
if (it != speculum.cend()) {
|
||||
mkey.iov_base = (void *)it->first.c_str();
|
||||
mkey.iov_len = it->first.size();
|
||||
|
47
test/test.h
47
test/test.h
@ -103,31 +103,33 @@ protected:
|
||||
#else
|
||||
using data_view = std::string;
|
||||
#endif
|
||||
static inline data_view S(const MDBX_val &v) {
|
||||
static inline data_view iov2dataview(const MDBX_val &v) {
|
||||
return (v.iov_base && v.iov_len)
|
||||
? data_view(static_cast<const char *>(v.iov_base), v.iov_len)
|
||||
: data_view();
|
||||
}
|
||||
static inline data_view S(const keygen::buffer &b) { return S(b->value); }
|
||||
static inline data_view iov2dataview(const keygen::buffer &b) {
|
||||
return iov2dataview(b->value);
|
||||
}
|
||||
|
||||
using Item = std::pair<std::string, std::string>;
|
||||
static MDBX_val dataview2iov(const data_view &v) {
|
||||
MDBX_val r;
|
||||
r.iov_base = (void *)v.data();
|
||||
r.iov_len = v.size();
|
||||
return r;
|
||||
}
|
||||
struct ItemCompare {
|
||||
const testcase *context;
|
||||
ItemCompare(const testcase *owner) : context(owner) {}
|
||||
|
||||
bool operator()(const Item &a, const Item &b) const {
|
||||
MDBX_val va, vb;
|
||||
va.iov_base = (void *)a.first.data();
|
||||
va.iov_len = a.first.size();
|
||||
vb.iov_base = (void *)b.first.data();
|
||||
vb.iov_len = b.first.size();
|
||||
MDBX_val va = dataview2iov(a.first), vb = dataview2iov(b.first);
|
||||
int cmp = mdbx_cmp(context->txn_guard.get(), context->dbi, &va, &vb);
|
||||
if (cmp == 0 &&
|
||||
(context->config.params.table_flags & MDBX_DUPSORT) != 0) {
|
||||
va.iov_base = (void *)a.second.data();
|
||||
va.iov_len = a.second.size();
|
||||
vb.iov_base = (void *)b.second.data();
|
||||
vb.iov_len = b.second.size();
|
||||
va = dataview2iov(a.second);
|
||||
vb = dataview2iov(b.second);
|
||||
cmp = mdbx_dcmp(context->txn_guard.get(), context->dbi, &va, &vb);
|
||||
}
|
||||
return cmp < 0;
|
||||
@ -159,6 +161,29 @@ protected:
|
||||
} last;
|
||||
|
||||
SET speculum{ItemCompare(this)}, speculum_committed{ItemCompare(this)};
|
||||
scoped_cursor_guard speculum_cursors[5];
|
||||
void speculum_prepare_cursors(const Item &item);
|
||||
void speculum_check_iterator(const char *where, const char *stage,
|
||||
const testcase::SET::const_iterator &it,
|
||||
const MDBX_val &key, const MDBX_val &data) const;
|
||||
void speculum_check_cursor(const char *where, const char *stage,
|
||||
const testcase::SET::const_iterator &it,
|
||||
int cursor_err, const MDBX_val &cursor_key,
|
||||
const MDBX_val &cursor_data) const;
|
||||
void speculum_check_cursor(const char *where, const char *stage,
|
||||
const testcase::SET::const_iterator &it,
|
||||
MDBX_cursor *cursor,
|
||||
const MDBX_cursor_op op) const;
|
||||
|
||||
void verbose(const char *where, const char *stage,
|
||||
const testcase::SET::const_iterator &it) const;
|
||||
void verbose(const char *where, const char *stage, const MDBX_val &key,
|
||||
const MDBX_val &data, int err = MDBX_SUCCESS) const;
|
||||
|
||||
bool is_same(const Item &a, const Item &b) const;
|
||||
bool is_same(const SET::const_iterator &it, const MDBX_val &k,
|
||||
const MDBX_val &v) const;
|
||||
|
||||
bool speculum_verify();
|
||||
int insert(const keygen::buffer &akey, const keygen::buffer &adata,
|
||||
MDBX_put_flags_t flags);
|
||||
|
@ -287,6 +287,9 @@ std::string data2hex(const void *ptr, size_t bytes, simple_checksum &checksum);
|
||||
bool hex2data(const char *hex_begin, const char *hex_end, void *ptr,
|
||||
size_t bytes, simple_checksum &checksum);
|
||||
bool is_samedata(const MDBX_val *a, const MDBX_val *b);
|
||||
inline bool is_samedata(const MDBX_val &a, const MDBX_val &b) {
|
||||
return is_samedata(&a, &b);
|
||||
}
|
||||
std::string format(const char *fmt, ...);
|
||||
|
||||
uint64_t entropy_ticks(void);
|
||||
|
Loading…
x
Reference in New Issue
Block a user