Add SqliteKeyValue.cpp.
GitOrigin-RevId: d0490b00c3342db191a29e7780d3292e3b593896
This commit is contained in:
parent
a2d725f484
commit
e9568c9a8e
@ -10,6 +10,7 @@ set(TDDB_SOURCE
|
|||||||
|
|
||||||
td/db/SqliteDb.cpp
|
td/db/SqliteDb.cpp
|
||||||
td/db/SqliteStatement.cpp
|
td/db/SqliteStatement.cpp
|
||||||
|
td/db/SqliteKeyValue.cpp
|
||||||
td/db/SqliteKeyValueAsync.cpp
|
td/db/SqliteKeyValueAsync.cpp
|
||||||
|
|
||||||
td/db/detail/RawSqliteDb.cpp
|
td/db/detail/RawSqliteDb.cpp
|
||||||
|
130
tddb/td/db/SqliteKeyValue.cpp
Normal file
130
tddb/td/db/SqliteKeyValue.cpp
Normal file
@ -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<bool> 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<uint8>(next[pos]);
|
||||||
|
value++;
|
||||||
|
next[pos] = static_cast<char>(value);
|
||||||
|
if (value != 0) {
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string{};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace td
|
@ -10,7 +10,6 @@
|
|||||||
#include "td/db/SqliteStatement.h"
|
#include "td/db/SqliteStatement.h"
|
||||||
|
|
||||||
#include "td/utils/logging.h"
|
#include "td/utils/logging.h"
|
||||||
#include "td/utils/ScopeGuard.h"
|
|
||||||
#include "td/utils/Slice.h"
|
#include "td/utils/Slice.h"
|
||||||
#include "td/utils/Status.h"
|
#include "td/utils/Status.h"
|
||||||
|
|
||||||
@ -20,106 +19,43 @@ namespace td {
|
|||||||
|
|
||||||
class SqliteKeyValue {
|
class SqliteKeyValue {
|
||||||
public:
|
public:
|
||||||
static Status drop(SqliteDb &connection, Slice kv_name) TD_WARN_UNUSED_RESULT {
|
static Status drop(SqliteDb &connection, Slice table_name) TD_WARN_UNUSED_RESULT {
|
||||||
return connection.exec(PSLICE() << "DROP TABLE IF EXISTS " << kv_name);
|
return connection.exec(PSLICE() << "DROP TABLE IF EXISTS " << table_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Status init(SqliteDb &connection, Slice kv_name) TD_WARN_UNUSED_RESULT {
|
static Status init(SqliteDb &connection, Slice table_name) TD_WARN_UNUSED_RESULT {
|
||||||
return connection.exec(PSLICE() << "CREATE TABLE IF NOT EXISTS " << kv_name << " (k BLOB PRIMARY KEY, v BLOB)");
|
return connection.exec(PSLICE() << "CREATE TABLE IF NOT EXISTS " << table_name << " (k BLOB PRIMARY KEY, v BLOB)");
|
||||||
}
|
}
|
||||||
|
|
||||||
using SeqNo = uint64;
|
using SeqNo = uint64;
|
||||||
Result<bool> init(string name) TD_WARN_UNUSED_RESULT {
|
|
||||||
name_ = std::move(name);
|
bool empty() const {
|
||||||
bool is_created = false;
|
return db_.empty();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Status init_with_connection(SqliteDb connection, string kv_name) TD_WARN_UNUSED_RESULT {
|
Result<bool> init(string path) TD_WARN_UNUSED_RESULT;
|
||||||
db_ = std::move(connection);
|
|
||||||
kv_name_ = std::move(kv_name);
|
|
||||||
TRY_STATUS(init(db_, kv_name_));
|
|
||||||
|
|
||||||
TRY_RESULT(set_stmt, db_.get_statement(PSLICE() << "REPLACE INTO " << kv_name_ << " (k, v) VALUES (?1, ?2)"));
|
Status init_with_connection(SqliteDb connection, string table_name) TD_WARN_UNUSED_RESULT;
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<bool> try_regenerate_index() TD_WARN_UNUSED_RESULT {
|
Result<bool> try_regenerate_index() TD_WARN_UNUSED_RESULT {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
static Status destroy(Slice name) TD_WARN_UNUSED_RESULT {
|
|
||||||
return SqliteDb::destroy(name);
|
static Status destroy(Slice path) TD_WARN_UNUSED_RESULT {
|
||||||
}
|
return SqliteDb::destroy(path);
|
||||||
void close_and_destroy() {
|
|
||||||
drop(db_, kv_name_).ensure();
|
|
||||||
auto name = std::move(name_);
|
|
||||||
clear();
|
|
||||||
if (!name.empty()) {
|
|
||||||
SqliteDb::destroy(name).ignore();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SeqNo set(Slice key, Slice value) {
|
void close_and_destroy();
|
||||||
set_stmt_.bind_blob(1, key).ensure();
|
|
||||||
set_stmt_.bind_blob(2, value).ensure();
|
|
||||||
set_stmt_.step().ensure();
|
|
||||||
set_stmt_.reset();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SeqNo erase(Slice key) {
|
SeqNo set(Slice key, Slice value);
|
||||||
erase_stmt_.bind_blob(1, key).ensure();
|
|
||||||
erase_stmt_.step().ensure();
|
string get(Slice key);
|
||||||
erase_stmt_.reset();
|
|
||||||
return 0;
|
SeqNo erase(Slice key);
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
Status begin_transaction() TD_WARN_UNUSED_RESULT {
|
Status begin_transaction() TD_WARN_UNUSED_RESULT {
|
||||||
return db_.begin_transaction();
|
return db_.begin_transaction();
|
||||||
@ -128,23 +64,7 @@ class SqliteKeyValue {
|
|||||||
return db_.commit_transaction();
|
return db_.commit_transaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
void erase_by_prefix(Slice prefix) {
|
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();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unordered_map<string, string> get_all() {
|
std::unordered_map<string, string> get_all() {
|
||||||
std::unordered_map<string, string> res;
|
std::unordered_map<string, string> res;
|
||||||
@ -160,6 +80,7 @@ class SqliteKeyValue {
|
|||||||
}
|
}
|
||||||
get_by_range(prefix, next, callback);
|
get_by_range(prefix, next, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class CallbackT>
|
template <class CallbackT>
|
||||||
void get_by_range(Slice from, Slice till, CallbackT &&callback) {
|
void get_by_range(Slice from, Slice till, CallbackT &&callback) {
|
||||||
SqliteStatement *stmt = nullptr;
|
SqliteStatement *stmt = nullptr;
|
||||||
@ -183,16 +104,13 @@ class SqliteKeyValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool empty() const {
|
|
||||||
return db_.empty();
|
|
||||||
}
|
|
||||||
void clear() {
|
void clear() {
|
||||||
*this = SqliteKeyValue();
|
*this = SqliteKeyValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
string name_; // deprecated
|
string path_;
|
||||||
string kv_name_;
|
string table_name_;
|
||||||
SqliteDb db_;
|
SqliteDb db_;
|
||||||
SqliteStatement get_stmt_;
|
SqliteStatement get_stmt_;
|
||||||
SqliteStatement set_stmt_;
|
SqliteStatement set_stmt_;
|
||||||
@ -203,20 +121,7 @@ class SqliteKeyValue {
|
|||||||
SqliteStatement get_by_prefix_stmt_;
|
SqliteStatement get_by_prefix_stmt_;
|
||||||
SqliteStatement get_by_prefix_rare_stmt_;
|
SqliteStatement get_by_prefix_rare_stmt_;
|
||||||
|
|
||||||
string next_prefix(Slice prefix) {
|
string next_prefix(Slice prefix);
|
||||||
string next = prefix.str();
|
|
||||||
size_t pos = next.size();
|
|
||||||
while (pos) {
|
|
||||||
pos--;
|
|
||||||
auto value = static_cast<uint8>(next[pos]);
|
|
||||||
value++;
|
|
||||||
next[pos] = static_cast<char>(value);
|
|
||||||
if (value != 0) {
|
|
||||||
return next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string{};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace td
|
} // namespace td
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace td {
|
namespace td {
|
||||||
|
|
||||||
class Guard {
|
class Guard {
|
||||||
public:
|
public:
|
||||||
Guard() = default;
|
Guard() = default;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user