/// \author Леонид Юрьев aka Leonid Yuriev \date 2015-2024 /// \copyright SPDX-License-Identifier: Apache-2.0 #include "mdbx.h++" #include #include #include #include static ::std::ostream &operator<<(::std::ostream &out, const mdbx::cursor::move_operation op) { static const char *const str[] = {"FIRST", "FIRST_DUP", "GET_BOTH", "GET_BOTH_RANGE", "GET_CURRENT", "GET_MULTIPLE", "LAST", "LAST_DUP", "NEXT", "NEXT_DUP", "NEXT_MULTIPLE", "NEXT_NODUP", "PREV", "PREV_DUP", "PREV_NODUP", "SET", "SET_KEY", "SET_RANGE", "PREV_MULTIPLE", "SET_LOWERBOUND", "SET_UPPERBOUND", "TO_KEY_LESSER_THAN", "TO_KEY_LESSER_OR_EQUAL", "TO_KEY_EQUAL", "TO_KEY_GREATER_OR_EQUAL", "TO_KEY_GREATER_THAN", "TO_EXACT_KEY_VALUE_LESSER_THAN", "TO_EXACT_KEY_VALUE_LESSER_OR_EQUAL", "TO_EXACT_KEY_VALUE_EQUAL", "TO_EXACT_KEY_VALUE_GREATER_OR_EQUAL", "TO_EXACT_KEY_VALUE_GREATER_THAN", "TO_PAIR_LESSER_THAN", "TO_PAIR_LESSER_OR_EQUAL", "TO_PAIR_EQUAL", "TO_PAIR_GREATER_OR_EQUAL", "TO_PAIR_GREATER_THAN"}; return out << str[op]; } using buffer = mdbx::default_buffer; using buffer_pair = mdbx::buffer_pair; std::default_random_engine prng(42); static buffer random(const unsigned &value) { switch (prng() % 3) { default: return buffer::hex(value); case 1: return buffer::base64(value); case 2: return buffer::base58(value); } } static buffer random_key() { return random(prng() % 10007); } static buffer random_value() { return random(prng() % 47); } using predicate = std::function; static bool probe(mdbx::txn txn, mdbx::map_handle dbi, mdbx::cursor::move_operation op, predicate cmp, const buffer_pair &pair) { auto seeker = txn.open_cursor(dbi); auto scanner = seeker.clone(); const bool scan_backward = op == mdbx::cursor::key_lesser_than || op == mdbx::cursor::key_lesser_or_equal || op == mdbx::cursor::multi_exactkey_value_lesser_than || op == mdbx::cursor::multi_exactkey_value_lesser_or_equal || op == mdbx::cursor::pair_lesser_than || op == mdbx::cursor::pair_lesser_or_equal; const bool is_multi = mdbx::is_multi(txn.get_handle_info(dbi).value_mode()); auto seek_result = seeker.move(op, pair.key, pair.value, false); auto scan_result = scanner.fullscan( [cmp, &pair](const mdbx::pair &scan) -> bool { return cmp(scan, pair); }, scan_backward); if (seek_result.done == scan_result && (!scan_result || seeker.is_same_position( scanner, op < mdbx::cursor::multi_exactkey_value_lesser_than && is_multi))) return true; std::cerr << std::endl; std::cerr << "bug:"; std::cerr << std::endl; std::cerr << std::string(is_multi ? "multi" : "single") << "-map, op " << op << ", key " << pair.key << ", value " << pair.value; std::cerr << std::endl; std::cerr << "\tscanner: "; if (scan_result) std::cerr << " done, key " << scanner.current(false).key << ", value " << scanner.current(false).value; else std::cerr << "not-found"; std::cerr << std::endl; std::cerr << "\t seeker: " << (seek_result.done ? " done" : "not-found") << ", key " << seek_result.key << ", value " << seek_result.value; std::cerr << std::endl; return false; } static bool probe(mdbx::txn txn, mdbx::map_handle dbi, mdbx::cursor::move_operation op, predicate cmp) { const auto pair = buffer_pair(random_key(), random_value()); const bool ok = probe(txn, dbi, op, cmp, pair); #if MDBX_DEBUG if (!ok) // повтор для отладки и поиска причин probe(txn, dbi, op, cmp, pair); #endif /* MDBX_DEBUG */ return ok; } static bool test(mdbx::txn txn, mdbx::map_handle dbi) { bool ok = true; ok = probe(txn, dbi, mdbx::cursor::key_lesser_than, [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool { return mdbx_cmp(txn, dbi, l.key, r.key) < 0; }) && ok; ok = probe(txn, dbi, mdbx::cursor::key_lesser_or_equal, [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool { return mdbx_cmp(txn, dbi, l.key, r.key) <= 0; }) && ok; ok = probe(txn, dbi, mdbx::cursor::key_equal, [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool { return mdbx_cmp(txn, dbi, l.key, r.key) == 0; }) && ok; ok = probe(txn, dbi, mdbx::cursor::key_greater_or_equal, [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool { return mdbx_cmp(txn, dbi, l.key, r.key) >= 0; }) && ok; ok = probe(txn, dbi, mdbx::cursor::key_greater_than, [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool { return mdbx_cmp(txn, dbi, l.key, r.key) > 0; }) && ok; ok = probe(txn, dbi, mdbx::cursor::multi_exactkey_value_lesser_than, [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool { return mdbx_cmp(txn, dbi, l.key, r.key) == 0 && mdbx_dcmp(txn, dbi, l.value, r.value) < 0; }) && ok; ok = probe(txn, dbi, mdbx::cursor::multi_exactkey_value_lesser_or_equal, [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool { return mdbx_cmp(txn, dbi, l.key, r.key) == 0 && mdbx_dcmp(txn, dbi, l.value, r.value) <= 0; }) && ok; ok = probe(txn, dbi, mdbx::cursor::multi_exactkey_value_equal, [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool { return mdbx_cmp(txn, dbi, l.key, r.key) == 0 && mdbx_dcmp(txn, dbi, l.value, r.value) == 0; }) && ok; ok = probe(txn, dbi, mdbx::cursor::multi_exactkey_value_greater_or_equal, [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool { return mdbx_cmp(txn, dbi, l.key, r.key) == 0 && mdbx_dcmp(txn, dbi, l.value, r.value) >= 0; }) && ok; ok = probe(txn, dbi, mdbx::cursor::multi_exactkey_value_greater, [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool { return mdbx_cmp(txn, dbi, l.key, r.key) == 0 && mdbx_dcmp(txn, dbi, l.value, r.value) > 0; }) && ok; ok = probe(txn, dbi, mdbx::cursor::pair_lesser_than, [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool { auto cmp = mdbx_cmp(txn, dbi, l.key, r.key); if (cmp == 0) cmp = mdbx_dcmp(txn, dbi, l.value, r.value); return cmp < 0; }) && ok; ok = probe(txn, dbi, mdbx::cursor::pair_lesser_or_equal, [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool { auto cmp = mdbx_cmp(txn, dbi, l.key, r.key); if (cmp == 0) cmp = mdbx_dcmp(txn, dbi, l.value, r.value); return cmp <= 0; }) && ok; ok = probe(txn, dbi, mdbx::cursor::pair_equal, [](const mdbx::pair &l, const mdbx::pair &r) -> bool { return l == r; }) && ok; ok = probe(txn, dbi, mdbx::cursor::pair_greater_or_equal, [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool { auto cmp = mdbx_cmp(txn, dbi, l.key, r.key); if (cmp == 0) cmp = mdbx_dcmp(txn, dbi, l.value, r.value); return cmp >= 0; }) && ok; ok = probe(txn, dbi, mdbx::cursor::pair_greater_than, [txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool { auto cmp = mdbx_cmp(txn, dbi, l.key, r.key); if (cmp == 0) cmp = mdbx_dcmp(txn, dbi, l.value, r.value); return cmp > 0; }) && ok; return ok; } int main(int argc, const char *argv[]) { (void)argc; (void)argv; mdbx::path db_filename = "test-posi"; mdbx::env_managed::remove(db_filename); mdbx::env_managed env(db_filename, mdbx::env_managed::create_parameters(), mdbx::env::operate_parameters(3)); auto txn = env.start_write(); auto single = txn.create_map("single", mdbx::key_mode::usual, mdbx::value_mode::single); auto multi = txn.create_map("multi", mdbx::key_mode::usual, mdbx::value_mode::multi); for (size_t i = 0; i < 1000; ++i) { auto key = random_key(); txn.upsert(single, key, random_value()); for (auto n = prng() % 5 + 1; n > 0; --n) txn.upsert(multi, key, random_value()); } txn.commit_embark_read(); bool ok = true; for (size_t i = 0; ok && i < 3333; ++i) { ok = test(txn, single) && ok; ok = test(txn, multi) && ok; } if (!ok) { std::cerr << "Fail\n"; return EXIT_FAILURE; } std::cout << "OK\n"; return EXIT_SUCCESS; }