summaryrefslogtreecommitdiff
path: root/libs/tdlib/td/tddb/td/db/SqliteDb.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/tdlib/td/tddb/td/db/SqliteDb.cpp')
-rw-r--r--libs/tdlib/td/tddb/td/db/SqliteDb.cpp228
1 files changed, 0 insertions, 228 deletions
diff --git a/libs/tdlib/td/tddb/td/db/SqliteDb.cpp b/libs/tdlib/td/tddb/td/db/SqliteDb.cpp
deleted file mode 100644
index 819818197d..0000000000
--- a/libs/tdlib/td/tddb/td/db/SqliteDb.cpp
+++ /dev/null
@@ -1,228 +0,0 @@
-//
-// 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/SqliteDb.h"
-
-#include "td/utils/format.h"
-#include "td/utils/port/path.h"
-#include "td/utils/port/Stat.h"
-#include "td/utils/Status.h"
-#include "td/utils/StringBuilder.h"
-#include "td/utils/Timer.h"
-
-#include "sqlite/sqlite3.h"
-
-namespace td {
-
-namespace {
-string db_key_to_sqlcipher_key(const DbKey &db_key) {
- if (db_key.is_empty()) {
- return "''";
- }
- if (db_key.is_password()) {
- return PSTRING() << "'" << db_key.data().str() << "'";
- }
- CHECK(db_key.is_raw_key());
- Slice raw_key = db_key.data();
- CHECK(raw_key.size() == 32);
- size_t expected_size = 64 + 5;
- string res(expected_size + 50, ' ');
- StringBuilder sb(res);
- sb << '"';
- sb << 'x';
- sb << '\'';
- sb << format::as_hex_dump<0>(raw_key);
- sb << '\'';
- sb << '"';
- CHECK(!sb.is_error());
- CHECK(sb.as_cslice().size() == expected_size);
- res.resize(expected_size);
- return res;
-}
-} // namespace
-
-SqliteDb::~SqliteDb() = default;
-
-Status SqliteDb::init(CSlice path, bool *was_created) {
- // If database does not exist, delete all other files which may left
- // from older database
- bool is_db_exists = stat(path).is_ok();
- if (!is_db_exists) {
- destroy(path).ignore();
- }
-
- if (was_created != nullptr) {
- *was_created = !is_db_exists;
- }
- sqlite3 *db;
- CHECK(sqlite3_threadsafe() != 0);
- int rc = sqlite3_open_v2(path.c_str(), &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE /*| SQLITE_OPEN_SHAREDCACHE*/,
- nullptr);
- if (rc != SQLITE_OK) {
- auto res = Status::Error(PSLICE() << "Failed to open db: " << detail::RawSqliteDb::last_error(db));
- sqlite3_close(db);
- return res;
- }
- sqlite3_busy_timeout(db, 1000 * 5 /* 5 seconds */);
- raw_ = std::make_shared<detail::RawSqliteDb>(db, path.str());
- return Status::OK();
-}
-
-static void trace_callback(void *ptr, const char *query) {
- LOG(ERROR) << query;
-}
-static int trace_v2_callback(unsigned code, void *ctx, void *p_raw, void *x_raw) {
- CHECK(code == SQLITE_TRACE_STMT);
- auto x = static_cast<const char *>(x_raw);
- if (x[0] == '-' && x[1] == '-') {
- trace_callback(ctx, x);
- } else {
- trace_callback(ctx, sqlite3_expanded_sql(static_cast<sqlite3_stmt *>(p_raw)));
- }
-
- return 0;
-}
-void SqliteDb::trace(bool flag) {
- sqlite3_trace_v2(raw_->db(), SQLITE_TRACE_STMT, flag ? trace_v2_callback : nullptr, nullptr);
-}
-
-Status SqliteDb::exec(CSlice cmd) {
- CHECK(!empty());
- char *msg;
- VLOG(sqlite) << "Start exec " << tag("cmd", cmd) << tag("db", raw_->db());
- auto rc = sqlite3_exec(raw_->db(), cmd.c_str(), nullptr, nullptr, &msg);
- VLOG(sqlite) << "Finish exec " << tag("cmd", cmd) << tag("db", raw_->db());
- if (rc != SQLITE_OK) {
- CHECK(msg != nullptr);
- return Status::Error(PSLICE() << tag("query", cmd) << " failed: " << msg);
- }
- CHECK(msg == nullptr);
- return Status::OK();
-}
-
-Result<bool> SqliteDb::has_table(Slice table) {
- TRY_RESULT(stmt, get_statement(PSLICE() << "SELECT count(*) FROM sqlite_master WHERE type='table' AND name='" << table
- << "'"));
- TRY_STATUS(stmt.step());
- CHECK(stmt.has_row());
- auto cnt = stmt.view_int32(0);
- return cnt == 1;
-}
-Result<string> SqliteDb::get_pragma(Slice name) {
- TRY_RESULT(stmt, get_statement(PSLICE() << "PRAGMA " << name));
- TRY_STATUS(stmt.step());
- CHECK(stmt.has_row());
- auto res = stmt.view_blob(0).str();
- TRY_STATUS(stmt.step());
- CHECK(!stmt.can_step());
- return std::move(res);
-}
-
-Result<int32> SqliteDb::user_version() {
- TRY_RESULT(get_version_stmt, get_statement("PRAGMA user_version"));
- TRY_STATUS(get_version_stmt.step());
- if (!get_version_stmt.has_row()) {
- return Status::Error("PRAGMA user_version failed");
- }
- return get_version_stmt.view_int32(0);
-}
-
-Status SqliteDb::set_user_version(int32 version) {
- return exec(PSLICE() << "PRAGMA user_version = " << version);
-}
-
-Status SqliteDb::begin_transaction() {
- return exec("BEGIN");
-}
-Status SqliteDb::commit_transaction() {
- return exec("COMMIT");
-}
-
-bool SqliteDb::is_encrypted() {
- return exec("SELECT count(*) FROM sqlite_master").is_error();
-}
-
-Result<SqliteDb> SqliteDb::open_with_key(CSlice path, const DbKey &db_key) {
- SqliteDb db;
- TRY_STATUS(db.init(path));
- if (!db_key.is_empty()) {
- if (!db.is_encrypted()) {
- return Status::Error("No key is needed");
- }
- auto key = db_key_to_sqlcipher_key(db_key);
- TRY_STATUS(db.exec(PSLICE() << "PRAGMA key = " << key));
- }
- if (db.is_encrypted()) {
- return Status::Error("Wrong key");
- }
- return std::move(db);
-}
-
-Status SqliteDb::change_key(CSlice path, const DbKey &new_db_key, const DbKey &old_db_key) {
- // fast path
- {
- auto r_db = open_with_key(path, new_db_key);
- if (r_db.is_ok()) {
- return Status::OK();
- }
- }
-
- TRY_RESULT(db, open_with_key(path, old_db_key));
- TRY_RESULT(user_version, db.user_version());
- auto new_key = db_key_to_sqlcipher_key(new_db_key);
- if (old_db_key.is_empty() && !new_db_key.is_empty()) {
- LOG(DEBUG) << "ENCRYPT";
- // Encrypt
- PerfWarningTimer timer("Encrypt sqlite database", 0.1);
- auto tmp_path = path.str() + ".ecnrypted";
- unlink(tmp_path).ignore();
-
- // make shure that database is not empty
- TRY_STATUS(db.exec("CREATE TABLE IF NOT EXISTS encryption_dummy_table(id INT PRIMARY KEY)"));
- //NB: not really safe
- TRY_STATUS(db.exec(PSLICE() << "ATTACH DATABASE '" << tmp_path << "' AS encrypted KEY " << new_key));
- TRY_STATUS(db.exec("SELECT sqlcipher_export('encrypted')"));
- TRY_STATUS(db.exec(PSLICE() << "PRAGMA encrypted.user_version = " << user_version));
- TRY_STATUS(db.exec("DETACH DATABASE encrypted"));
- db.close();
- TRY_STATUS(rename(tmp_path, path));
- } else if (!old_db_key.is_empty() && new_db_key.is_empty()) {
- LOG(DEBUG) << "DECRYPT";
- // Dectypt
- PerfWarningTimer timer("Decrypt sqlite database", 0.1);
- auto tmp_path = path.str() + ".ecnrypted";
- unlink(tmp_path).ignore();
-
- //NB: not really safe
- TRY_STATUS(db.exec(PSLICE() << "ATTACH DATABASE '" << tmp_path << "' AS decrypted KEY ''"));
- TRY_STATUS(db.exec("SELECT sqlcipher_export('decrypted')"));
- TRY_STATUS(db.exec(PSLICE() << "PRAGMA decrypted.user_version = " << user_version));
- TRY_STATUS(db.exec("DETACH DATABASE decrypted"));
- db.close();
- TRY_STATUS(rename(tmp_path, path));
- } else {
- LOG(DEBUG) << "REKEY";
- PerfWarningTimer timer("Rekey sqlite database", 0.1);
- TRY_STATUS(db.exec(PSLICE() << "PRAGMA rekey = " << new_key));
- }
-
- TRY_RESULT(new_db, open_with_key(path, new_db_key));
- CHECK(new_db.user_version().ok() == user_version);
- return Status::OK();
-}
-Status SqliteDb::destroy(Slice path) {
- return detail::RawSqliteDb::destroy(path);
-}
-
-Result<SqliteStatement> SqliteDb::get_statement(CSlice statement) {
- sqlite3_stmt *stmt = nullptr;
- auto rc = sqlite3_prepare_v2(get_native(), statement.c_str(), static_cast<int>(statement.size()) + 1, &stmt, nullptr);
- if (rc != SQLITE_OK) {
- return Status::Error(PSLICE() << "Failed to prepare sqlite " << tag("stmt", statement) << raw_->last_error());
- }
- return SqliteStatement(stmt, raw_);
-}
-} // namespace td