2#ifndef _MDBX_CONTAINERS_KEY_VALUE_TABLE_HPP_INCLUDED
3#define _MDBX_CONTAINERS_KEY_VALUE_TABLE_HPP_INCLUDED
10#include <unordered_set>
26 template<
class KeyT,
class ValueT>
35 std::string name =
"kv_store",
36 MDBX_db_flags_t flags = MDBX_DB_DEFAULTS | MDBX_CREATE)
46 std::string name =
"kv_store",
47 MDBX_db_flags_t flags = MDBX_DB_DEFAULTS | MDBX_CREATE)
62 template<template <class...> class ContainerT>
86 template<
template<
class...>
class ContainerT = std::map>
88 using ReturnT = std::conditional_t<
89 std::is_same_v<ContainerT<KeyT, ValueT>, std::vector<std::pair<KeyT, ValueT>>>,
90 std::vector<std::pair<KeyT, ValueT>>,
91 ContainerT<KeyT, ValueT>
120 operator ValueT()
const {
122 if (val)
return *val;
147 template<
template <
class...>
class ContainerT>
148 void load(ContainerT<KeyT, ValueT>& container, MDBX_txn* txn =
nullptr) {
159 template<
template <
class...>
class ContainerT>
168 void load(std::vector<std::pair<KeyT, ValueT>>& container, MDBX_txn* txn =
nullptr) {
187 template<
template<
class...>
class ContainerT = std::map>
189 using ReturnT = std::conditional_t<
190 std::is_same_v<ContainerT<KeyT, ValueT>, std::vector<std::pair<KeyT, ValueT>>>,
191 std::vector<std::pair<KeyT, ValueT>>,
192 ContainerT<KeyT, ValueT>
207 template<
template<
class...>
class ContainerT = std::map>
217 template<
template <
class...>
class ContainerT>
218 void append(
const ContainerT<KeyT, ValueT>& container, MDBX_txn* txn =
nullptr) {
229 template<
template <
class...>
class ContainerT>
239 void append(
const std::vector<std::pair<KeyT, ValueT>>& container, MDBX_txn* txn =
nullptr) {
258 template<
template <
class...>
class ContainerT>
259 void reconcile(
const ContainerT<KeyT, ValueT>& container, MDBX_txn* txn =
nullptr) {
270 template<
template <
class...>
class ContainerT>
279 void reconcile(
const std::vector<std::pair<KeyT, ValueT>>& container, MDBX_txn* txn =
nullptr) {
299 bool insert(
const KeyT &key,
const ValueT &value, MDBX_txn* txn =
nullptr) {
322 bool insert(
const std::pair<KeyT, ValueT> &pair, MDBX_txn* txn =
nullptr) {
383 ValueT
at(
const KeyT& key, MDBX_txn* txn =
nullptr)
const {
386 if (!
db_get(key, value, txn)) {
387 throw std::out_of_range(
"Key not found in database");
409 bool try_get(
const KeyT& key, ValueT& out, MDBX_txn* txn)
const {
412 res =
db_get(key, out, txn);
427#if __cplusplus >= 201703L
433 std::optional<ValueT> find(
const KeyT& key, MDBX_txn* txn =
nullptr)
const {
434 std::optional<ValueT> result;
437 if (
db_get(key, tmp, txn)) {
438 result = std::move(tmp);
449 std::optional<ValueT> find(
const KeyT& key,
const Transaction& txn)
const {
450 return find(key, txn.handle());
459 std::pair<bool, ValueT>
find_compat(
const KeyT& key, MDBX_txn* txn =
nullptr)
const {
460 std::pair<bool, ValueT> result{
false, ValueT{}};
462 if (
db_get(key, result.second, txn)) {
483 bool contains(
const KeyT& key, MDBX_txn* txn =
nullptr)
const {
504 std::size_t
count(MDBX_txn* txn =
nullptr)
const {
524 bool empty(MDBX_txn* txn =
nullptr)
const {
545 bool erase(
const KeyT &key, MDBX_txn* txn =
nullptr) {
565 void clear(MDBX_txn* txn =
nullptr) {
599 action(txn_guard.handle());
604 txn_guard.rollback();
617 template<
template <
class...>
class ContainerT>
618 void db_load(ContainerT<KeyT, ValueT>& container, MDBX_txn* txn_handle) {
619 MDBX_cursor* cursor =
nullptr;
620 check_mdbx(mdbx_cursor_open(txn_handle,
m_dbi, &cursor),
"Failed to open MDBX cursor");
622 MDBX_val db_key, db_val;
623 while (mdbx_cursor_get(cursor, &db_key, &db_val, MDBX_NEXT) == MDBX_SUCCESS) {
626 container.emplace(std::move(key), std::move(value));
629 mdbx_cursor_close(cursor);
636 void db_load(std::vector<std::pair<KeyT, ValueT>>& out_vector, MDBX_txn* txn) {
637 MDBX_cursor* cursor =
nullptr;
638 check_mdbx(mdbx_cursor_open(txn,
m_dbi, &cursor),
"Failed to open MDBX cursor");
640 MDBX_val db_key, db_val;
641 while (mdbx_cursor_get(cursor, &db_key, &db_val, MDBX_NEXT) == MDBX_SUCCESS) {
644 out_vector.emplace_back(std::move(key), std::move(value));
647 mdbx_cursor_close(cursor);
656 bool db_get(
const KeyT& key, ValueT& value, MDBX_txn* txn_handle)
const {
659 int rc = mdbx_get(txn_handle,
m_dbi, &db_key, &db_val);
660 if (rc == MDBX_NOTFOUND)
return false;
674 int rc = mdbx_get(txn_handle,
m_dbi, &db_key, &db_val);
675 if (rc == MDBX_NOTFOUND)
return false;
676 check_mdbx(rc,
"Failed to check key presence");
686 check_mdbx(mdbx_dbi_stat(txn_handle,
m_dbi, &stat,
sizeof(stat)),
"Failed to query database statistics");
687 return stat.ms_entries;
695 template<
template <
class...>
class ContainerT>
696 void db_append(
const ContainerT<KeyT, ValueT>& container, MDBX_txn* txn_handle) {
697 for (
const auto& pair : container) {
701 mdbx_put(txn_handle,
m_dbi, &db_key, &db_val, MDBX_UPSERT),
702 "Failed to write record"
711 void db_append(
const std::vector<std::pair<KeyT, ValueT>>& container, MDBX_txn* txn_handle) {
712 for (
const auto& [key, value] : container) {
716 mdbx_put(txn_handle,
m_dbi, &db_key, &db_val, MDBX_UPSERT),
717 "Failed to write record"
729 template<
template <
class...>
class ContainerT>
730 void db_reconcile(
const ContainerT<KeyT, ValueT>& container, MDBX_txn* txn_handle) {
732 std::unordered_set<KeyT> new_keys;
733 for (
const auto& pair : container) {
734 new_keys.insert(pair.first);
738 mdbx_put(txn_handle,
m_dbi, &db_key, &db_val, MDBX_UPSERT),
739 "Failed to write record"
744 MDBX_cursor* cursor =
nullptr;
745 check_mdbx(mdbx_cursor_open(txn_handle,
m_dbi, &cursor),
"Failed to open MDBX cursor");
747 MDBX_val db_key, db_val;
748 while (mdbx_cursor_get(cursor, &db_key, &db_val, MDBX_NEXT) == MDBX_SUCCESS) {
750 if (new_keys.find(key) == new_keys.end()) {
751 check_mdbx(mdbx_cursor_del(cursor, MDBX_CURRENT),
"Failed to delete record using cursor");
755 mdbx_cursor_close(cursor);
763 void db_reconcile(
const std::vector<std::pair<KeyT, ValueT>>& container, MDBX_txn* txn_handle) {
765 std::unordered_set<KeyT> new_keys;
766 for (
size_t i = 0; i < container.size(); ++i) {
767 const KeyT& key = container[i].first;
768 const ValueT& value = container[i].second;
770 new_keys.insert(key);
775 mdbx_put(txn_handle,
m_dbi, &db_key, &db_val, MDBX_UPSERT),
776 "Failed to write record"
781 MDBX_cursor* cursor =
nullptr;
782 check_mdbx(mdbx_cursor_open(txn_handle,
m_dbi, &cursor),
"Failed to open MDBX cursor");
784 MDBX_val db_key, db_val;
785 while (mdbx_cursor_get(cursor, &db_key, &db_val, MDBX_NEXT) == MDBX_SUCCESS) {
787 if (new_keys.find(key) == new_keys.end()) {
788 check_mdbx(mdbx_cursor_del(cursor, MDBX_CURRENT),
"Failed to delete record using cursor");
792 mdbx_cursor_close(cursor);
804 int rc = mdbx_put(txn_handle,
m_dbi, &db_key, &db_val, MDBX_NOOVERWRITE);
806 if (rc == MDBX_SUCCESS)
808 if (rc == MDBX_KEYEXIST)
811 check_mdbx(rc,
"Failed to insert key-value pair");
824 mdbx_put(txn_handle,
m_dbi, &db_key, &db_val, MDBX_UPSERT),
825 "Failed to insert or assign key-value pair"
834 bool db_erase(
const KeyT& key, MDBX_txn* txn_handle) {
836 int rc = mdbx_del(txn_handle,
m_dbi, &db_key,
nullptr);
837 if (rc == MDBX_SUCCESS)
return true;
838 if (rc == MDBX_NOTFOUND)
return false;
MDBX_txn * thread_txn() const
Returns the transaction bound to the current thread, if any.
MDBX_dbi m_dbi
DBI handle for the opened table.
BaseTable(std::shared_ptr< Connection > connection, std::string name, MDBX_db_flags_t flags)
Construct the database table accessor.
std::shared_ptr< Connection > m_connection
Shared connection to MDBX environment.
Parameters used by Connection to create the MDBX environment.
Manages a single MDBX environment and an optional read-only transaction.
Helper proxy for convenient assignment via operator[].
AssignmentProxy(KeyValueTable &db, KeyT key)
Constructs the proxy for a specific key.
KeyValueTable & m_db
Reference to the owning table.
KeyT m_key
Key associated with this proxy.
AssignmentProxy & operator=(const ValueT &value)
Assigns a value to the stored key.
auto operator()()
Loads all key-value pairs from the database into a container.
void load(ContainerT< KeyT, ValueT > &container, const Transaction &txn)
Loads data from the database into the container using provided transaction.
void db_reconcile(const std::vector< std::pair< KeyT, ValueT > > &container, MDBX_txn *txn_handle)
Reconciles the content of the database with the given vector of key-value pairs.
bool db_insert_if_absent(const KeyT &key, const ValueT &value, MDBX_txn *txn_handle)
Inserts a key-value pair only if the key does not already exist.
std::size_t count(MDBX_txn *txn=nullptr) const
Returns the number of elements in the database.
std::size_t db_count(MDBX_txn *txn_handle) const
Returns the number of elements in the database.
void load(ContainerT< KeyT, ValueT > &container, MDBX_txn *txn=nullptr)
Loads data from the database into the container.
bool insert(const KeyT &key, const ValueT &value, MDBX_txn *txn=nullptr)
Inserts key-value only if key is absent.
bool empty(MDBX_txn *txn=nullptr) const
Checks if the database is empty.
void insert_or_assign(const KeyT &key, const ValueT &value, MDBX_txn *txn=nullptr)
Inserts or replaces key-value pair.
void load(std::vector< std::pair< KeyT, ValueT > > &container, MDBX_txn *txn=nullptr)
Loads all key-value pairs into a std::vector of pairs.
bool contains(const KeyT &key, const Transaction &txn) const
Checks whether a key exists in the database.
KeyValueTable & operator=(const std::vector< std::pair< KeyT, ValueT > > &container)
Assigns a vector of key-value pairs to the database.
void reconcile(const std::vector< std::pair< KeyT, ValueT > > &container, MDBX_txn *txn=nullptr)
Reconciles the database with the vector of key-value pairs.
bool try_get(const KeyT &key, ValueT &out, const Transaction &txn) const
Tries to find value by key.
void insert_or_assign(const std::pair< KeyT, ValueT > &pair, MDBX_txn *txn=nullptr)
Inserts or replaces key-value pair.
bool insert(const KeyT &key, const ValueT &value, const Transaction &txn)
Inserts key-value only if key is absent.
bool db_erase(const KeyT &key, MDBX_txn *txn_handle)
Removes a key from the database.
bool try_get(const KeyT &key, ValueT &out, MDBX_txn *txn) const
Tries to find value by key.
void db_append(const ContainerT< KeyT, ValueT > &container, MDBX_txn *txn_handle)
Appends the content of the container to the database.
void insert_or_assign(const std::pair< KeyT, ValueT > &pair, const Transaction &txn)
Inserts or replaces key-value pair.
AssignmentProxy operator[](const KeyT &key)
Provides convenient access to insert or read a value by key.
std::pair< bool, ValueT > find_compat(const KeyT &key, MDBX_txn *txn=nullptr) const
Finds value by key.
void db_load(std::vector< std::pair< KeyT, ValueT > > &out_vector, MDBX_txn *txn)
Loads all key-value pairs into a std::vector of pairs.
KeyValueTable(const Config &config, std::string name="kv_store", MDBX_db_flags_t flags=MDBX_DB_DEFAULTS|MDBX_CREATE)
Constructor with configuration.
bool contains(const KeyT &key, MDBX_txn *txn=nullptr) const
Checks whether a key exists in the database.
void db_load(ContainerT< KeyT, ValueT > &container, MDBX_txn *txn_handle)
Loads data from the database into the container.
void reconcile(const std::vector< std::pair< KeyT, ValueT > > &container, const Transaction &txn)
Reconciles the database with the vector of key-value pairs using provided transaction.
bool empty(const Transaction &txn) const
Checks if the database is empty.
void db_clear(MDBX_txn *txn_handle)
Clears all key-value pairs from the database.
void db_insert_or_assign(const KeyT &key, const ValueT &value, MDBX_txn *txn_handle)
Inserts or replaces the key-value pair.
void db_append(const std::vector< std::pair< KeyT, ValueT > > &container, MDBX_txn *txn_handle)
Appends the content of the vector to the database.
auto retrieve_all(const Transaction &txn)
Retrieves all key-value pairs into the specified container type.
void clear(const Transaction &txn)
Clears all key-value pairs from the database.
void append(const ContainerT< KeyT, ValueT > &container, const Transaction &txn)
Appends data to the database.
ValueT at(const KeyT &key, const Transaction &txn) const
Retrieves value by key or throws.
void reconcile(const ContainerT< KeyT, ValueT > &container, MDBX_txn *txn=nullptr)
Reconciles the database with the container.
bool erase(const KeyT &key, const Transaction &txn)
Removes key from DB.
std::size_t count(const Transaction &txn) const
Returns the number of elements in the database.
void clear(MDBX_txn *txn=nullptr)
Clears all key-value pairs from the database.
void load(std::vector< std::pair< KeyT, ValueT > > &container, const Transaction &txn)
Loads all key-value pairs into a std::vector of pairs.
void db_reconcile(const ContainerT< KeyT, ValueT > &container, MDBX_txn *txn_handle)
Reconciles the content of the database with the container.
ValueT at(const KeyT &key, MDBX_txn *txn=nullptr) const
Retrieves value by key or throws.
void reconcile(const ContainerT< KeyT, ValueT > &container, const Transaction &txn)
Reconciles the database with the container.
bool db_contains(const KeyT &key, MDBX_txn *txn_handle) const
Checks whether a key exists in the database.
std::pair< bool, ValueT > find_compat(const KeyT &key, const Transaction &txn) const
Finds value by key.
bool insert(const std::pair< KeyT, ValueT > &pair, const Transaction &txn)
Inserts key-value only if key is absent.
KeyValueTable(std::shared_ptr< Connection > connection, std::string name="kv_store", MDBX_db_flags_t flags=MDBX_DB_DEFAULTS|MDBX_CREATE)
Default constructor.
void append(const std::vector< std::pair< KeyT, ValueT > > &container, MDBX_txn *txn=nullptr)
Appends data from a vector to the database.
bool erase(const KeyT &key, MDBX_txn *txn=nullptr)
Removes key from DB.
~KeyValueTable() override final=default
Destructor.
void insert_or_assign(const KeyT &key, const ValueT &value, const Transaction &txn)
Inserts or replaces key-value pair.
bool db_get(const KeyT &key, ValueT &value, MDBX_txn *txn_handle) const
Gets a value by key from the database.
void append(const ContainerT< KeyT, ValueT > &container, MDBX_txn *txn=nullptr)
Appends data to the database.
auto retrieve_all(MDBX_txn *txn=nullptr)
Retrieves all key-value pairs into the specified container type.
void append(const std::vector< std::pair< KeyT, ValueT > > &container, const Transaction &txn)
Appends data from a vector to the database using a provided transaction.
void with_transaction(F &&action, TransactionMode mode=TransactionMode::WRITABLE, MDBX_txn *txn=nullptr) const
Executes a functor within a transaction context.
bool insert(const std::pair< KeyT, ValueT > &pair, MDBX_txn *txn=nullptr)
Inserts key-value only if key is absent.
Manages MDBX transactions with automatic cleanup and error handling.
MDBX_txn * handle() const noexcept
Returns the internal MDBX transaction handle.
Publicly usable low-level components of the MDBX Containers library.
std::enable_if<!has_value_type< T >::value &&!std::is_same< T, std::vector< typenameT::value_type > >::value &&!std::is_trivially_copyable< typenameT::value_type >::value &&!has_to_bytes< T >::value &&!std::is_same< T, std::string >::value &&!std::is_trivially_copyable< T >::value, MDBX_val >::type serialize_value(const T &value)
Serializes a general value into MDBX_val.
std::enable_if<!has_from_bytes< T >::value &&!std::is_same< T, std::string >::value &&!std::is_trivially_copyable< T >::value, T >::type deserialize_value(const MDBX_val &val)
Deserializes a value from MDBX_val into type T.
std::enable_if<!has_to_bytes< T >::value &&!std::is_same< T, std::string >::value &&!std::is_trivially_copyable< T >::value, MDBX_val >::type serialize_key(const T &key)
Serializes a key into MDBX_val for database operations.
MDBX_db_flags_t get_mdbx_flags()
Returns MDBX flags for a given key type.
void check_mdbx(int rc, const std::string &context)
Throws an MdbxException if MDBX return code indicates an error.
TransactionMode
Specifies the access mode of a transaction.
@ READ_ONLY
Read-only transaction (no write operations allowed).
@ WRITABLE
Writable transaction (allows inserts, updates, deletes).