From b63e2a8fe03bdcbc79cec08e8874f15549cce6ce Mon Sep 17 00:00:00 2001 From: Christian Speckner Date: Tue, 29 Dec 2020 21:50:30 +0100 Subject: [PATCH] Say hello to composite key value repo. --- .vscode/settings.json | 3 +- .../CompositeKeyValueRepository.hxx | 35 ++++++ .../AbstractKeyValueRepositorySqlite.cxx | 80 ++++++++++++++ .../AbstractKeyValueRepositorySqlite.hxx | 26 +++++ .../CompositeKeyValueRepositorySqlite.cxx | 100 ++++++++++++++++++ .../CompositeKeyValueRepositorySqlite.hxx | 81 ++++++++++++++ .../sqlite/KeyValueRepositorySqlite.cxx | 60 ++--------- .../sqlite/KeyValueRepositorySqlite.hxx | 18 ++-- .../repository/sqlite/SqliteStatement.cxx | 7 ++ .../repository/sqlite/SqliteStatement.hxx | 2 + src/common/repository/sqlite/module.mk | 2 + 11 files changed, 354 insertions(+), 60 deletions(-) create mode 100644 src/common/repository/CompositeKeyValueRepository.hxx create mode 100644 src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.cxx create mode 100644 src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.hxx create mode 100644 src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.cxx create mode 100644 src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.hxx diff --git a/.vscode/settings.json b/.vscode/settings.json index a55c4e90c..696a71474 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -98,6 +98,7 @@ "forward_list": "cpp", "regex": "cpp", "valarray": "cpp", - "ranges": "cpp" + "ranges": "cpp", + "stop_token": "cpp" } } diff --git a/src/common/repository/CompositeKeyValueRepository.hxx b/src/common/repository/CompositeKeyValueRepository.hxx new file mode 100644 index 000000000..d63d61f44 --- /dev/null +++ b/src/common/repository/CompositeKeyValueRepository.hxx @@ -0,0 +1,35 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#ifndef COMPOSITE_KEY_VALUE_REPOSITORY_HXX +#define COMPOSITE_KEY_VALUE_REPOSITORY_HXX + +#include "KeyValueRepository.hxx" +#include "bspf.hxx" + +class CompositeKeyValueRepository +{ + public: + + virtual ~CompositeKeyValueRepository() = default; + + virtual shared_ptr get(const string& key) = 0; + + virtual bool has(const string& key) = 0; +}; + +#endif // COMPOSITE_KEY_VALUE_REPOSITORY_HXX diff --git a/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.cxx b/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.cxx new file mode 100644 index 000000000..398338b51 --- /dev/null +++ b/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.cxx @@ -0,0 +1,80 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#include "AbstractKeyValueRepositorySqlite.hxx" +#include "Logger.hxx" +#include "SqliteError.hxx" +#include "SqliteTransaction.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +std::map AbstractKeyValueRepositorySqlite::load() +{ + std::map values; + + try { + SqliteStatement& stmt{stmtSelect()}; + + while (stmt.step()) + values[stmt.columnText(0)] = stmt.columnText(1); + + stmt.reset(); + } + catch (const SqliteError& err) { + Logger::info(err.what()); + } + + return values; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void AbstractKeyValueRepositorySqlite::save(const std::map& values) +{ + try { + SqliteTransaction tx{database()}; + + for (const auto& pair: values) { + SqliteStatement& stmt{stmtInsert(pair.first, pair.second.toString())}; + + stmt.step(); + stmt.reset(); + } + + tx.commit(); + } + catch (const SqliteError& err) { + Logger::info(err.what()); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void AbstractKeyValueRepositorySqlite::save(const string& key, const Variant& value) +{ + try { + SqliteStatement& stmt{stmtInsert(key, value.toString())}; + + stmt + .bind(1, key.c_str()) + .bind(2, value.toCString()) + .step(); + + stmt.reset(); + } + catch (const SqliteError& err) { + Logger::info(err.what()); + } +} + diff --git a/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.hxx b/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.hxx new file mode 100644 index 000000000..00d327fc0 --- /dev/null +++ b/src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.hxx @@ -0,0 +1,26 @@ +#ifndef ABSTRACT_KEY_VALUE_REPOSITORY_SQLITE_HXX +#define ABSTRACT_KEY_VALUE_REPOSITORY_SQLITE_HXX + +#include "bspf.hxx" +#include "repository/KeyValueRepository.hxx" +#include "SqliteDatabase.hxx" +#include "SqliteStatement.hxx" + +class AbstractKeyValueRepositorySqlite : public KeyValueRepository +{ + public: + + std::map load() override; + + void save(const std::map& values) override; + + void save(const string& key, const Variant& value) override; + + protected: + + virtual SqliteStatement& stmtInsert(const string& key, const string& value) = 0; + virtual SqliteStatement& stmtSelect() = 0; + virtual SqliteDatabase& database() = 0; +}; + +#endif // ABSTRACT_KEY_VALUE_REPOSITORY_SQLITE_HXX diff --git a/src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.cxx b/src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.cxx new file mode 100644 index 000000000..928459ea9 --- /dev/null +++ b/src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.cxx @@ -0,0 +1,100 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#include "CompositeKeyValueRepositorySqlite.hxx" +#include "Logger.hxx" +#include "SqliteError.hxx" +#include "bspf.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CompositeKeyValueRepositorySqlite::CompositeKeyValueRepositorySqlite(SqliteDatabase& db, const string& tableName) + : myTableName{tableName}, myDb{db} +{} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +shared_ptr CompositeKeyValueRepositorySqlite::get(const string& key) +{ + return make_shared(*this, key); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CompositeKeyValueRepositorySqlite::has(const string& key) +{ + try { + myStmtCount->reset(); + myStmtCount->bind(1, key.c_str()); + + if (!myStmtCount->step()) + throw new SqliteError("count failed"); + + Int32 rowCount = myStmtCount->columnInt(0); + + myStmtCount->reset(); + + return rowCount > 0; + } catch (const SqliteError& err) { + Logger::info(err.what()); + + return false; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CompositeKeyValueRepositorySqlite::initialize() +{ + myDb.exec( + "CREATE TABLE IF NOT EXISTS `" + myTableName + "` (`key1` TEXT, `key2` TEXT, `value` TEXT, PRIMARY KEY (`key1`, `key2`)) WITHOUT ROWID" + ); + + myStmtInsert = make_unique(myDb, "INSERT OR REPLACE INTO `" + myTableName + "` VALUES (?, ?, ?)"); + myStmtSelect = make_unique(myDb, "SELECT `key2`, `VALUE` FROM `" + myTableName + "` WHERE `key1` = ?"); + myStmtCount = make_unique(myDb, "SELECT COUNT(*) FROM `" + myTableName + "` WHERE `key1` = ?"); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CompositeKeyValueRepositorySqlite::ProxyRepository::ProxyRepository( + const CompositeKeyValueRepositorySqlite& repo, + const string& key +) : myRepo(repo), myKey(key) +{} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +SqliteStatement& CompositeKeyValueRepositorySqlite::ProxyRepository::stmtInsert( + const string& key, const string& value +) { + myRepo.myStmtInsert->reset(); + + return (*myRepo.myStmtInsert) + .bind(1, myKey.c_str()) + .bind(2, key.c_str()) + .bind(3, value.c_str()); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +SqliteStatement& CompositeKeyValueRepositorySqlite::ProxyRepository::stmtSelect() +{ + myRepo.myStmtSelect->reset(); + + return (*myRepo.myStmtSelect) + .bind(1, myKey.c_str()); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +SqliteDatabase& CompositeKeyValueRepositorySqlite::ProxyRepository::database() +{ + return myRepo.myDb; +} diff --git a/src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.hxx b/src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.hxx new file mode 100644 index 000000000..61e3a6889 --- /dev/null +++ b/src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.hxx @@ -0,0 +1,81 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#ifndef COMPOSITE_KEY_VALUE_REPOSITORY_SQLITE_HXX +#define COMPOSITE_KEY_VALUE_REPOSITORY_SQLITE_HXX + +#include "repository/CompositeKeyValueRepository.hxx" + +#include "SqliteDatabase.hxx" +#include "SqliteStatement.hxx" +#include "AbstractKeyValueRepositorySqlite.hxx" + +class CompositeKeyValueRepositorySqlite : public CompositeKeyValueRepository { + public: + + CompositeKeyValueRepositorySqlite(SqliteDatabase& db, const string& tableName); + + shared_ptr get(const string& key) override; + + bool has(const string& key) override; + + void initialize(); + + private: + + class ProxyRepository : public AbstractKeyValueRepositorySqlite { + public: + + ProxyRepository(const CompositeKeyValueRepositorySqlite& repo, const string& key); + + protected: + + SqliteStatement& stmtInsert(const string& key, const string& value) override; + SqliteStatement& stmtSelect() override; + SqliteDatabase& database() override; + + private: + + const CompositeKeyValueRepositorySqlite& myRepo; + const string myKey; + + private: + + ProxyRepository(const ProxyRepository&) = delete; + ProxyRepository(ProxyRepository&&) = delete; + ProxyRepository& operator=(const ProxyRepository&) = delete; + ProxyRepository& operator=(ProxyRepository&&) = delete; + }; + + private: + + string myTableName; + SqliteDatabase& myDb; + + unique_ptr myStmtInsert; + unique_ptr myStmtSelect; + unique_ptr myStmtCount; + + private: + + CompositeKeyValueRepositorySqlite(const CompositeKeyValueRepositorySqlite&) = delete; + CompositeKeyValueRepositorySqlite(CompositeKeyValueRepositorySqlite&&) = delete; + CompositeKeyValueRepositorySqlite& operator=(const CompositeKeyValueRepositorySqlite&) = delete; + CompositeKeyValueRepositorySqlite& operator=(CompositeKeyValueRepositorySqlite&&) = delete; +}; + +#endif // COMPOSITE_KEY_VALUE_REPOSITORY_SQLITE_HXX diff --git a/src/common/repository/sqlite/KeyValueRepositorySqlite.cxx b/src/common/repository/sqlite/KeyValueRepositorySqlite.cxx index c871521b3..b2330871b 100644 --- a/src/common/repository/sqlite/KeyValueRepositorySqlite.cxx +++ b/src/common/repository/sqlite/KeyValueRepositorySqlite.cxx @@ -17,8 +17,6 @@ #include "KeyValueRepositorySqlite.hxx" #include "Logger.hxx" -#include "SqliteError.hxx" -#include "SqliteTransaction.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - KeyValueRepositorySqlite::KeyValueRepositorySqlite( @@ -29,65 +27,27 @@ KeyValueRepositorySqlite::KeyValueRepositorySqlite( } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -std::map KeyValueRepositorySqlite::load() +SqliteStatement& KeyValueRepositorySqlite::stmtInsert(const string& key, const string& value) { - std::map values; + myStmtInsert->reset(); - try { - myStmtSelect->reset(); - - while (myStmtSelect->step()) - values[myStmtSelect->columnText(0)] = myStmtSelect->columnText(1); - - myStmtSelect->reset(); - } - catch (const SqliteError& err) { - Logger::info(err.what()); - } - - return values; + return (*myStmtInsert) + .bind(1, key.c_str()) + .bind(2, value.c_str()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void KeyValueRepositorySqlite::save(const std::map& values) +SqliteStatement& KeyValueRepositorySqlite::stmtSelect() { - try { - SqliteTransaction tx(myDb); + myStmtInsert->reset(); - myStmtInsert->reset(); - - for (const auto& pair: values) { - (*myStmtInsert) - .bind(1, pair.first.c_str()) - .bind(2, pair.second.toCString()) - .step(); - - myStmtInsert->reset(); - } - - tx.commit(); - } - catch (const SqliteError& err) { - Logger::info(err.what()); - } + return *myStmtSelect; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void KeyValueRepositorySqlite::save(const string& key, const Variant& value) +SqliteDatabase& KeyValueRepositorySqlite::database() { - try { - myStmtInsert->reset(); - - (*myStmtInsert) - .bind(1, key.c_str()) - .bind(2, value.toCString()) - .step(); - - myStmtInsert->reset(); - } - catch (const SqliteError& err) { - Logger::info(err.what()); - } + return myDb; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/common/repository/sqlite/KeyValueRepositorySqlite.hxx b/src/common/repository/sqlite/KeyValueRepositorySqlite.hxx index bbbea6daf..46e7a532c 100644 --- a/src/common/repository/sqlite/KeyValueRepositorySqlite.hxx +++ b/src/common/repository/sqlite/KeyValueRepositorySqlite.hxx @@ -19,24 +19,24 @@ #define KEY_VALUE_REPOSITORY_SQLITE_HXX #include "bspf.hxx" -#include "repository/KeyValueRepository.hxx" +#include "AbstractKeyValueRepositorySqlite.hxx" #include "SqliteDatabase.hxx" #include "SqliteStatement.hxx" -class KeyValueRepositorySqlite : public KeyValueRepository +class KeyValueRepositorySqlite : public AbstractKeyValueRepositorySqlite { public: KeyValueRepositorySqlite(SqliteDatabase& db, const string& tableName); - std::map load() override; - - void save(const std::map& values) override; - - void save(const string& key, const Variant& value) override; - void initialize(); + protected: + + SqliteStatement& stmtInsert(const string& key, const string& value) override; + SqliteStatement& stmtSelect() override; + SqliteDatabase& database() override; + private: string myTableName; @@ -50,7 +50,7 @@ class KeyValueRepositorySqlite : public KeyValueRepository KeyValueRepositorySqlite(const KeyValueRepositorySqlite&) = delete; KeyValueRepositorySqlite(KeyValueRepositorySqlite&&) = delete; KeyValueRepositorySqlite& operator=(const KeyValueRepositorySqlite&) = delete; - KeyValueRepositorySqlite operator=(KeyValueRepositorySqlite&&) = delete; + KeyValueRepositorySqlite& operator=(KeyValueRepositorySqlite&&) = delete; }; #endif // KEY_VALUE_REPOSITORY_SQLITE_HXX diff --git a/src/common/repository/sqlite/SqliteStatement.cxx b/src/common/repository/sqlite/SqliteStatement.cxx index 87dc25d34..4c2c4d513 100644 --- a/src/common/repository/sqlite/SqliteStatement.cxx +++ b/src/common/repository/sqlite/SqliteStatement.cxx @@ -61,3 +61,10 @@ string SqliteStatement::columnText(int index) const { return reinterpret_cast(sqlite3_column_text(myStmt, index)); } + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int SqliteStatement::columnInt(int index) const +{ + return sqlite3_column_int(myStmt, index); +} diff --git a/src/common/repository/sqlite/SqliteStatement.hxx b/src/common/repository/sqlite/SqliteStatement.hxx index c1d50ca11..07ad29f3d 100644 --- a/src/common/repository/sqlite/SqliteStatement.hxx +++ b/src/common/repository/sqlite/SqliteStatement.hxx @@ -39,6 +39,8 @@ class SqliteStatement { string columnText(int index) const; + Int32 columnInt(int index) const; + private: sqlite3_stmt* myStmt{nullptr}; diff --git a/src/common/repository/sqlite/module.mk b/src/common/repository/sqlite/module.mk index 0c2192960..bc814bb12 100644 --- a/src/common/repository/sqlite/module.mk +++ b/src/common/repository/sqlite/module.mk @@ -1,7 +1,9 @@ MODULE := src/common/repository/sqlite MODULE_OBJS := \ + src/common/repository/sqlite/AbstractKeyValueRepositorySqlite.o \ src/common/repository/sqlite/KeyValueRepositorySqlite.o \ + src/common/repository/sqlite/CompositeKeyValueRepositorySqlite.o \ src/common/repository/sqlite/SettingsDb.o \ src/common/repository/sqlite/SqliteDatabase.o \ src/common/repository/sqlite/SqliteStatement.o \