LMDBAL 0.6.0
LMDB (Lightning Memory-Mapped Database Manager) Abstraction Layer
Loading...
Searching...
No Matches
cursor.hpp
1/*
2 * LMDB Abstraction Layer.
3 * Copyright (C) 2023 Yury Gubich <blue@macaw.me>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21#include "cursor.h"
22#include <iostream>
23
43static uint32_t idCounter = 0;
44
50template<class K, class V>
52 storage(parent),
53 cursor(nullptr),
54 state(closed),
55 id(++idCounter)
56{
57 storage->cursors[id] = this;
58}
59
65template<class K, class V>
67 storage(nullptr),
68 cursor(nullptr),
69 state(closed),
70 id(0)
71{}
72
76template<class K, class V>
78 storage(other.storage),
79 cursor(other.cursor),
80 state(other.state),
81 id(other.id)
82{
83 other.terminated();
84 if (id != 0)
85 storage->cursors[id] = this;
86
87 other.freed();
88}
89
96template<class K, class V>
98 terminated();
99
100 if (id != 0)
101 storage->cursors.erase(id);
102
103 storage = other.storage;
104 cursor = other.cursor;
105 state = other.state;
106 id = other.id;
107
108 if (id != 0) {
109 other.freed();
110 other.state = closed;
111
112 storage->cursors[id] = this;
113 }
114
115 return *this;
116}
117
123template<class K, class V>
125 close();
126
127 if (id != 0)
128 storage->cursors.erase(id);
129}
130
137template<class K, class V>
139 close();
140
141 if (id != 0)
142 storage->cursors.erase(id);
143
144 freed();
145}
146
153template<class K, class V>
155 terminated();
156 freed();
157}
158
165template<class K, class V>
167 cursor = nullptr;
168 storage = nullptr;
169 id = 0;
170}
171
177template<class K, class V>
179 return id == 0;
180}
181
185template<class K, class V>
187 close(); //for now it's the same, but if I ever going to make writable cursor - here is where it's gonna be different
188}
189
203template<class K, class V>
205 if (empty())
206 throw CursorEmpty(openCursorMethodName);
207
208 storage->ensureOpened(openCursorMethodName);
209 switch (state) {
210 case closed: {
211 TransactionID txn = storage->beginReadOnlyTransaction();
212 int result = storage->_mdbCursorOpen(txn, &cursor);
213 if (result != MDB_SUCCESS)
214 storage->throwUnknown(result, txn);
215
216 storage->transactionStarted(txn, true);
217 state = openedPrivate;
218 } break;
219 default:
220 break;
221 }
222}
223
240template<class K, class V>
241void LMDBAL::Cursor<K, V>::open (const Transaction& transaction) {
242 if (empty())
243 throw CursorEmpty(openCursorMethodName);
244
245 storage->ensureOpened(openCursorMethodName);
246 TransactionID txn = storage->extractTransactionId(transaction, openCursorMethodName);
247 switch (state) {
248 case closed: {
249 int result = storage->_mdbCursorOpen(txn, &cursor);
250 if (result != MDB_SUCCESS)
251 storage->throwUnknown(result);
252
253 state = openedPublic;
254 } break;
255 default:
256 break;
257 }
258}
259
277template<class K, class V>
279 if (empty())
280 throw CursorEmpty(openCursorMethodName);
281
282 storage->ensureOpened(renewCursorMethodName);
283 switch (state) {
284 case openedPrivate: {
285 TransactionID txn = storage->_mdbCursorTxn(cursor);
286 storage->abortTransaction(txn);
287 storage->transactionAborted(txn);
288 [[fallthrough]];
289 }
290 case openedPublic: {
291 TransactionID txn = storage->beginReadOnlyTransaction();
292 int result = storage->_mdbCursorRenew(txn, cursor);
293 if (result != MDB_SUCCESS)
294 storage->throwUnknown(result, txn);
295
296 storage->transactionStarted(txn, true);
297 state = openedPrivate;
298 } break;
299 default:
300 break;
301 }
302}
303
324template<class K, class V>
325void LMDBAL::Cursor<K, V>::renew (const Transaction& transaction) {
326 if (empty())
327 throw CursorEmpty(openCursorMethodName);
328
329 storage->ensureOpened(renewCursorMethodName);
330 TransactionID txn = storage->extractTransactionId(transaction, renewCursorMethodName);
331 switch (state) {
332 case openedPrivate: {
333 TransactionID txn = storage->_mdbCursorTxn(cursor);
334 storage->abortTransaction(txn);
335 storage->transactionAborted(txn);
336 [[fallthrough]];
337 }
338 case openedPublic: {
339 int result = storage->_mdbCursorRenew(txn, cursor);
340 if (result != MDB_SUCCESS)
341 storage->throwUnknown(result);
342
343 state = openedPublic;
344 } break;
345 default:
346 break;
347 }
348}
349
360template<class K, class V>
362 switch (state) {
363 case openedPublic: {
364 storage->_mdbCursorClose(cursor);
365
366 state = closed;
367 } break;
368 case openedPrivate: {
369 TransactionID txn = storage->_mdbCursorTxn(cursor);
370 storage->_mdbCursorClose(cursor);
371 storage->abortTransaction(txn);
372 storage->transactionAborted(txn);
373
374 state = closed;
375 } break;
376 default:
377 break;
378 }
379}
380
384template<class K, class V>
386 return state != closed;
387}
388
401template<class K, class V>
402void LMDBAL::Cursor<K, V>::first (K& key, V& value) {
403 operateCursorRead(key, value, MDB_FIRST, firstMethodName, firstOperationName);
404}
405
418template<class K, class V>
419void LMDBAL::Cursor<K, V>::last (K& key, V& value) {
420 operateCursorRead(key, value, MDB_LAST, lastMethodName, lastOperationName);
421}
422
441template<class K, class V>
442void LMDBAL::Cursor<K, V>::next (K& key, V& value) {
443 operateCursorRead(key, value, MDB_NEXT, nextMethodName, nextOperationName);
444}
445
464template<class K, class V>
465void LMDBAL::Cursor<K, V>::prev (K& key, V& value) {
466 operateCursorRead(key, value, MDB_PREV, prevMethodName, prevOperationName);
467}
468
483template<class K, class V>
484void LMDBAL::Cursor<K, V>::current (K& key, V& value) const {
485 operateCursorRead(key, value, MDB_GET_CURRENT, currentMethodName, currentOperationName);
486}
487
499template<class K, class V>
500std::pair<K, V> LMDBAL::Cursor<K, V>::first () {
501 std::pair<K, V> result;
502 operateCursorRead(result.first, result.second, MDB_FIRST, firstMethodName, firstOperationName);
503 return result;
504}
505
517template<class K, class V>
518std::pair<K, V> LMDBAL::Cursor<K, V>::last () {
519 std::pair<K, V> result;
520 operateCursorRead(result.first, result.second, MDB_LAST, lastMethodName, lastOperationName);
521 return result;
522}
523
541template<class K, class V>
542std::pair<K, V> LMDBAL::Cursor<K, V>::next () {
543 std::pair<K, V> result;
544 operateCursorRead(result.first, result.second, MDB_NEXT, nextMethodName, nextOperationName);
545 return result;
546}
547
565template<class K, class V>
566std::pair<K, V> LMDBAL::Cursor<K, V>::prev () {
567 std::pair<K, V> result;
568 operateCursorRead(result.first, result.second, MDB_PREV, prevMethodName, prevOperationName);
569 return result;
570}
571
585template<class K, class V>
586std::pair<K, V> LMDBAL::Cursor<K, V>::current () const {
587 std::pair<K, V> result;
588 operateCursorRead(result.first, result.second, MDB_GET_CURRENT, currentMethodName, currentOperationName);
589 return result;
590}
591
604template<class K, class V>
605bool LMDBAL::Cursor<K, V>::set (const K& key) {
606 if (state == closed)
607 storage->throwCursorNotReady(setMethodName);
608
609 MDB_val mdbKey = storage->keySerializer.setData(key);
610 int result = storage->_mdbCursorSet(cursor, mdbKey);
611 if (result == MDB_SUCCESS)
612 return true;
613 else if (result == MDB_NOTFOUND)
614 return false;
615
616 storage->throwUnknown(result);
617 return false; //unreachable, just to suppress the warning
618}
619
635template<class K, class V>
637 K& key,
638 V& value,
639 MDB_cursor_op operation,
640 const std::string& methodName,
641 const std::string& operationName
642) const {
643 if (state == closed)
644 storage->throwCursorNotReady(methodName);
645
646 MDB_val mdbKey, mdbValue;
647 int result = storage->_mdbCursorGet(cursor, mdbKey, mdbValue, operation);
648 if (result != MDB_SUCCESS)
649 storage->throwNotFoundOrUnknown(result, operationName);
650
651 storage->keySerializer.deserialize(mdbKey, key);
652 storage->valueSerializer.deserialize(mdbValue, value);
653
654 if (state == openedPrivate)
655 storage->discoveredRecord(key, value);
656 else
657 storage->discoveredRecord(key, value, storage->_mdbCursorTxn(cursor));
658}
Thrown if an empty cursor was somehow operated.
Definition exceptions.h:102
An object to iterate storages.
Definition cursor.h:31
std::pair< K, V > first()
Queries the first element in the storage.
Definition cursor.hpp:500
void drop()
Turns cursor into an empty one, releasing resources.
Definition cursor.hpp:138
bool empty() const
Returns true if the cursor is empty.
Definition cursor.hpp:178
void renew()
Renews a cursor.
Definition cursor.hpp:278
~Cursor()
Destroys a cursor.
Definition cursor.hpp:124
void close()
Termiates a sequence of operations with the cursor.
Definition cursor.hpp:361
bool opened() const
Tells if the cursor is open.
Definition cursor.hpp:385
void open()
Opens the cursor for operations.
Definition cursor.hpp:204
std::pair< K, V > prev()
Queries the previous element from the storage.
Definition cursor.hpp:566
std::pair< K, V > last()
Queries the last element in the storage.
Definition cursor.hpp:518
std::pair< K, V > current() const
Returns current cursor element from the storage.
Definition cursor.hpp:586
bool set(const K &target)
Sets cursors to the defined position.
Definition cursor.hpp:605
Cursor()
Creates an empty cursor.
Definition cursor.hpp:66
std::pair< K, V > next()
Queries the next element from the storage.
Definition cursor.hpp:542
This is a basic key value storage.
Definition storage.h:134
Public read only transaction.
Definition transaction.h:26