* @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 {
}
~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)
{
}
return out;
}
-
} // namespace
+
KVStore::KVStore(const std::string& path)
: mConn(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()
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()
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());
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());
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");
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());
}
}
-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) {
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) {
return values;
}
+
} // namespace config
#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 {
*/
void clear();
-
/**
* @return Number of all stored values
*/
/**
* 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;
};
+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