Storing serializable types in KVStore 44/26144/2
authorJan Olszak <j.olszak@samsung.com>
Mon, 11 Aug 2014 10:54:12 +0000 (12:54 +0200)
committerJan Olszak <j.olszak@samsung.com>
Mon, 18 Aug 2014 13:24:43 +0000 (15:24 +0200)
[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
src/config/kvstore.cpp
src/config/kvstore.hpp
src/config/sqlite3/connection.hpp

index 99c1dd5..5219bac 100644 (file)
@@ -11,7 +11,6 @@ Summary:       Config library
 BuildRequires: cmake
 BuildRequires: pkgconfig(sqlite3)
 
-
 %description
 The package provides libConfig library.
 
index f48ee6f..cc174e4 100644 (file)
  * @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 <exception>
 #include <limits>
 #include <memory>
-#include <algorithm>
 #include <set>
 
 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<sqlite3::Statement>& stmtPtr)
+        : mStmtPtr(stmtPtr) {}
+    ~ScopedReset()
+    {
+        mStmtPtr->reset();
+    }
+private:
+    std::unique_ptr<sqlite3::Statement>& 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<std::string>& values)
+void KVStore::setInternal(const std::string& key, const std::initializer_list<std::string>& values)
 {
-    set(key, std::vector<std::string>(values));
+    setInternal(key, std::vector<std::string>(values));
 }
 
-void KVStore::set(const std::string& key, const std::vector<std::string>& values)
+void KVStore::setInternal(const std::string& key, const std::vector<std::string>& values)
 {
     if (values.size() > std::numeric_limits<unsigned int>::max()) {
         throw ConfigException("Too many values to insert");
@@ -220,11 +226,11 @@ void KVStore::set(const std::string& key, const std::vector<std::string>& 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<std::string>& 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<const char*>(sqlite3_column_text(mGetValueStmt->get(), FIRST_COLUMN));
 }
 
-std::vector<std::string> KVStore::list(const std::string& key)
+std::vector<std::string> KVStore::getInternal(const std::string& key, std::vector<std::string>*)
 {
     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<std::string> KVStore::list(const std::string& key)
 
     return values;
 }
+
 } // namespace config
index 991625b..94258f1 100644 (file)
 #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 <sqlite3.h>
-#include <vector>
-#include <string>
-#include <memory>
+#include <algorithm>
 #include <initializer_list>
+#include <memory>
 #include <mutex>
+#include <sstream>
+#include <string>
+#include <vector>
 
 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<std::string>& values);
+    template<typename T>
+    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<std::string>& values)
-     * @param key string key of the value
-     * @param values [description]
-     */
-    void set(const std::string& key, const std::initializer_list<std::string>& 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<typename T = std::string>
+    T get(const std::string& key)
+    {
+        return getInternal(key, static_cast<T*>(nullptr));
+    }
 
-    /**
-     * @param key string key of the value
-     * @return vector of values corresponding to the key
-     */
-    std::vector<std::string> list(const std::string& key);
 
 private:
     typedef std::lock_guard<std::mutex> Lock;
 
+    void setInternal(const std::string& key, const std::string& value);
+    void setInternal(const std::string& key, const std::initializer_list<std::string>& values);
+    void setInternal(const std::string& key, const std::vector<std::string>& values);
+    template<typename T>
+    void setInternal(const std::string& key, const T& value);
+    template<typename T>
+    void setInternal(const std::string& key, const std::vector<T>& values);
+
+    std::string getInternal(const std::string& key, std::string*);
+    std::vector<std::string> getInternal(const std::string& key, std::vector<std::string>*);
+    template<typename T>
+    T getInternal(const std::string& key, T*);
+    template<typename T>
+    std::vector<T> getInternal(const std::string& key, std::vector<T>*);
+
     std::mutex mConnMtx;
 
     sqlite3::Connection mConn;
@@ -133,6 +137,60 @@ private:
 
 };
 
+template<typename T>
+void KVStore::setInternal(const std::string& key, const T& value)
+{
+    std::ostringstream oss;
+    oss << value;
+    setInternal(key, oss.str());
+}
+
+template<typename T>
+void KVStore::setInternal(const std::string& key, const std::vector<T>& values)
+{
+    std::vector<std::string> 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<typename T>
+T KVStore::getInternal(const std::string& key, T*)
+{
+    std::istringstream ss(getInternal(key, static_cast<std::string*>(nullptr)));
+    T ret;
+    ss >> ret;
+    return ret;
+}
+
+template<typename T>
+std::vector<T> KVStore::getInternal(const std::string& key, std::vector<T>*)
+{
+    std::vector<std::string> strValues = getInternal(key, static_cast<std::vector<std::string>*>(nullptr));
+    std::vector<T> 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
index bf53afc..850332a 100644 (file)
@@ -36,6 +36,7 @@ struct Connection {
      * @param path database file path
      */
     Connection(const std::string& path);
+    Connection(const Connection&) = delete;
     ~Connection();
 
     /**