Skip to content
Snippets Groups Projects
sqlite_storage.cc 2.69 KiB
Newer Older
  • Learn to ignore specific revisions
  • Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
    #include <vereign/kvstore/sqlite_storage.hh>
    
    
    #include <vereign/kvstore/errors.hh>
    
    #include <vereign/kvstore/lock.hh>
    
    #include <vereign/encoding/binary.hh>
    
    #include <boost/optional.hpp>
    #include <vereign/core/lock_guard.hh>
    
    #include <vereign/sqlite/errors.hh>
    #include <sqlite3.h>
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
    
    #include <array>
    
    
    namespace {
    constexpr int createTableRetryCount = 10;
    constexpr auto createTableRetrySleep = std::chrono::milliseconds{1000};
    }
    
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
    namespace vereign::kvstore {
    
    
    SqliteStorage::SqliteStorage(const std::string& db_path)
      : db_{db_path}
    {
      kvstore::Lock l{*this, createTableRetryCount, createTableRetrySleep};
    
      db_.Execute(R"(
    CREATE TABLE IF NOT EXISTS storage (
      key TEXT PRIMARY KEY NOT NULL,
      value BLOB
    );
      )");
    }
    
    SqliteStorage::~SqliteStorage() {
      if (lock_count_ != 0) {
        db_.Commit();
    
    void SqliteStorage::Lock() {
      if (lock_count_ != 0) {
        lock_count_++;
    
      try {
        db_.BeginExplicitTransaction();
        lock_count_++;
      } catch (const sqlite::Error& err) {
        if (err.code() == SQLITE_BUSY) {
          throw LockError{};
    
    void SqliteStorage::Unlock() {
      if (lock_count_ == 0) {
        throw Error{"unexpected call Unlock with non existent lock"};
      }
    
      lock_count_--;
      if (lock_count_ == 0) {
        db_.Commit();
      }
    
    void SqliteStorage::DeleteAll() {
      kvstore::Lock l{*this};
    
      db_.Execute("DELETE FROM storage;");
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
    void SqliteStorage::PutBytes(const std::string& key, bytes::View value) {
    
      kvstore::Lock l{*this};
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
    
      auto stmt = db_.Prepare("REPLACE INTO storage(key, value) VALUES(?, ?);");
      stmt.BindText(1, key);
      stmt.BindBlob(2, value);
      stmt.Step();
    }
    
    void SqliteStorage::GetBytes(const std::string& key, bytes::Buffer& value) {
    
      kvstore::Lock l{*this};
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
    
      auto stmt = db_.Prepare("SELECT value FROM storage WHERE key = ?");
      stmt.BindText(1, key);
      auto end = stmt.Step();
    
      if (!end) {
        value.Write(stmt.GetColumnBlob(0));
      }
    }
    
    void SqliteStorage::PutInt64(const std::string& key, int64_t value) {
    
      kvstore::Lock l{*this};
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      std::array<uint8_t, 8> encoded;
      encoding::binary::EncodeUint64(encoded.data(), value);
    
      auto stmt = db_.Prepare("REPLACE INTO storage(key, value) VALUES(?, ?);");
      stmt.BindText(1, key);
      stmt.BindBlob(2, bytes::View(encoded.data(), 8));
      stmt.Step();
    }
    
    auto SqliteStorage::GetInt64(const std::string& key) -> int64_t {
    
      kvstore::Lock l{*this};
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
    
      auto stmt = db_.Prepare("SELECT value FROM storage WHERE key = ?");
      stmt.BindText(1, key);
      auto end = stmt.Step();
    
      if (end) {
        return 0;
      }
    
      auto buf = stmt.GetColumnBlob(0);
      if (buf.Size() != 8) {
    
        throw Error("cannot decode bytes size");
    
    Daniel Lyubomirov's avatar
    Daniel Lyubomirov committed
      }
    
      int64_t result = encoding::binary::DecodeUint64(buf);
    
      return result;
    }
    
    } // namespace vereign::kvstore