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>
87#if __cplusplus >= 201703L
89 using ReturnT = std::conditional_t<
90 std::is_same_v<ContainerT<KeyT, ValueT>, std::vector<std::pair<KeyT, ValueT>>>,
91 std::vector<std::pair<KeyT, ValueT>>,
92 ContainerT<KeyT, ValueT>
102 typename std::conditional<
103 std::is_same<ContainerT<KeyT, ValueT>, std::vector<std::pair<KeyT, ValueT> > >::value,
104 std::vector<std::pair<KeyT, ValueT> >,
105 ContainerT<KeyT, ValueT>
107 typedef typename std::conditional<
108 std::is_same<ContainerT<KeyT, ValueT>, std::vector<std::pair<KeyT, ValueT> > >::value,
109 std::vector<std::pair<KeyT, ValueT> >,
110 ContainerT<KeyT, ValueT>
140 operator ValueT()
const {
141#if __cplusplus >= 201703L
143 if (val)
return *val;
145 std::pair<bool, ValueT> val =
m_db.find(
m_key);
146 if (val.first)
return val.second;
172 template<
template <
class...>
class ContainerT>
173 void load(ContainerT<KeyT, ValueT>& container, MDBX_txn* txn =
nullptr) {
184 template<
template <
class...>
class ContainerT>
193 void load(std::vector<std::pair<KeyT, ValueT>>& container, MDBX_txn* txn =
nullptr) {
212 template<
template<
class...>
class ContainerT = std::map>
213#if __cplusplus >= 201703L
215 using ReturnT = std::conditional_t<
216 std::is_same_v<ContainerT<KeyT, ValueT>, std::vector<std::pair<KeyT, ValueT>>>,
217 std::vector<std::pair<KeyT, ValueT>>,
218 ContainerT<KeyT, ValueT>
233 template<
template<
class...>
class ContainerT = std::map>
238 typename std::conditional<
239 std::is_same<ContainerT<KeyT, ValueT>, std::vector<std::pair<KeyT, ValueT> > >::value,
240 std::vector<std::pair<KeyT, ValueT> >,
241 ContainerT<KeyT, ValueT>
243 typedef typename std::conditional<
244 std::is_same<ContainerT<KeyT, ValueT>, std::vector<std::pair<KeyT, ValueT> > >::value,
245 std::vector<std::pair<KeyT, ValueT> >,
246 ContainerT<KeyT, ValueT>
261 template<
template<
class...>
class ContainerT = std::map>
262 typename std::conditional<
263 std::is_same<ContainerT<KeyT, ValueT>, std::vector<std::pair<KeyT, ValueT> > >::value,
264 std::vector<std::pair<KeyT, ValueT> >,
265 ContainerT<KeyT, ValueT>
276 template<
template <
class...>
class ContainerT>
277 void append(
const ContainerT<KeyT, ValueT>& container, MDBX_txn* txn =
nullptr) {
288 template<
template <
class...>
class ContainerT>
298 void append(
const std::vector<std::pair<KeyT, ValueT>>& container, MDBX_txn* txn =
nullptr) {
317 template<
template <
class...>
class ContainerT>
318 void reconcile(
const ContainerT<KeyT, ValueT>& container, MDBX_txn* txn =
nullptr) {
329 template<
template <
class...>
class ContainerT>
338 void reconcile(
const std::vector<std::pair<KeyT, ValueT>>& container, MDBX_txn* txn =
nullptr) {
358 bool insert(
const KeyT &key,
const ValueT &value, MDBX_txn* txn =
nullptr) {
381 bool insert(
const std::pair<KeyT, ValueT> &pair, MDBX_txn* txn =
nullptr) {
442 ValueT
at(
const KeyT& key, MDBX_txn* txn =
nullptr)
const {
445 if (!
db_get(key, value, txn)) {
446 throw std::out_of_range(
"Key not found in database");
468 bool try_get(
const KeyT& key, ValueT& out, MDBX_txn* txn)
const {
471 res =
db_get(key, out, txn);
486#if __cplusplus >= 201703L
492 std::optional<ValueT>
find(
const KeyT& key, MDBX_txn* txn =
nullptr)
const {
493 std::optional<ValueT> result;
496 if (
db_get(key, tmp, txn)) {
497 result = std::move(tmp);
508 std::optional<ValueT>
find(
const KeyT& key,
const Transaction& txn)
const {
509 return find(key, txn.handle());
517 std::pair<bool, ValueT>
find(
const KeyT& key, MDBX_txn* txn =
nullptr)
const {
518 std::pair<bool, ValueT> result(
false, ValueT());
520 if (
db_get(key, result.second, txn)) {
542 std::pair<bool, ValueT>
find_compat(
const KeyT& key, MDBX_txn* txn =
nullptr)
const {
543 std::pair<bool, ValueT> result{
false, ValueT{}};
545 if (
db_get(key, result.second, txn)) {
566 bool contains(
const KeyT& key, MDBX_txn* txn =
nullptr)
const {
587 std::size_t
count(MDBX_txn* txn =
nullptr)
const {
607 bool empty(MDBX_txn* txn =
nullptr)
const {
628 bool erase(
const KeyT &key, MDBX_txn* txn =
nullptr) {
648 void clear(MDBX_txn* txn =
nullptr) {
682 action(txn_guard.handle());
687 txn_guard.rollback();
700 template<
template <
class...>
class ContainerT>
701 void db_load(ContainerT<KeyT, ValueT>& container, MDBX_txn* txn_handle) {
702 MDBX_cursor* cursor =
nullptr;
703 check_mdbx(mdbx_cursor_open(txn_handle,
m_dbi, &cursor),
"Failed to open MDBX cursor");
705 MDBX_val db_key, db_val;
706 while (mdbx_cursor_get(cursor, &db_key, &db_val, MDBX_NEXT) == MDBX_SUCCESS) {
709 container.emplace(std::move(key), std::move(value));
712 mdbx_cursor_close(cursor);
719 void db_load(std::vector<std::pair<KeyT, ValueT>>& out_vector, MDBX_txn* txn) {
720 MDBX_cursor* cursor =
nullptr;
721 check_mdbx(mdbx_cursor_open(txn,
m_dbi, &cursor),
"Failed to open MDBX cursor");
723 MDBX_val db_key, db_val;
724 while (mdbx_cursor_get(cursor, &db_key, &db_val, MDBX_NEXT) == MDBX_SUCCESS) {
727 out_vector.emplace_back(std::move(key), std::move(value));
730 mdbx_cursor_close(cursor);
739 bool db_get(
const KeyT& key, ValueT& value, MDBX_txn* txn_handle)
const {
743 int rc = mdbx_get(txn_handle,
m_dbi, &db_key, &db_val);
744 if (rc == MDBX_NOTFOUND)
return false;
759 int rc = mdbx_get(txn_handle,
m_dbi, &db_key, &db_val);
760 if (rc == MDBX_NOTFOUND)
return false;
761 check_mdbx(rc,
"Failed to check key presence");
771 check_mdbx(mdbx_dbi_stat(txn_handle,
m_dbi, &stat,
sizeof(stat)),
"Failed to query database statistics");
772 return stat.ms_entries;
780 template<
template <
class...>
class ContainerT>
781 void db_append(
const ContainerT<KeyT, ValueT>& container, MDBX_txn* txn_handle) {
785 for (
const auto& pair : container) {
789 mdbx_put(txn_handle,
m_dbi, &db_key, &db_val, MDBX_UPSERT),
790 "Failed to write record"
799 void db_append(
const std::vector<std::pair<KeyT, ValueT>>& container, MDBX_txn* txn_handle) {
803#if __cplusplus >= 201703L
804 for (
const auto& [key, value] : container) {
808 mdbx_put(txn_handle,
m_dbi, &db_key, &db_val, MDBX_UPSERT),
809 "Failed to write record"
813 for (
typename std::vector<std::pair<KeyT, ValueT> >::const_iterator it = container.begin();
814 it != container.end(); ++it) {
815 const KeyT& key = it->first;
816 const ValueT& value = it->second;
820 mdbx_put(txn_handle,
m_dbi, &db_key, &db_val, MDBX_UPSERT),
821 "Failed to write record"
834 template<
template <
class...>
class ContainerT>
835 void db_reconcile(
const ContainerT<KeyT, ValueT>& container, MDBX_txn* txn_handle) {
840 std::unordered_set<KeyT> new_keys;
841 for (
const auto& pair : container) {
842 new_keys.insert(pair.first);
846 mdbx_put(txn_handle,
m_dbi, &db_key, &db_val, MDBX_UPSERT),
847 "Failed to write record"
852 MDBX_cursor* cursor =
nullptr;
853 check_mdbx(mdbx_cursor_open(txn_handle,
m_dbi, &cursor),
"Failed to open MDBX cursor");
855 MDBX_val db_key, db_val;
856 while (mdbx_cursor_get(cursor, &db_key, &db_val, MDBX_NEXT) == MDBX_SUCCESS) {
858 if (new_keys.find(key) == new_keys.end()) {
859 check_mdbx(mdbx_cursor_del(cursor, MDBX_CURRENT),
"Failed to delete record using cursor");
863 mdbx_cursor_close(cursor);
871 void db_reconcile(
const std::vector<std::pair<KeyT, ValueT>>& container, MDBX_txn* txn_handle) {
876 std::unordered_set<KeyT> new_keys;
877 for (
size_t i = 0; i < container.size(); ++i) {
878 const KeyT& key = container[i].first;
879 const ValueT& value = container[i].second;
881 new_keys.insert(key);
886 mdbx_put(txn_handle,
m_dbi, &db_key, &db_val, MDBX_UPSERT),
887 "Failed to write record"
892 MDBX_cursor* cursor =
nullptr;
893 check_mdbx(mdbx_cursor_open(txn_handle,
m_dbi, &cursor),
"Failed to open MDBX cursor");
895 MDBX_val db_key, db_val;
896 while (mdbx_cursor_get(cursor, &db_key, &db_val, MDBX_NEXT) == MDBX_SUCCESS) {
898 if (new_keys.find(key) == new_keys.end()) {
899 check_mdbx(mdbx_cursor_del(cursor, MDBX_CURRENT),
"Failed to delete record using cursor");
903 mdbx_cursor_close(cursor);
917 int rc = mdbx_put(txn_handle,
m_dbi, &db_key, &db_val, MDBX_NOOVERWRITE);
919 if (rc == MDBX_SUCCESS)
921 if (rc == MDBX_KEYEXIST)
924 check_mdbx(rc,
"Failed to insert key-value pair");
939 mdbx_put(txn_handle,
m_dbi, &db_key, &db_val, MDBX_UPSERT),
940 "Failed to insert or assign key-value pair"
949 bool db_erase(
const KeyT& key, MDBX_txn* txn_handle) {
952 int rc = mdbx_del(txn_handle,
m_dbi, &db_key,
nullptr);
953 if (rc == MDBX_SUCCESS)
return true;
954 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.
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.
std::pair< bool, ValueT > find(const KeyT &key, MDBX_txn *txn=nullptr) const
Finds value by key.
void db_append(const std::vector< std::pair< KeyT, ValueT > > &container, MDBX_txn *txn_handle)
Appends the content of the vector to the database.
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::pair< bool, ValueT > find(const KeyT &key, const Transaction &txn) const
Finds value by key.
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.
std::conditional< std::is_same< ContainerT< KeyT, ValueT >, std::vector< std::pair< KeyT, ValueT > > >::value, std::vector< std::pair< KeyT, ValueT > >, ContainerT< KeyT, ValueT > >::type retrieve_all(const Transaction &txn)
Retrieves all key-value pairs into the specified container type.
std::conditional< std::is_same< ContainerT< KeyT, ValueT >, std::vector< std::pair< KeyT, ValueT > > >::value, std::vector< std::pair< KeyT, ValueT > >, ContainerT< KeyT, ValueT > >::type operator()()
Loads all key-value pairs from the database into a container.
std::conditional< std::is_same< ContainerT< KeyT, ValueT >, std::vector< std::pair< KeyT, ValueT > > >::value, std::vector< std::pair< KeyT, ValueT > >, ContainerT< KeyT, ValueT > >::type retrieve_all(MDBX_txn *txn=nullptr)
Retrieves all key-value pairs into the specified container type.
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.
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, SerializeScratch &sc)
Serializes a general value into MDBX_val.
std::enable_if<!has_value_type< T >::value &&!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.
MDBX_db_flags_t get_mdbx_flags()
Returns MDBX flags for a given key type.
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, SerializeScratch &sc)
Serializes a key into MDBX_val for database operations.
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).
Per-call scratch buffer to produce MDBX_val without using thread_local.