MDBX Containers
Loading...
Searching...
No Matches
AnyValueTable.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef _MDBX_CONTAINERS_ANY_VALUE_TABLE_HPP_INCLUDED
3#define _MDBX_CONTAINERS_ANY_VALUE_TABLE_HPP_INCLUDED
4
7
8#include "common.hpp"
9
10namespace mdbxc {
11
16 template <class KeyT>
17 class AnyValueTable final : public BaseTable {
18 public:
23 AnyValueTable(std::shared_ptr<Connection> conn,
24 std::string name = "any_store",
25 MDBX_db_flags_t flags = MDBX_DB_DEFAULTS | MDBX_CREATE)
26 : BaseTable(std::move(conn), std::move(name), flags | get_mdbx_flags<KeyT>()) {}
27
32 explicit AnyValueTable(const Config& cfg,
33 std::string name = "any_store",
34 MDBX_db_flags_t flags = MDBX_DB_DEFAULTS | MDBX_CREATE)
35 : BaseTable(Connection::create(cfg), std::move(name), flags | get_mdbx_flags<KeyT>()) {}
36
38 ~AnyValueTable() override = default;
39
40 // --- Write ---
41
47 template <class T>
48 void set(const KeyT& key, const T& value, MDBX_txn* txn = nullptr) {
49 with_transaction([&](MDBX_txn* t){
50 put_typed(key, value, true, t);
52 }
53
55 template <class T>
56 void set(const KeyT& key, const T& value, const Transaction& txn) {
57 set(key, value, txn.handle());
58 }
59
62 template <class T>
63 bool insert(const KeyT& key, const T& value, MDBX_txn* txn = nullptr) {
64 bool res = false;
65 with_transaction([&](MDBX_txn* t){
66 res = put_typed(key, value, false, t);
68 return res;
69 }
70
72 template <class T>
73 bool insert(const KeyT& key, const T& value, const Transaction& txn) {
74 return insert<T>(key, value, txn.handle());
75 }
76
84 template <class T, class Fn>
85 void update(const KeyT& key, Fn&& fn, bool create_if_missing = false, MDBX_txn* txn = nullptr) {
86 with_transaction([&](MDBX_txn* t){
87 T tmp{};
88 bool exists = get_typed(key, tmp, t);
89 if (!exists) {
90 if (!create_if_missing) {
91 throw std::out_of_range("Key not found");
92 }
93 }
94 fn(tmp);
95 put_typed(key, tmp, true, t);
97 }
98
100 template <class T, class Fn>
101 void update(const KeyT& key, Fn&& fn, bool create_if_missing, const Transaction& txn) {
102 update<T, Fn>(key, std::forward<Fn>(fn), create_if_missing, txn.handle());
103 }
104
105 // --- Read ---
106
112 template <class T>
113 T get(const KeyT& key, MDBX_txn* txn = nullptr) const {
114 T out{};
115 bool found = false;
116 with_transaction([&](MDBX_txn* t){
117 found = get_typed(key, out, t);
119 if (!found) {
120 throw std::out_of_range("Key not found");
121 }
122 return out;
123 }
124
126 template <class T>
127 T get(const KeyT& key, const Transaction& txn) const {
128 return get<T>(key, txn.handle());
129 }
130
133#if __cplusplus >= 201703L
134 template <class T>
135 std::optional<T> find(const KeyT& key, MDBX_txn* txn = nullptr) const {
136 std::optional<T> result;
137 with_transaction([&](MDBX_txn* t){
138 T tmp{};
139 try {
140 if (get_typed(key, tmp, t)) {
141 result = std::move(tmp);
142 }
143 } catch (const std::bad_cast&) {
144 // type mismatch -> treat as not found
145 }
147 return result;
148 }
149
151 template <class T>
152 std::optional<T> find(const KeyT& key, const Transaction& txn) const {
153 return find<T>(key, txn.handle());
154 }
155
161 template <class T>
162 T get_or(const KeyT& key, T default_value, MDBX_txn* txn = nullptr) const {
163 if (auto val = find<T>(key, txn)) {
164 return *std::move(val);
165 }
166 return default_value;
167 }
168
174 template <class T>
175 T get_or(const KeyT& key, T default_value, const Transaction& txn) const {
176 return get_or<T>(key, std::move(default_value), txn.handle());
177 }
178#else
184 template <class T>
185 std::pair<bool, T> find_compat(const KeyT& key, MDBX_txn* txn = nullptr) const {
186 std::pair<bool, T> result{false, T{}};
187 with_transaction([&](MDBX_txn* t){
188 try {
189 if (get_typed(key, result.second, t)) {
190 result.first = true;
191 }
192 } catch (const std::bad_cast&) {
193 // type mismatch -> treat as not found
194 }
196 return result;
197 }
198
204 template <class T>
205 std::pair<bool, T> find_compat(const KeyT& key, const Transaction& txn) const {
206 return find_compat<T>(key, txn.handle());
207 }
208
215 template <class T>
216 T get_or(const KeyT& key, T default_value, MDBX_txn* txn = nullptr) const {
217 auto res = find_compat<T>(key, txn);
218 if (res.first) {
219 return std::move(res.second);
220 }
221 return default_value;
222 }
223
230 template <class T>
231 T get_or(const KeyT& key, T default_value, const Transaction& txn) const {
232 return get_or<T>(key, std::move(default_value), txn.handle());
233 }
234#endif
235
236 // --- Meta ---
237
243 template <class KT = KeyT>
244 bool contains(const KT& key, MDBX_txn* txn = nullptr) const {
245 bool res = false;
246 with_transaction([&](MDBX_txn* t){ res = db_contains(key, t); }, TransactionMode::READ_ONLY, txn);
247 return res;
248 }
249
254 bool contains(const KeyT& key, const Transaction& txn) const {
255 return contains(key, txn.handle());
256 }
257
262 bool erase(const KeyT& key, MDBX_txn* txn = nullptr) {
263 bool res = false;
264 with_transaction([&](MDBX_txn* t){ res = db_erase(key, t); }, TransactionMode::WRITABLE, txn);
265 return res;
266 }
267
272 bool erase(const KeyT& key, const Transaction& txn) {
273 return erase(key, txn.handle());
274 }
275
279 std::vector<KeyT> keys(MDBX_txn* txn = nullptr) const {
280 std::vector<KeyT> out;
281 with_transaction([&](MDBX_txn* t){ db_list_keys(out, t); }, TransactionMode::READ_ONLY, txn);
282 return out;
283 }
284
288 std::vector<KeyT> keys(const Transaction& txn) const {
289 return keys(txn.handle());
290 }
291
294 void set_type_tag_check(bool enabled) noexcept { m_check_type_tag = enabled; }
295
296 private:
297 bool m_check_type_tag = false;
298
299 template<typename F>
300 void with_transaction(F&& action, TransactionMode mode, MDBX_txn* txn) const {
301 if (txn) {
302 action(txn);
303 return;
304 }
305 txn = thread_txn();
306 if (txn) {
307 action(txn);
308 return;
309 }
310 auto txn_guard = m_connection->transaction(mode);
311 try {
312 action(txn_guard.handle());
313 txn_guard.commit();
314 } catch (...) {
315 try { txn_guard.rollback(); } catch (...) {}
316 throw;
317 }
318 }
319
320 template <class T>
321 bool put_typed(const KeyT& key, const T& value, bool upsert, MDBX_txn* txn) {
322 SerializeScratch sc_key;
323 SerializeScratch sc_value;
324 MDBX_val db_key = serialize_key(key, sc_key);
325 MDBX_val raw_val = serialize_value(value, sc_value);
326 MDBX_val db_val = wrap_with_type_tag<T>(raw_val);
327 MDBX_put_flags_t flags = upsert ? MDBX_UPSERT : MDBX_NOOVERWRITE;
328 int rc = mdbx_put(txn, m_dbi, &db_key, &db_val, flags);
329 if (!upsert && rc == MDBX_KEYEXIST) {
330 return false;
331 }
332 check_mdbx(rc, upsert ? "Failed to set value" : "Failed to insert value");
333 return true;
334 }
335
336 template <class T>
337 bool get_typed(const KeyT& key, T& out, MDBX_txn* txn) const {
338 SerializeScratch sc_key;
339 MDBX_val db_key = serialize_key(key, sc_key);
340 MDBX_val db_val{};
341 int rc = mdbx_get(txn, m_dbi, &db_key, &db_val);
342 if (rc == MDBX_NOTFOUND) return false;
343 check_mdbx(rc, "Failed to retrieve value");
344 MDBX_val checked = unwrap_and_check_type_tag<T>(db_val);
345 out = deserialize_value<T>(checked);
346 return true;
347 }
348
349 bool db_contains(const KeyT& key, MDBX_txn* txn) const {
350 SerializeScratch sc_key;
351 MDBX_val db_key = serialize_key(key, sc_key);
352 int rc = mdbx_get(txn, m_dbi, &db_key, nullptr);
353 if (rc == MDBX_SUCCESS) return true;
354 if (rc == MDBX_NOTFOUND) return false;
355 check_mdbx(rc, "Failed to check key presence");
356 return false;
357 }
358
359 bool db_erase(const KeyT& key, MDBX_txn* txn) {
360 SerializeScratch sc_key;
361 MDBX_val db_key = serialize_key(key, sc_key);
362 int rc = mdbx_del(txn, m_dbi, &db_key, nullptr);
363 if (rc == MDBX_SUCCESS) return true;
364 if (rc == MDBX_NOTFOUND) return false;
365 check_mdbx(rc, "Failed to erase key");
366 return false;
367 }
368
369 void db_list_keys(std::vector<KeyT>& out, MDBX_txn* txn) const {
370 MDBX_cursor* cursor = nullptr;
371 check_mdbx(mdbx_cursor_open(txn, m_dbi, &cursor), "Failed to open MDBX cursor");
372 MDBX_val db_key, db_val;
373 while (mdbx_cursor_get(cursor, &db_key, &db_val, MDBX_NEXT) == MDBX_SUCCESS) {
374 out.emplace_back(deserialize_value<KeyT>(db_key));
375 }
376 mdbx_cursor_close(cursor);
377 }
378
379 template <class T>
380 MDBX_val wrap_with_type_tag(const MDBX_val& raw) const {
381 if (!m_check_type_tag) return raw;
382 return raw; // TODO: implement type-tag prefix
383 }
384
385 template <class T>
386 MDBX_val unwrap_and_check_type_tag(const MDBX_val& raw) const {
387 if (!m_check_type_tag) return raw;
388 return raw; // TODO: verify type-tag
389 }
390 };
391
392} // namespace mdbxc
393
394#endif // _MDBX_CONTAINERS_ANY_VALUE_TABLE_HPP_INCLUDED
std::vector< KeyT > keys(MDBX_txn *txn=nullptr) const
List all keys stored in table.
bool insert(const KeyT &key, const T &value, MDBX_txn *txn=nullptr)
Insert value if key does not exist.
std::pair< bool, T > find_compat(const KeyT &key, const Transaction &txn) const
Find value by key using external transaction (C++11 mode).
MDBX_val wrap_with_type_tag(const MDBX_val &raw) const
~AnyValueTable() override=default
Destructor.
void set(const KeyT &key, const T &value, const Transaction &txn)
Set value using external transaction.
void set(const KeyT &key, const T &value, MDBX_txn *txn=nullptr)
Set value for key, replacing existing value.
AnyValueTable(const Config &cfg, std::string name="any_store", MDBX_db_flags_t flags=MDBX_DB_DEFAULTS|MDBX_CREATE)
Constructs table using configuration.
std::vector< KeyT > keys(const Transaction &txn) const
List keys using external transaction.
bool get_typed(const KeyT &key, T &out, MDBX_txn *txn) const
void set_type_tag_check(bool enabled) noexcept
Enable or disable type-tag checking.
T get_or(const KeyT &key, T default_value, MDBX_txn *txn=nullptr) const
Get value or default if missing (C++11 mode).
bool db_erase(const KeyT &key, MDBX_txn *txn)
bool insert(const KeyT &key, const T &value, const Transaction &txn)
Insert value using external transaction.
void with_transaction(F &&action, TransactionMode mode, MDBX_txn *txn) const
bool erase(const KeyT &key, const Transaction &txn)
Erase using external transaction.
AnyValueTable(std::shared_ptr< Connection > conn, std::string name="any_store", MDBX_db_flags_t flags=MDBX_DB_DEFAULTS|MDBX_CREATE)
Constructs table using existing connection.
bool db_contains(const KeyT &key, MDBX_txn *txn) const
void update(const KeyT &key, Fn &&fn, bool create_if_missing, const Transaction &txn)
Update using external transaction.
bool m_check_type_tag
Flag enabling type-tag verification.
T get_or(const KeyT &key, T default_value, const Transaction &txn) const
Get value or default using external transaction (C++11 mode).
bool contains(const KT &key, MDBX_txn *txn=nullptr) const
Check if key exists.
bool put_typed(const KeyT &key, const T &value, bool upsert, MDBX_txn *txn)
std::pair< bool, T > find_compat(const KeyT &key, MDBX_txn *txn=nullptr) const
Find value by key.
void update(const KeyT &key, Fn &&fn, bool create_if_missing=false, MDBX_txn *txn=nullptr)
Update value using functor.
T get(const KeyT &key, const Transaction &txn) const
Retrieve using external transaction.
T get(const KeyT &key, MDBX_txn *txn=nullptr) const
Retrieve stored value or throw if missing.
bool contains(const KeyT &key, const Transaction &txn) const
Check key existence using external transaction.
bool erase(const KeyT &key, MDBX_txn *txn=nullptr)
Erase key from table.
MDBX_val unwrap_and_check_type_tag(const MDBX_val &raw) const
void db_list_keys(std::vector< KeyT > &out, MDBX_txn *txn) const
MDBX_txn * thread_txn() const
Returns the transaction bound to the current thread, if any.
Definition BaseTable.hpp:97
MDBX_dbi m_dbi
DBI handle for the opened table.
Definition BaseTable.hpp:93
BaseTable(std::shared_ptr< Connection > connection, std::string name, MDBX_db_flags_t flags)
Construct the database table accessor.
Definition BaseTable.hpp:22
std::shared_ptr< Connection > m_connection
Shared connection to MDBX environment.
Definition BaseTable.hpp:92
Parameters used by Connection to create the MDBX environment.
Definition Config.hpp:17
Manages a single MDBX environment and an optional read-only transaction.
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.
Definition utils.hpp:376
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.
Definition utils.hpp:486
MDBX_db_flags_t get_mdbx_flags()
Returns MDBX flags for a given key type.
Definition utils.hpp:94
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.
Definition utils.hpp:230
void check_mdbx(int rc, const std::string &context)
Throws an MdbxException if MDBX return code indicates an error.
Definition utils.hpp:23
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.
Definition utils.hpp:168