31 #define RETURN_IF_ERROR(code) \ 33 const DatabaseStatus ret = (code); \ 34 if (ret != DatabaseStatus::Success) \ 46 VLOG(2) <<
"Sqlite database busy";
51 VLOG(2) <<
"No entries returned";
55 if (ret == SQLITE_CONSTRAINT_FOREIGNKEY) {
56 VLOG(2) <<
"Foreign key not found";
58 }
else if (ret == SQLITE_CONSTRAINT_PRIMARYKEY ||
59 ret == SQLITE_CONSTRAINT_UNIQUE) {
60 VLOG(2) <<
"Duplicate entries in table";
64 LOG(DFATAL) <<
"Unknown error from sqlite (" << ret
65 <<
"): " << sqlite3_errstr(ret);
73 struct GetColumn<std::string> {
74 static std::string Get(sqlite3_stmt* stmt,
size_t index) {
75 const size_t size = sqlite3_column_bytes(stmt, index);
76 auto* ret = sqlite3_column_text(stmt, index);
77 return std::string{
reinterpret_cast<const char*
>(ret), size};
81 struct GetColumn<std::vector<uint8_t>> {
82 static std::vector<uint8_t> Get(sqlite3_stmt* stmt,
size_t index) {
83 const size_t size = sqlite3_column_bytes(stmt, index);
85 reinterpret_cast<const uint8_t*
>(sqlite3_column_blob(stmt, index));
86 return std::vector<uint8_t>{ret, ret + size};
90 struct GetColumn<int64_t> {
91 static int64_t Get(sqlite3_stmt* stmt,
size_t index) {
92 return sqlite3_column_int64(stmt, index);
96 template <
typename Func,
typename... Columns>
98 template <
typename Func>
99 struct GetColumns<Func> {
100 template <
typename... Args>
101 static int Get(sqlite3_stmt* ,
size_t , Func&& func,
103 return func(std::forward<Args>(args)...);
106 template <
typename Func,
typename T,
typename... Rest>
107 struct GetColumns<Func, T, Rest...> {
108 template <
typename... Args>
109 static int Get(sqlite3_stmt* stmt,
size_t index, Func&& func,
111 T temp = GetColumn<T>::Get(stmt, index);
112 return GetColumns<Func, Rest...>::Get(
113 stmt, index + 1, std::forward<Func>(func), std::forward<Args>(args)...,
118 template <
typename T>
119 struct BindSingleArg;
121 struct BindSingleArg<std::string> {
122 static int Bind(sqlite3_stmt* stmt,
size_t index,
const std::string& arg) {
123 return sqlite3_bind_text(stmt, index, arg.c_str(), arg.size(),
128 struct BindSingleArg<std::vector<uint8_t>> {
129 static int Bind(sqlite3_stmt* stmt,
size_t index,
130 const std::vector<uint8_t>& arg) {
131 return sqlite3_bind_blob64(stmt, index, arg.data(), arg.size(),
136 struct BindSingleArg<int64_t> {
137 static int Bind(sqlite3_stmt* stmt,
size_t index, int64_t arg) {
138 return sqlite3_bind_int64(stmt, index, arg);
142 template <
typename... Args>
146 static int Bind(sqlite3_stmt* ,
size_t ) {
150 template <
typename T,
typename... Rest>
151 struct BindArgs<T, Rest...> {
152 static int Bind(sqlite3_stmt* stmt,
size_t offset, T&& cur, Rest&&... rest) {
154 stmt, offset, std::forward<T>(cur));
155 if (ret != SQLITE_OK)
157 return BindArgs<Rest...>::Bind(stmt, offset + 1,
158 std::forward<Rest>(rest)...);
163 template <
typename... InParams,
typename... Columns>
164 DatabaseStatus ExecGetResults(sqlite3* db, std::function<
int(Columns...)> cb,
165 const std::string& cmd, InParams&&... params) {
166 VLOG(2) <<
"Querying sqlite: " << cmd;
169 int ret = sqlite3_prepare_v2(db, cmd.c_str(), cmd.size(), &stmt,
nullptr);
170 if (ret != SQLITE_OK)
171 return MapErrorCode(ret);
172 std::unique_ptr<sqlite3_stmt, int (*)(sqlite3_stmt*)> stmt_safe(
173 stmt, &sqlite3_finalize);
175 ret = BindArgs<InParams...>::Bind(stmt, 1, std::forward<InParams>(params)...);
176 if (ret != SQLITE_OK)
177 return MapErrorCode(ret);
179 while ((ret = sqlite3_step(stmt)) == SQLITE_ROW) {
180 ret = GetColumns<std::function<int(Columns...)>&, Columns...>::Get(stmt, 0,
182 if (ret != SQLITE_OK)
183 return MapErrorCode(ret);
185 return MapErrorCode(ret == SQLITE_DONE ? SQLITE_OK : ret);
188 template <
typename... Args>
191 std::function<int()> ignore = []() {
return SQLITE_OK; };
192 return ExecGetResults(db, ignore, cmd, std::forward<Args>(args)...);
195 template <
typename T,
typename... Args>
197 const std::string& cmd, Args&&... args) {
199 std::function<int(T)>
get = [&](T value) {
204 swap(value, *result);
209 RETURN_IF_ERROR(ExecGetResults(db,
get, cmd, std::forward<Args>(args)...));
239 DCHECK(db_) <<
"Transaction is closed";
243 const std::string cmd =
244 "INSERT INTO databases (name, version) VALUES (?1, ?2)";
245 return ExecCommand(db_, cmd, db_name, version);
250 DCHECK(db_) <<
"Transaction is closed";
253 if (version <= old_version)
256 const std::string cmd =
"UPDATE databases SET version = ?2 WHERE name == ?1";
257 return ExecCommand(db_, cmd, db_name, version);
261 DCHECK(db_) <<
"Transaction is closed";
268 const std::string delete_cmd =
"DELETE FROM databases WHERE name == ?1";
269 return ExecCommand(db_, delete_cmd, db_name);
274 DCHECK(db_) <<
"Transaction is closed";
275 const std::string cmd =
"SELECT version FROM databases WHERE name == ?1";
276 return ExecGetSingleResult(db_, version, cmd, db_name);
281 const std::string& db_name,
const std::string& store_name) {
282 DCHECK(db_) <<
"Transaction is closed";
283 const std::string cmd =
284 "INSERT INTO object_stores (db_name, store_name) VALUES (?1, ?2)";
288 return ExecCommand(db_, cmd, db_name, store_name);
292 const std::string& db_name,
const std::string& store_name) {
293 DCHECK(db_) <<
"Transaction is closed";
300 const std::string cmd =
301 "DELETE FROM object_stores WHERE db_name == ?1 AND store_name == ?2";
302 return ExecCommand(db_, cmd, db_name, store_name);
306 const std::string& db_name, std::vector<std::string>* names) {
307 DCHECK(db_) <<
"Transaction is closed";
314 std::function<int(std::string)> cb = [&](std::string value) {
315 names->push_back(std::move(value));
318 const std::string cmd =
319 "SELECT store_name FROM object_stores WHERE db_name == ?1";
320 return ExecGetResults(db_, cb, cmd, db_name);
325 const std::string& store_name,
326 const std::vector<uint8_t>& data,
328 DCHECK(db_) <<
"Transaction is closed";
332 const std::string select_cmd =
333 "SELECT COALESCE(MAX(key), 0) FROM objects WHERE store == ?1";
337 const std::string insert_cmd =
338 "INSERT INTO objects (store, key, body) VALUES (?1, ?2, ?3)";
339 return ExecCommand(db_, insert_cmd, store_id, *key, data);
343 const std::string& store_name,
345 std::vector<uint8_t>* data) {
346 DCHECK(db_) <<
"Transaction is closed";
347 const std::string cmd =
348 "SELECT body FROM objects " 349 "INNER JOIN object_stores ON object_stores.id == objects.store " 350 "WHERE db_name == ?1 AND store_name == ?2 AND key == ?3";
351 return ExecGetSingleResult(db_, data, cmd, db_name, store_name, key);
355 const std::string& store_name,
357 const std::vector<uint8_t>& data) {
358 DCHECK(db_) <<
"Transaction is closed";
362 const std::string cmd =
363 "INSERT OR REPLACE INTO objects (store, key, body) VALUES (?1, ?2, ?3)";
364 return ExecCommand(db_, cmd, store_id, key, data);
368 const std::string& store_name,
370 DCHECK(db_) <<
"Transaction is closed";
371 const std::string cmd = R
"( 372 DELETE FROM objects WHERE key == ?3 AND store == ( 373 SELECT id FROM object_stores WHERE db_name == ?1 AND store_name == ?2) 375 return ExecCommand(db_, cmd, db_name, store_name, key);
379 const std::string& store_name,
381 bool ascending, int64_t* found_key) {
382 DCHECK(db_) <<
"Transaction is closed";
389 SELECT key FROM objects 390 WHERE store == (SELECT id FROM object_stores 391 WHERE db_name == ?1 AND store_name == ?2) 395 ascending ? "ASC" :
"DESC");
396 return ExecGetSingleResult(db_, found_key, cmd, db_name, store_name);
400 SELECT key FROM objects 401 WHERE store == (SELECT id FROM object_stores 402 WHERE db_name == ?1 AND store_name == ?2) AND 407 ascending ? ">" :
"<", ascending ?
"ASC" :
"DESC");
408 return ExecGetSingleResult(db_, found_key, cmd, db_name, store_name,
414 DCHECK(db_) <<
"Transaction is closed";
417 return ExecCommand(db,
"COMMIT");
421 DCHECK(db_) <<
"Transaction is closed";
424 return ExecCommand(db,
"ROLLBACK");
428 DatabaseStatus SqliteTransaction::GetStoreId(
const std::string& db_name,
429 const std::string& store_name,
431 const std::string get_cmd =
432 "SELECT id FROM object_stores " 433 "WHERE db_name == ?1 AND store_name == ?2";
434 return ExecGetSingleResult(db_, store_id, get_cmd, db_name, store_name);
439 : path_(file_path), db_(nullptr) {}
442 const auto ret = sqlite3_close(db_);
443 if (ret != SQLITE_OK) {
444 LOG(ERROR) <<
"Error closing sqlite connection: " << sqlite3_errstr(ret);
457 const std::string init_cmd = R
"( 458 -- Timeout, in milliseconds, to wait if there is an exclusive lock on the 459 -- database. When in WAL mode, we can have non-exclusive writes, so we 460 -- should never get busy normally. 461 PRAGMA busy_timeout = 250; 462 PRAGMA foreign_keys = ON; 463 -- Switch to WAL journaling mode; this is faster (usually) and allows for 464 -- non-exclusive write transactions. 465 PRAGMA journal_mode = WAL; 467 CREATE TABLE IF NOT EXISTS databases ( 468 name TEXT NOT NULL PRIMARY KEY, 469 version INTEGER NOT NULL, 473 CREATE TABLE IF NOT EXISTS object_stores ( 474 id INTEGER PRIMARY KEY NOT NULL, 475 db_name TEXT NOT NULL, 476 store_name TEXT NOT NULL, 477 UNIQUE (db_name, store_name), 478 FOREIGN KEY (db_name) REFERENCES databases(name) ON DELETE CASCADE 481 CREATE TABLE IF NOT EXISTS objects ( 482 store INTEGER NOT NULL, 483 key INTEGER NOT NULL, 485 PRIMARY KEY (store, key), 486 FOREIGN KEY (store) REFERENCES object_stores (id) ON DELETE CASCADE 490 sqlite3_exec(db, init_cmd.c_str(), nullptr,
nullptr,
nullptr)));
498 transaction->db_ = db_;
503 return MapErrorCode(sqlite3_wal_checkpoint_v2(
504 db_,
nullptr, SQLITE_CHECKPOINT_PASSIVE,
nullptr,
nullptr));
DatabaseStatus DeleteData(const std::string &db_name, const std::string &store_name, int64_t key)
DatabaseStatus DeleteObjectStore(const std::string &db_name, const std::string &store_name)
DatabaseStatus ListObjectStores(const std::string &db_name, std::vector< std::string > *names)
std::string StringPrintf(const char *format,...)
DatabaseStatus Rollback()
DatabaseStatus GetData(const std::string &db_name, const std::string &store_name, int64_t key, std::vector< uint8_t > *data)
DatabaseStatus AddData(const std::string &db_name, const std::string &store_name, const std::vector< uint8_t > &data, int64_t *key)
DatabaseStatus UpdateData(const std::string &db_name, const std::string &store_name, int64_t key, const std::vector< uint8_t > &data)
SqliteConnection(const std::string &file_path)
DatabaseStatus GetDbVersion(const std::string &db_name, int64_t *version)
const T & value() const &
DatabaseStatus CreateObjectStore(const std::string &db_name, const std::string &store_name)
DatabaseStatus CreateDb(const std::string &db_name, int64_t version)
#define RETURN_IF_ERROR(code)
void swap(shared_lock< Mutex > &a, shared_lock< Mutex > &b)
DatabaseStatus FindData(const std::string &db_name, const std::string &store_name, optional< int64_t > key, bool ascending, int64_t *found_key)
DatabaseStatus UpdateDbVersion(const std::string &db_name, int64_t version)
SqliteTransaction & operator=(SqliteTransaction &&)
DatabaseStatus DeleteDb(const std::string &db_name)
DatabaseStatus BeginTransaction(SqliteTransaction *transaction)