From 813c5304778b3f43e3ccbe17c96d979328b21b6d Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Mon, 11 Aug 2014 12:54:12 +0200 Subject: [PATCH] Storing serializable types in KVStore [Bug/Feature] One can store many types of values in KVStore [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: If656263cd7da1c558290a0baf5e6e44f88e9b865 --- packaging/libConfig.spec | 1 - src/config/kvstore.cpp | 83 ++++++++++++++------------ src/config/kvstore.hpp | 122 ++++++++++++++++++++++++++++---------- src/config/sqlite3/connection.hpp | 1 + 4 files changed, 136 insertions(+), 71 deletions(-) diff --git a/packaging/libConfig.spec b/packaging/libConfig.spec index 99c1dd5..5219bac 100644 --- a/packaging/libConfig.spec +++ b/packaging/libConfig.spec @@ -11,7 +11,6 @@ Summary: Config library BuildRequires: cmake BuildRequires: pkgconfig(sqlite3) - %description The package provides libConfig library. diff --git a/src/config/kvstore.cpp b/src/config/kvstore.cpp index f48ee6f..cc174e4 100644 --- a/src/config/kvstore.cpp +++ b/src/config/kvstore.cpp @@ -22,13 +22,14 @@ * @brief Definition of a class for key-value storage in a sqlite3 database */ +#include "config.hpp" #include "config/kvstore.hpp" #include "config/exception.hpp" +#include #include #include -#include #include namespace config { @@ -46,12 +47,26 @@ struct Transaction { } ~Transaction() { - mConnRef.exec("COMMIT TRANSACTION"); + if (std::uncaught_exception()) { + mConnRef.exec("ROLLBACK TRANSACTION"); + } else { + mConnRef.exec("COMMIT TRANSACTION"); + } } private: sqlite3::Connection& mConnRef; }; +struct ScopedReset { + ScopedReset(std::unique_ptr& stmtPtr) + : mStmtPtr(stmtPtr) {} + ~ScopedReset() + { + mStmtPtr->reset(); + } +private: + std::unique_ptr& mStmtPtr; +}; std::string escape(const std::string& in) { @@ -85,9 +100,9 @@ std::string escape(const std::string& in) } return out; } - } // namespace + KVStore::KVStore(const std::string& path) : mConn(path) { @@ -97,24 +112,13 @@ KVStore::KVStore(const std::string& path) KVStore::~KVStore() { - } void KVStore::setupDb() { Lock lock(mConnMtx); - const std::string setupScript = R"setupScript( - BEGIN EXCLUSIVE TRANSACTION; - - CREATE TABLE IF NOT EXISTS data ( - key TEXT PRIMARY KEY, - value TEXT NOT NULL - ); - - COMMIT TRANSACTION; - )setupScript"; - - mConn.exec(setupScript); + Transaction transaction(mConn); + mConn.exec("CREATE TABLE IF NOT EXISTS data (key TEXT PRIMARY KEY, value TEXT NOT NULL)"); } void KVStore::prepareStatements() @@ -131,7 +135,6 @@ void KVStore::prepareStatements() new sqlite3::Statement(mConn, "INSERT OR REPLACE INTO data (key, value) VALUES (?,?)")); mRemoveValuesStmt.reset( new sqlite3::Statement(mConn, "DELETE FROM data WHERE key GLOB ? ||'*' ")); - } void KVStore::clear() @@ -143,7 +146,8 @@ void KVStore::clear() unsigned int KVStore::size() { - mGetSizeStmt->reset(); + Lock lock(mConnMtx); + ScopedReset scopedReset(mGetSizeStmt); if (::sqlite3_step(mGetSizeStmt->get()) != SQLITE_ROW) { throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); @@ -161,9 +165,9 @@ unsigned int KVStore::count(const std::string& key) unsigned int KVStore::countInternal(const std::string& key) { - mGetValueCountStmt->reset(); + ScopedReset scopedReset(mGetValueCountStmt); - ::sqlite3_bind_text(mGetValueCountStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, 0); + ::sqlite3_bind_text(mGetValueCountStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT); if (::sqlite3_step(mGetValueCountStmt->get()) != SQLITE_ROW) { throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); @@ -181,34 +185,36 @@ void KVStore::remove(const std::string& key) void KVStore::removeInternal(const std::string& key) { - mRemoveValuesStmt->reset(); - ::sqlite3_bind_text(mRemoveValuesStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, 0); + ScopedReset scopedReset(mRemoveValuesStmt); + + ::sqlite3_bind_text(mRemoveValuesStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC); if (::sqlite3_step(mRemoveValuesStmt->get()) != SQLITE_DONE) { throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); } } -void KVStore::set(const std::string& key, const std::string& value) +void KVStore::setInternal(const std::string& key, const std::string& value) { Lock lock(mConnMtx); - mSetValueStmt->reset(); - ::sqlite3_bind_text(mSetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, 0); - ::sqlite3_bind_text(mSetValueStmt->get(), 2, value.c_str(), AUTO_DETERM_SIZE, 0); + ::sqlite3_bind_text(mSetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC); + ::sqlite3_bind_text(mSetValueStmt->get(), 2, value.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC); Transaction transaction(mConn); + ScopedReset scopedReset(mSetValueStmt); + if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) { throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); } } -void KVStore::set(const std::string& key, const std::initializer_list& values) +void KVStore::setInternal(const std::string& key, const std::initializer_list& values) { - set(key, std::vector(values)); + setInternal(key, std::vector(values)); } -void KVStore::set(const std::string& key, const std::vector& values) +void KVStore::setInternal(const std::string& key, const std::vector& values) { if (values.size() > std::numeric_limits::max()) { throw ConfigException("Too many values to insert"); @@ -220,11 +226,11 @@ void KVStore::set(const std::string& key, const std::vector& values removeInternal(key); for (unsigned int i = 0; i < values.size(); ++i) { - mSetValueStmt->reset(); + ScopedReset scopedReset(mSetValueStmt); const std::string modifiedKey = key + "." + std::to_string(i);; - ::sqlite3_bind_text(mSetValueStmt->get(), 1, modifiedKey.c_str(), AUTO_DETERM_SIZE, 0); - ::sqlite3_bind_text(mSetValueStmt->get(), 2, values[i].c_str(), AUTO_DETERM_SIZE, 0); + ::sqlite3_bind_text(mSetValueStmt->get(), 1, modifiedKey.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC); + ::sqlite3_bind_text(mSetValueStmt->get(), 2, values[i].c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC); if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) { throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); @@ -232,14 +238,14 @@ void KVStore::set(const std::string& key, const std::vector& values } } -std::string KVStore::get(const std::string& key) +std::string KVStore::getInternal(const std::string& key, std::string*) { Lock lock(mConnMtx); - mGetValueStmt->reset(); - ::sqlite3_bind_text(mGetValueStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, 0); + ::sqlite3_bind_text(mGetValueStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT); Transaction transaction(mConn); + ScopedReset scopedReset(mGetValueStmt); int ret = ::sqlite3_step(mGetValueStmt->get()); if (ret == SQLITE_DONE) { @@ -252,14 +258,14 @@ std::string KVStore::get(const std::string& key) return reinterpret_cast(sqlite3_column_text(mGetValueStmt->get(), FIRST_COLUMN)); } -std::vector KVStore::list(const std::string& key) +std::vector KVStore::getInternal(const std::string& key, std::vector*) { Lock lock(mConnMtx); - mGetValueListStmt->reset(); - ::sqlite3_bind_text(mGetValueListStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, 0); + ::sqlite3_bind_text(mGetValueListStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT); Transaction transaction(mConn); + ScopedReset scopedReset(mGetValueListStmt); unsigned int valuesSize = countInternal(key); if (valuesSize == 0) { @@ -277,4 +283,5 @@ std::vector KVStore::list(const std::string& key) return values; } + } // namespace config diff --git a/src/config/kvstore.hpp b/src/config/kvstore.hpp index 991625b..94258f1 100644 --- a/src/config/kvstore.hpp +++ b/src/config/kvstore.hpp @@ -25,15 +25,16 @@ #ifndef COMMON_CONFIG_KVSTORE_HPP #define COMMON_CONFIG_KVSTORE_HPP -#include "config/sqlite3/statement.hpp" #include "config/sqlite3/connection.hpp" +#include "config/sqlite3/statement.hpp" -#include -#include -#include -#include +#include #include +#include #include +#include +#include +#include namespace config { @@ -52,7 +53,6 @@ public: */ void clear(); - /** * @return Number of all stored values */ @@ -76,45 +76,49 @@ public: /** * Stores a single value corresponding to the passed key - * @param key string key of the value - * @param value string value - */ - void set(const std::string& key, const std::string& value); - - /** - * Stores a vector of values. - * Generates new keys using appending a '.' and consecutive integers. - * Removes values corresponding to this key before anything is added. * * @param key string key of the value - * @param value string value + * @param value value corresponding to the key */ - void set(const std::string& key, const std::vector& values); + template + void set(const std::string& key, const T& value) + { + return setInternal(key, value); + } /** - * Stores values from the list. + * Gets the value corresponding to the key. + * Uses stringstreams to parse. * - * @see KVStore::set(const std::string& key, const std::vector& values) - * @param key string key of the value - * @param values [description] - */ - void set(const std::string& key, const std::initializer_list& values); - - /** * @param key string key of the value - * @return string value corresponding to this particular key + * @tparam T = std::string desired type of the return value + * @return value corresponding to the key */ - std::string get(const std::string& key); + template + T get(const std::string& key) + { + return getInternal(key, static_cast(nullptr)); + } - /** - * @param key string key of the value - * @return vector of values corresponding to the key - */ - std::vector list(const std::string& key); private: typedef std::lock_guard Lock; + void setInternal(const std::string& key, const std::string& value); + void setInternal(const std::string& key, const std::initializer_list& values); + void setInternal(const std::string& key, const std::vector& values); + template + void setInternal(const std::string& key, const T& value); + template + void setInternal(const std::string& key, const std::vector& values); + + std::string getInternal(const std::string& key, std::string*); + std::vector getInternal(const std::string& key, std::vector*); + template + T getInternal(const std::string& key, T*); + template + std::vector getInternal(const std::string& key, std::vector*); + std::mutex mConnMtx; sqlite3::Connection mConn; @@ -133,6 +137,60 @@ private: }; +template +void KVStore::setInternal(const std::string& key, const T& value) +{ + std::ostringstream oss; + oss << value; + setInternal(key, oss.str()); +} + +template +void KVStore::setInternal(const std::string& key, const std::vector& values) +{ + std::vector strValues(values.size()); + + auto toString = [](const T & value) -> std::string { + std::ostringstream oss; + oss << value; + return oss.str(); + }; + std::transform(values.begin(), + values.end(), + strValues.begin(), + toString); + + setInternal(key, strValues); +} + +template +T KVStore::getInternal(const std::string& key, T*) +{ + std::istringstream ss(getInternal(key, static_cast(nullptr))); + T ret; + ss >> ret; + return ret; +} + +template +std::vector KVStore::getInternal(const std::string& key, std::vector*) +{ + std::vector strValues = getInternal(key, static_cast*>(nullptr)); + std::vector values(strValues.size()); + + auto parse = [](const std::string & strValue) -> T { + std::istringstream iss; + T value; + iss >> value; + return value; + }; + std::transform(strValues.begin(), + strValues.end(), + values.begin(), + parse); + + return values; +} } // namespace config #endif // COMMON_CONFIG_KVSTORE_HPP diff --git a/src/config/sqlite3/connection.hpp b/src/config/sqlite3/connection.hpp index bf53afc..850332a 100644 --- a/src/config/sqlite3/connection.hpp +++ b/src/config/sqlite3/connection.hpp @@ -36,6 +36,7 @@ struct Connection { * @param path database file path */ Connection(const std::string& path); + Connection(const Connection&) = delete; ~Connection(); /** -- 2.7.4