-
Notifications
You must be signed in to change notification settings - Fork 0
/
sqlite3_map.h
229 lines (190 loc) · 8.01 KB
/
sqlite3_map.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
// Copyright (c) 2015-2016 The Pebblecoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
namespace sqlite3
{
struct sqlite3;
struct sqlite3_stmt;
}
#include <string>
#include <cstdint>
#include <memory>
#include <functional>
namespace sqlite3 {
/** A *very basic* key/value store backed by sqlite3. Types can be anything that serializes
* to a std::string, with the serialization methods passed into the constructor. Some built-in
* ones are provided in sqlite3_map_ser.h
*
* NOTES:
* - Not thread-safe!
* - Only provides InputIterator
* - iterators give an unordered view of the map
* - all iterators must be destructed before reopen()ing or destructing the map, or an exception
* will be thrown
* - find() returns iterator that only points to that element, not any others
*/
template <class K, class V>
class sqlite3_map
{
public:
typedef K key_type;
typedef V mapped_type;
typedef std::pair<K, V> value_type;
typedef std::unique_ptr<sqlite3_stmt, std::function<void(sqlite3_stmt*)>> stmt_ptr;
public:
/// bare-minimum iterator to be able to loop through all entries
/// reference gotten from iterator are only valid so long as the iterator
/// does not move. assigning to the iterator does not change any values
/// in the map
struct iterator
{
friend class sqlite3_map<K, V>;
public:
// must init with a prepared statement, which this will take ownership of
iterator(const sqlite3_map<K, V>& parent, stmt_ptr&& pStmt);
iterator(const iterator& o);
iterator(iterator&& o);
iterator& operator=(iterator o);
~iterator();
public:
iterator& operator++();
value_type operator++(int);
const value_type& operator*() const;
const value_type* operator->() const;
bool operator==(const iterator& i2) const;
bool operator!=(const iterator& i2) const;
private:
std::string& getKey() const;
std::string& getValue() const;
value_type& getPair() const;
void invalidateCache() const;
/// erase, returning new iterator
iterator erase();
/// return whether this iterator is valid
bool valid() const;
private:
const sqlite3_map<K, V>& parent;
size_t which_db; // to track reopen()s
stmt_ptr pStmt;
bool is_past_the_end;
mutable bool cached_key_valid;
mutable bool cached_value_valid;
mutable bool cached_pair_valid;
mutable std::string cached_key;
mutable std::string cached_value;
mutable value_type cached_pair;
public:
// assumes swapping two iterators of same map
friend void swap(iterator& first, iterator& second)
{
using std::swap;
swap(first.which_db, second.which_db);
swap(first.pStmt, second.pStmt);
swap(first.is_past_the_end, second.is_past_the_end);
swap(first.cached_key_valid, second.cached_key_valid);
swap(first.cached_value_valid, second.cached_value_valid);
swap(first.cached_pair_valid, second.cached_pair_valid);
swap(first.cached_key, second.cached_key);
swap(first.cached_value, second.cached_value);
swap(first.cached_pair, second.cached_pair);
}
};
// only const InputIterator anyway
typedef iterator const_iterator;
public:
/// create an sqlite3 map. if filename is nullptr, an in-memory database is created
sqlite3_map(const char *filename,
std::function<K(const std::string&)> load_key,
std::function<std::string(const K&)> store_key,
std::function<V(const std::string&)> load_value,
std::function<std::string(const V&)> store_value);
sqlite3_map(sqlite3_map&& o);
sqlite3_map& operator=(sqlite3_map&& o);
~sqlite3_map();
/// close the database. all iterators invalidated, all operations besides reopen() are undefined
void close();
/// close the file & open with a new file. all iterators are invalidated, but can
/// still be compared to other iterators (but will compare unequal to anything from an
/// old database). no data is transferred. if filename is nullptr, an in-memory database is created
void reopen(const char *filename);
/// set the autocommit behavior: whether to commit after every modification, and whether to
/// commit when the database is closed. Defaults are false, true.
void set_autocommit(bool per_modification, bool on_close);
/// get the autocommit behavior
void get_autocommit(bool& per_modification, bool& on_close) const;
/// commit whatever changes. only necessary if autocommit is disabled
void commit();
/// rollback to previous commit. invalidates all iterators
void rollback();
/// load the value at a given key. if the key doesn't exist, returns a value-initialized value
V load(const K& key) const;
/// store a value to a key
void store(const K& key, const V& value);
/// insert if key doesn't exist. return pair(iterator to element, whether insertion took place)
std::pair<iterator, bool> insert(const value_type& value);
/// count of a key, either 0 or 1
size_t count(const K& key) const;
/// return whether the map contains a value for the given key
bool contains(const K& key) const;
/// returns whether the map is empty
bool empty() const;
/// returns the size of the map
size_t size() const;
/// clears the map
void clear();
/// erases the element with the given key
size_t erase(const K& key);
/// erase the element at the given iterator. returns an iterator to the next element
iterator erase(iterator pos);
/// valid iterator to start of map
const_iterator cbegin() const;
const_iterator begin() const { return cbegin(); }
iterator begin() { return cbegin(); }
/// returns an invalid iterator
const_iterator cend() const;
const_iterator end() const { return cend(); }
iterator end() { return cend(); }
/// iterator to element of the given key. either end(), or an iterator with just this
/// element (i.e. ++ of the it gives end())
const_iterator find(const K& key) const;
iterator find(const K& key) { return const_cast<const sqlite3_map<K, V>&>(*this).find(key); }
private:
void checked(int rc, const std::string& op="") const;
void checked_exec(stmt_ptr& stmt) const; // execute statement fully
void checked_exec(const char *query) const; // execute query fully
stmt_ptr prepare_stmt(const char *query) const;
stmt_ptr prepare_find_key_blob(const std::string& key_blob) const;
private:
sqlite3 *pDb;
size_t which_db;
std::function<K(const std::string&)> load_key;
std::function<std::string(const K&)> store_key;
std::function<V(const std::string&)> load_value;
std::function<std::string(const V&)> store_value;
bool autocommit_per_modification;
bool autocommit_on_close;
const char *db_filename;
// optimization
stmt_ptr _opt_count_stmt;
stmt_ptr _opt_count_key_stmt;
stmt_ptr _opt_insert_stmt;
public:
friend void swap(sqlite3_map& first, sqlite3_map& second)
{
using std::swap;
swap(first.pDb, second.pDb);
swap(first.which_db, second.which_db);
swap(first.load_key, second.load_key);
swap(first.store_key, second.store_key);
swap(first.load_value, second.load_value);
swap(first.store_value, second.store_value);
swap(first.autocommit_per_modification, second.autocommit_per_modification);
swap(first.autocommit_on_close, second.autocommit_on_close);
swap(first.db_filename, second.db_filename);
swap(first._opt_count_stmt, second._opt_count_stmt);
swap(first._opt_count_key_stmt, second._opt_count_key_stmt);
swap(first._opt_insert_stmt, second._opt_insert_stmt);
}
};
}