Writing the size of a vector in the KVStore 01/28201/4
authorJan Olszak <j.olszak@samsung.com>
Thu, 18 Sep 2014 08:46:53 +0000 (10:46 +0200)
committerJan Olszak <j.olszak@samsung.com>
Wed, 1 Oct 2014 13:15:12 +0000 (15:15 +0200)
[Bug/Feature]   No way to write an empty vector in the KVStore
[Cause]         N/A
[Solution]      N/A
[Verification]  Build, install, run tests

Change-Id: Ia4d67e33e9320c27cf6d86e809bf5c54edd940cc

src/config/from-kvstore-visitor.hpp
src/config/kvstore.cpp
src/config/kvstore.hpp
src/config/to-kvstore-visitor.hpp

index 40ecaaa..0c91eb5 100644 (file)
@@ -77,18 +77,19 @@ private:
         value.accept(visitor);
     }
 
-    template<typename T>
+    template<typename T, typename std::enable_if<isVisitable<T>::value, int>::type = 0>
     void getInternal(const std::string& name, std::vector<T>& values)
     {
         values.clear();
-        for (unsigned int i = 0;; ++i) {
+        size_t vectorSize = mStorePtr->get<size_t>(name);
+        if (vectorSize == 0) {
+            return;
+        }
+
+        values.resize(vectorSize);
+        for (size_t i = 0; i < vectorSize; ++i) {
             const std::string k = key(name, std::to_string(i));
-            if (mStorePtr->count(k) == 0) {
-                return;
-            }
-            T value;
-            getInternal(k, value);
-            values.push_back(value);
+            getInternal(k, values[i]);
         }
     }
 };
index 03231be..6c26e64 100644 (file)
@@ -39,24 +39,6 @@ namespace {
 const int AUTO_DETERM_SIZE = -1;
 const int FIRST_COLUMN = 0;
 
-struct Transaction {
-    Transaction(sqlite3::Connection& connRef)
-        : mConnRef(connRef)
-    {
-        mConnRef.exec("BEGIN EXCLUSIVE TRANSACTION");
-    }
-    ~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) {}
@@ -68,9 +50,19 @@ private:
     std::unique_ptr<sqlite3::Statement>& mStmtPtr;
 };
 
-std::string escape(const std::string& in)
+/**
+ * Escape characters used by the GLOB function.
+ */
+void sqliteEscapeStr(::sqlite3_context* context, int argc, ::sqlite3_value** values)
 {
-    const std::set<char> toEscape({'?', '*', '[', ']'});
+    char* inBuff = (char*)sqlite3_value_text(values[0]);
+    if (argc != 1 || inBuff == NULL) {
+        sqlite3_result_error(context, "SQL function escapeSequence() called with invalid arguments.\n", -1);
+        return;
+    }
+
+    std::string in(inBuff);
+    static const std::set<char> toEscape({'?', '*', '[', ']'});
 
     // Compute the out size
     auto isEscapeChar = [&](char c) {
@@ -80,7 +72,7 @@ std::string escape(const std::string& in)
                                      in.end(),
                                      isEscapeChar);
     if (numEscape == 0) {
-        return in;
+        sqlite3_result_text(context, in.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
     }
 
     // Escape characters
@@ -98,16 +90,46 @@ std::string escape(const std::string& in)
             out[j] = in[i];
         }
     }
-    return out;
+    sqlite3_result_text(context, out.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
 }
+
 } // namespace
 
 
+
+struct KVStore::Transaction {
+    Transaction(KVStore* kvstorePtr)
+        : mKVStorePtr(kvstorePtr)
+    {
+        if (mKVStorePtr->mTransactionCounter == 0) {
+            mKVStorePtr->mConn.exec("BEGIN EXCLUSIVE TRANSACTION");
+        }
+        mKVStorePtr->mTransactionCounter += 1;
+    }
+
+    ~Transaction()
+    {
+        mKVStorePtr->mTransactionCounter -= 1;
+        if (mKVStorePtr->mTransactionCounter == 0) {
+            if (std::uncaught_exception()) {
+                mKVStorePtr->mConn.exec("ROLLBACK TRANSACTION");
+            } else {
+                mKVStorePtr->mConn.exec("COMMIT TRANSACTION");
+            }
+        }
+    }
+
+private:
+    config::KVStore* mKVStorePtr;
+};
+
+
 KVStore::KVStore(const std::string& path)
     : mPath(path),
       mConn(path)
 {
     setupDb();
+    createFunctions();
     prepareStatements();
 }
 
@@ -115,6 +137,7 @@ KVStore::KVStore(const KVStore& store)
     : mPath(store.mPath),
       mConn(mPath)
 {
+    createFunctions();
     prepareStatements();
 }
 
@@ -125,69 +148,75 @@ KVStore::~KVStore()
 void KVStore::setupDb()
 {
     Lock lock(mConnMtx);
-    Transaction transaction(mConn);
+    Transaction transaction(this);
     mConn.exec("CREATE TABLE IF NOT EXISTS data (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
 }
 
 void KVStore::prepareStatements()
 {
     mGetValueStmt.reset(
-        new sqlite3::Statement(mConn, "SELECT value FROM data WHERE key GLOB ? LIMIT 1"));
-    mGetValueListStmt.reset(
-        new sqlite3::Statement(mConn, "SELECT value FROM data WHERE key GLOB ? ||'*' ORDER BY key"));
-    mGetValueCountStmt.reset(
-        new sqlite3::Statement(mConn, "SELECT count(key) FROM data WHERE key GLOB ? ||'*' "));
-    mGetSizeStmt.reset(
-        new sqlite3::Statement(mConn, "SELECT count(key) FROM data"));
+        new sqlite3::Statement(mConn, "SELECT value FROM data WHERE key = ? LIMIT 1"));
+    mGetKeyExistsStmt.reset(
+        new sqlite3::Statement(mConn, "SELECT 1 FROM data WHERE key = ?1 OR key GLOB escapeStr(?1) || '.*' LIMIT 1"));
+    mGetIsEmptyStmt.reset(
+        new sqlite3::Statement(mConn, "SELECT 1 FROM data LIMIT 1"));
     mSetValueStmt.reset(
         new sqlite3::Statement(mConn, "INSERT OR REPLACE INTO data (key, value) VALUES (?,?)"));
     mRemoveValuesStmt.reset(
-        new sqlite3::Statement(mConn, "DELETE FROM data WHERE key GLOB ? ||'*' "));
+        new sqlite3::Statement(mConn, "DELETE FROM data WHERE key = ?1  OR key GLOB escapeStr(?1) ||'.*' "));
+}
+
+void KVStore::createFunctions()
+{
+    int ret = sqlite3_create_function(mConn.get(), "escapeStr", 1, SQLITE_ANY, 0, &sqliteEscapeStr, 0, 0);
+    if (ret != SQLITE_OK) {
+        throw ConfigException("Error during creating functions: " + mConn.getErrorMessage());
+    }
 }
 
 void KVStore::clear()
 {
     Lock lock(mConnMtx);
-    Transaction transaction(mConn);
+    Transaction transaction(this);
     mConn.exec("DELETE FROM data");
 }
 
-unsigned int KVStore::size()
+bool KVStore::isEmpty()
 {
     Lock lock(mConnMtx);
-    ScopedReset scopedReset(mGetSizeStmt);
+    ScopedReset scopedReset(mGetIsEmptyStmt);
 
-    if (::sqlite3_step(mGetSizeStmt->get()) != SQLITE_ROW) {
+    int ret = ::sqlite3_step(mGetIsEmptyStmt->get());
+    if (ret == SQLITE_DONE) {
+        return true;
+    } else if (ret == SQLITE_ROW) {
+        return false;
+    } else {
         throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
     }
-
-    return static_cast<unsigned int>(::sqlite3_column_int(mGetSizeStmt->get(), FIRST_COLUMN));
 }
 
-unsigned int KVStore::count(const std::string& key)
+bool KVStore::exists(const std::string& key)
 {
     Lock lock(mConnMtx);
-    Transaction transaction(mConn);
-    return countInternal(key);
-}
+    ScopedReset scopedReset(mGetKeyExistsStmt);
 
-unsigned int KVStore::countInternal(const std::string& key)
-{
-    ScopedReset scopedReset(mGetValueCountStmt);
-
-    ::sqlite3_bind_text(mGetValueCountStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
+    ::sqlite3_bind_text(mGetKeyExistsStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
 
-    if (::sqlite3_step(mGetValueCountStmt->get()) != SQLITE_ROW) {
+    int ret = ::sqlite3_step(mGetKeyExistsStmt->get());
+    if (ret == SQLITE_DONE) {
+        return false;
+    } else if (ret == SQLITE_ROW) {
+        return true;
+    } else {
         throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
     }
-
-    return static_cast<unsigned int>(::sqlite3_column_int(mGetValueCountStmt->get(), FIRST_COLUMN));
 }
 
 void KVStore::remove(const std::string& key)
 {
     Lock lock(mConnMtx);
-    Transaction transaction(mConn);
+    Transaction transaction(this);
     removeInternal(key);
 }
 
@@ -209,7 +238,7 @@ void KVStore::setInternal(const std::string& key, const std::string& value)
     ::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);
+    Transaction transaction(this);
     ScopedReset scopedReset(mSetValueStmt);
 
     if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) {
@@ -229,20 +258,17 @@ void KVStore::setInternal(const std::string& key, const std::vector<std::string>
     }
 
     Lock lock(mConnMtx);
-    Transaction transaction(mConn);
+    Transaction transaction(this);
 
     removeInternal(key);
 
-    for (unsigned int i = 0; i < values.size(); ++i) {
-        ScopedReset scopedReset(mSetValueStmt);
-        const std::string modifiedKey = key + "." + std::to_string(i);;
-
-        ::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);
+    // Save vector's capacity
+    setInternal(key, values.size());
 
-        if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) {
-            throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
-        }
+    // Save vector's elements
+    for (unsigned int i = 0; i < values.size(); ++i) {
+        setInternal(config::key(key, std::to_string(i)),
+                    values[i]);
     }
 }
 
@@ -250,14 +276,14 @@ std::string KVStore::getInternal(const std::string& key, std::string*)
 {
     Lock lock(mConnMtx);
 
-    ::sqlite3_bind_text(mGetValueStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
+    ::sqlite3_bind_text(mGetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
 
-    Transaction transaction(mConn);
+    Transaction transaction(this);
     ScopedReset scopedReset(mGetValueStmt);
 
     int ret = ::sqlite3_step(mGetValueStmt->get());
     if (ret == SQLITE_DONE) {
-        throw ConfigException("No value corresponding to the key");
+        throw ConfigException("No value corresponding to the key: " + key);
     }
     if (ret != SQLITE_ROW) {
         throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
@@ -269,24 +295,18 @@ std::string KVStore::getInternal(const std::string& key, std::string*)
 std::vector<std::string> KVStore::getInternal(const std::string& key, std::vector<std::string>*)
 {
     Lock lock(mConnMtx);
+    Transaction transaction(this);
 
-    ::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);
+    unsigned int valuesSize = get<unsigned int>(key);
+    std::vector<std::string> values(valuesSize);
     if (valuesSize == 0) {
-        throw ConfigException("No value corresponding to the key");
+        return values;
     }
 
-    std::vector<std::string> values(valuesSize);
-    for (std::string& value : values) {
-        if (::sqlite3_step(mGetValueListStmt->get()) != SQLITE_ROW) {
-            throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
-        }
-        value = reinterpret_cast<const char*>(
-                    sqlite3_column_text(mGetValueListStmt->get(), FIRST_COLUMN));
+    for (unsigned int i = 0; i < values.size(); ++i) {
+        values[i] = getInternal(config::key(key, std::to_string(i)),
+                                static_cast<std::string*>(nullptr));
+
     }
 
     return values;
index 6e30d99..3a2455b 100644 (file)
@@ -55,16 +55,16 @@ public:
     void clear();
 
     /**
-     * @return Number of all stored values
+     * @return Is there any data stored
      */
-    unsigned int size();
+    bool isEmpty();
 
     /**
      * @param key string regexp of the stored values
      *
-     * @return Number of values corresponding to the passed key
+     * @return Does this key exist in the database
      */
-    unsigned int count(const std::string& key);
+    bool exists(const std::string& key);
 
     /**
      * Removes values corresponding to the passed key.
@@ -103,7 +103,9 @@ public:
 
 
 private:
-    typedef std::lock_guard<std::mutex> Lock;
+    struct Transaction;
+    typedef std::lock_guard<std::recursive_mutex> Lock;
+    unsigned int mTransactionCounter;
 
     void setInternal(const std::string& key, const std::string& value);
     void setInternal(const std::string& key, const std::initializer_list<std::string>& values);
@@ -120,22 +122,22 @@ private:
     template<typename T>
     std::vector<T> getInternal(const std::string& key, std::vector<T>*);
 
-    std::mutex mConnMtx;
+    std::recursive_mutex mConnMtx;
 
     std::string mPath;
     sqlite3::Connection mConn;
     std::unique_ptr<sqlite3::Statement> mGetValueStmt;
-    std::unique_ptr<sqlite3::Statement> mGetValueCountStmt;
-    std::unique_ptr<sqlite3::Statement> mGetSizeStmt;
+    std::unique_ptr<sqlite3::Statement> mGetKeyExistsStmt;
+    std::unique_ptr<sqlite3::Statement> mGetIsEmptyStmt;
     std::unique_ptr<sqlite3::Statement> mGetValueListStmt;
     std::unique_ptr<sqlite3::Statement> mSetValueStmt;
     std::unique_ptr<sqlite3::Statement> mRemoveValuesStmt;
 
     void setupDb();
     void prepareStatements();
+    void createFunctions();
 
     void removeInternal(const std::string& key);
-    unsigned int countInternal(const std::string& key);
 
 };
 
index 8da093b..477ff83 100644 (file)
@@ -78,10 +78,11 @@ private:
         value.accept(visitor);
     }
 
-    template<typename T>
+    template<typename T, typename std::enable_if<isVisitable<T>::value, int>::type = 0>
     void setInternal(const std::string& name, const std::vector<T>& values)
     {
         mStorePtr->remove(name);
+        mStorePtr->set(name, values.size());
         for (size_t i = 0; i < values.size(); ++i) {
             setInternal(key(name, std::to_string(i)), values[i]);
         }