From e9568c9a8ef1176629d500e35c4e4c69b0b84e32 Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 22 Jul 2018 02:56:40 +0300 Subject: [PATCH] Add SqliteKeyValue.cpp. GitOrigin-RevId: d0490b00c3342db191a29e7780d3292e3b593896 --- tddb/CMakeLists.txt | 1 + tddb/td/db/SqliteKeyValue.cpp | 130 +++++++++++++++++++++++++++++++ tddb/td/db/SqliteKeyValue.h | 143 ++++++---------------------------- tdutils/td/utils/ScopeGuard.h | 1 + 4 files changed, 156 insertions(+), 119 deletions(-) create mode 100644 tddb/td/db/SqliteKeyValue.cpp diff --git a/tddb/CMakeLists.txt b/tddb/CMakeLists.txt index 531dcc5c..50e17120 100644 --- a/tddb/CMakeLists.txt +++ b/tddb/CMakeLists.txt @@ -10,6 +10,7 @@ set(TDDB_SOURCE td/db/SqliteDb.cpp td/db/SqliteStatement.cpp + td/db/SqliteKeyValue.cpp td/db/SqliteKeyValueAsync.cpp td/db/detail/RawSqliteDb.cpp diff --git a/tddb/td/db/SqliteKeyValue.cpp b/tddb/td/db/SqliteKeyValue.cpp new file mode 100644 index 00000000..34e0c4e5 --- /dev/null +++ b/tddb/td/db/SqliteKeyValue.cpp @@ -0,0 +1,130 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/db/SqliteKeyValue.h" + +#include "td/utils/ScopeGuard.h" + +namespace td { + +Result SqliteKeyValue::init(string path) { + path_ = std::move(path); + bool is_created = false; + SqliteDb db; + TRY_STATUS(db.init(path, &is_created)); + TRY_STATUS(db.exec("PRAGMA encoding=\"UTF-8\"")); + TRY_STATUS(db.exec("PRAGMA synchronous=NORMAL")); + TRY_STATUS(db.exec("PRAGMA journal_mode=WAL")); + TRY_STATUS(db.exec("PRAGMA temp_store=MEMORY")); + TRY_STATUS(init_with_connection(std::move(db), "KV")); + return is_created; +} + +Status SqliteKeyValue::init_with_connection(SqliteDb connection, string table_name) { + db_ = std::move(connection); + table_name_ = std::move(table_name); + TRY_STATUS(init(db_, table_name_)); + + TRY_RESULT(set_stmt, db_.get_statement(PSLICE() << "REPLACE INTO " << table_name_ << " (k, v) VALUES (?1, ?2)")); + set_stmt_ = std::move(set_stmt); + TRY_RESULT(get_stmt, db_.get_statement(PSLICE() << "SELECT v FROM " << table_name_ << " WHERE k = ?1")); + get_stmt_ = std::move(get_stmt); + TRY_RESULT(erase_stmt, db_.get_statement(PSLICE() << "DELETE FROM " << table_name_ << " WHERE k = ?1")); + erase_stmt_ = std::move(erase_stmt); + TRY_RESULT(get_all_stmt, db_.get_statement(PSLICE() << "SELECT k, v FROM " << table_name_ << "")); + + TRY_RESULT(erase_by_prefix_stmt, + db_.get_statement(PSLICE() << "DELETE FROM " << table_name_ << " WHERE ?1 <= k AND k < ?2")); + erase_by_prefix_stmt_ = std::move(erase_by_prefix_stmt); + + TRY_RESULT(erase_by_prefix_rare_stmt, + db_.get_statement(PSLICE() << "DELETE FROM " << table_name_ << " WHERE ?1 <= k")); + erase_by_prefix_rare_stmt_ = std::move(erase_by_prefix_rare_stmt); + + TRY_RESULT(get_by_prefix_stmt, + db_.get_statement(PSLICE() << "SELECT k, v FROM " << table_name_ << " WHERE ?1 <= k AND k < ?2")); + get_by_prefix_stmt_ = std::move(get_by_prefix_stmt); + + TRY_RESULT(get_by_prefix_rare_stmt, + db_.get_statement(PSLICE() << "SELECT k, v FROM " << table_name_ << " WHERE ?1 <= k")); + get_by_prefix_rare_stmt_ = std::move(get_by_prefix_rare_stmt); + + get_all_stmt_ = std::move(get_all_stmt); + return Status::OK(); +} + +void SqliteKeyValue::close_and_destroy() { + drop(db_, table_name_).ensure(); + auto path = std::move(path_); + clear(); + if (!path.empty()) { + destroy(path).ignore(); + } +} + +SqliteKeyValue::SeqNo SqliteKeyValue::set(Slice key, Slice value) { + set_stmt_.bind_blob(1, key).ensure(); + set_stmt_.bind_blob(2, value).ensure(); + set_stmt_.step().ensure(); + set_stmt_.reset(); + return 0; +} + +string SqliteKeyValue::get(Slice key) { + SCOPE_EXIT { + get_stmt_.reset(); + }; + get_stmt_.bind_blob(1, key).ensure(); + get_stmt_.step().ensure(); + if (!get_stmt_.has_row()) { + return ""; + } + auto data = get_stmt_.view_blob(0).str(); + get_stmt_.step().ignore(); + return data; +} + +SqliteKeyValue::SeqNo SqliteKeyValue::erase(Slice key) { + erase_stmt_.bind_blob(1, key).ensure(); + erase_stmt_.step().ensure(); + erase_stmt_.reset(); + return 0; +} + +void SqliteKeyValue::erase_by_prefix(Slice prefix) { + auto next = next_prefix(prefix); + if (next.empty()) { + SCOPE_EXIT { + erase_by_prefix_rare_stmt_.reset(); + }; + erase_by_prefix_rare_stmt_.bind_blob(1, prefix).ensure(); + erase_by_prefix_rare_stmt_.step().ensure(); + } else { + SCOPE_EXIT { + erase_by_prefix_stmt_.reset(); + }; + erase_by_prefix_stmt_.bind_blob(1, prefix).ensure(); + erase_by_prefix_stmt_.bind_blob(2, next).ensure(); + erase_by_prefix_stmt_.step().ensure(); + } +} + +string SqliteKeyValue::next_prefix(Slice prefix) { + string next = prefix.str(); + size_t pos = next.size(); + while (pos) { + pos--; + auto value = static_cast(next[pos]); + value++; + next[pos] = static_cast(value); + if (value != 0) { + return next; + } + } + return string{}; +} + +} // namespace td diff --git a/tddb/td/db/SqliteKeyValue.h b/tddb/td/db/SqliteKeyValue.h index f4221f36..31ac9a53 100644 --- a/tddb/td/db/SqliteKeyValue.h +++ b/tddb/td/db/SqliteKeyValue.h @@ -10,7 +10,6 @@ #include "td/db/SqliteStatement.h" #include "td/utils/logging.h" -#include "td/utils/ScopeGuard.h" #include "td/utils/Slice.h" #include "td/utils/Status.h" @@ -20,106 +19,43 @@ namespace td { class SqliteKeyValue { public: - static Status drop(SqliteDb &connection, Slice kv_name) TD_WARN_UNUSED_RESULT { - return connection.exec(PSLICE() << "DROP TABLE IF EXISTS " << kv_name); + static Status drop(SqliteDb &connection, Slice table_name) TD_WARN_UNUSED_RESULT { + return connection.exec(PSLICE() << "DROP TABLE IF EXISTS " << table_name); } - static Status init(SqliteDb &connection, Slice kv_name) TD_WARN_UNUSED_RESULT { - return connection.exec(PSLICE() << "CREATE TABLE IF NOT EXISTS " << kv_name << " (k BLOB PRIMARY KEY, v BLOB)"); + static Status init(SqliteDb &connection, Slice table_name) TD_WARN_UNUSED_RESULT { + return connection.exec(PSLICE() << "CREATE TABLE IF NOT EXISTS " << table_name << " (k BLOB PRIMARY KEY, v BLOB)"); } using SeqNo = uint64; - Result init(string name) TD_WARN_UNUSED_RESULT { - name_ = std::move(name); - bool is_created = false; - SqliteDb db; - TRY_STATUS(db.init(name, &is_created)); - TRY_STATUS(db.exec("PRAGMA encoding=\"UTF-8\"")); - TRY_STATUS(db.exec("PRAGMA synchronous=NORMAL")); - TRY_STATUS(db.exec("PRAGMA journal_mode=WAL")); - TRY_STATUS(db.exec("PRAGMA temp_store=MEMORY")); - TRY_STATUS(init_with_connection(std::move(db), "KV")); - return is_created; + + bool empty() const { + return db_.empty(); } - Status init_with_connection(SqliteDb connection, string kv_name) TD_WARN_UNUSED_RESULT { - db_ = std::move(connection); - kv_name_ = std::move(kv_name); - TRY_STATUS(init(db_, kv_name_)); + Result init(string path) TD_WARN_UNUSED_RESULT; - TRY_RESULT(set_stmt, db_.get_statement(PSLICE() << "REPLACE INTO " << kv_name_ << " (k, v) VALUES (?1, ?2)")); - set_stmt_ = std::move(set_stmt); - TRY_RESULT(get_stmt, db_.get_statement(PSLICE() << "SELECT v FROM " << kv_name_ << " WHERE k = ?1")); - get_stmt_ = std::move(get_stmt); - TRY_RESULT(erase_stmt, db_.get_statement(PSLICE() << "DELETE FROM " << kv_name_ << " WHERE k = ?1")); - erase_stmt_ = std::move(erase_stmt); - TRY_RESULT(get_all_stmt, db_.get_statement(PSLICE() << "SELECT k, v FROM " << kv_name_ << "")); - - TRY_RESULT(erase_by_prefix_stmt, - db_.get_statement(PSLICE() << "DELETE FROM " << kv_name_ << " WHERE ?1 <= k AND k < ?2")); - erase_by_prefix_stmt_ = std::move(erase_by_prefix_stmt); - - TRY_RESULT(erase_by_prefix_rare_stmt, - db_.get_statement(PSLICE() << "DELETE FROM " << kv_name_ << " WHERE ?1 <= k")); - erase_by_prefix_rare_stmt_ = std::move(erase_by_prefix_rare_stmt); - - TRY_RESULT(get_by_prefix_stmt, - db_.get_statement(PSLICE() << "SELECT k, v FROM " << kv_name_ << " WHERE ?1 <= k AND k < ?2")); - get_by_prefix_stmt_ = std::move(get_by_prefix_stmt); - - TRY_RESULT(get_by_prefix_rare_stmt, - db_.get_statement(PSLICE() << "SELECT k, v FROM " << kv_name_ << " WHERE ?1 <= k")); - get_by_prefix_rare_stmt_ = std::move(get_by_prefix_rare_stmt); - - get_all_stmt_ = std::move(get_all_stmt); - return Status::OK(); - } + Status init_with_connection(SqliteDb connection, string table_name) TD_WARN_UNUSED_RESULT; Result try_regenerate_index() TD_WARN_UNUSED_RESULT { return false; } + void close() { clear(); } - static Status destroy(Slice name) TD_WARN_UNUSED_RESULT { - return SqliteDb::destroy(name); - } - void close_and_destroy() { - drop(db_, kv_name_).ensure(); - auto name = std::move(name_); - clear(); - if (!name.empty()) { - SqliteDb::destroy(name).ignore(); - } + + static Status destroy(Slice path) TD_WARN_UNUSED_RESULT { + return SqliteDb::destroy(path); } - SeqNo set(Slice key, Slice value) { - set_stmt_.bind_blob(1, key).ensure(); - set_stmt_.bind_blob(2, value).ensure(); - set_stmt_.step().ensure(); - set_stmt_.reset(); - return 0; - } + void close_and_destroy(); - SeqNo erase(Slice key) { - erase_stmt_.bind_blob(1, key).ensure(); - erase_stmt_.step().ensure(); - erase_stmt_.reset(); - return 0; - } - string get(Slice key) { - SCOPE_EXIT { - get_stmt_.reset(); - }; - get_stmt_.bind_blob(1, key).ensure(); - get_stmt_.step().ensure(); - if (!get_stmt_.has_row()) { - return ""; - } - auto data = get_stmt_.view_blob(0).str(); - get_stmt_.step().ignore(); - return data; - } + SeqNo set(Slice key, Slice value); + + string get(Slice key); + + SeqNo erase(Slice key); Status begin_transaction() TD_WARN_UNUSED_RESULT { return db_.begin_transaction(); @@ -128,23 +64,7 @@ class SqliteKeyValue { return db_.commit_transaction(); } - void erase_by_prefix(Slice prefix) { - auto next = next_prefix(prefix); - if (next.empty()) { - SCOPE_EXIT { - erase_by_prefix_rare_stmt_.reset(); - }; - erase_by_prefix_rare_stmt_.bind_blob(1, prefix).ensure(); - erase_by_prefix_rare_stmt_.step().ensure(); - } else { - SCOPE_EXIT { - erase_by_prefix_stmt_.reset(); - }; - erase_by_prefix_stmt_.bind_blob(1, prefix).ensure(); - erase_by_prefix_stmt_.bind_blob(2, next).ensure(); - erase_by_prefix_stmt_.step().ensure(); - } - }; + void erase_by_prefix(Slice prefix); std::unordered_map get_all() { std::unordered_map res; @@ -160,6 +80,7 @@ class SqliteKeyValue { } get_by_range(prefix, next, callback); } + template void get_by_range(Slice from, Slice till, CallbackT &&callback) { SqliteStatement *stmt = nullptr; @@ -183,16 +104,13 @@ class SqliteKeyValue { } } - bool empty() const { - return db_.empty(); - } void clear() { *this = SqliteKeyValue(); } private: - string name_; // deprecated - string kv_name_; + string path_; + string table_name_; SqliteDb db_; SqliteStatement get_stmt_; SqliteStatement set_stmt_; @@ -203,20 +121,7 @@ class SqliteKeyValue { SqliteStatement get_by_prefix_stmt_; SqliteStatement get_by_prefix_rare_stmt_; - string next_prefix(Slice prefix) { - string next = prefix.str(); - size_t pos = next.size(); - while (pos) { - pos--; - auto value = static_cast(next[pos]); - value++; - next[pos] = static_cast(value); - if (value != 0) { - return next; - } - } - return string{}; - } + string next_prefix(Slice prefix); }; } // namespace td diff --git a/tdutils/td/utils/ScopeGuard.h b/tdutils/td/utils/ScopeGuard.h index a914ce35..b31198aa 100644 --- a/tdutils/td/utils/ScopeGuard.h +++ b/tdutils/td/utils/ScopeGuard.h @@ -14,6 +14,7 @@ #include namespace td { + class Guard { public: Guard() = default;