2 * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
4 * Contact: Jan Olszak <j.olszak@samsung.com>
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License
21 * @author Jan Olszak (j.olszak@samsung.com)
22 * @brief Definition of a class for key-value storage in a sqlite3 database
27 #include "config/kvstore.hpp"
28 #include "config/exception.hpp"
39 const int AUTO_DETERM_SIZE = -1;
40 const int FIRST_COLUMN = 0;
43 ScopedReset(std::unique_ptr<sqlite3::Statement>& stmtPtr)
44 : mStmtPtr(stmtPtr) {}
50 std::unique_ptr<sqlite3::Statement>& mStmtPtr;
54 * Escape characters used by the GLOB function.
56 void sqliteEscapeStr(::sqlite3_context* context, int argc, ::sqlite3_value** values)
58 char* inBuff = (char*)sqlite3_value_text(values[0]);
59 if (argc != 1 || inBuff == NULL) {
60 sqlite3_result_error(context, "SQL function escapeSequence() called with invalid arguments.\n", -1);
64 std::string in(inBuff);
65 static const std::set<char> toEscape({'?', '*', '[', ']'});
67 // Compute the out size
68 auto isEscapeChar = [&](char c) {
69 return toEscape.count(c) == 1;
71 size_t numEscape = std::count_if(in.begin(),
75 sqlite3_result_text(context, in.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
79 std::string out(in.size() + 2 * numEscape, 'x');
80 for (size_t i = 0, j = 0;
83 if (isEscapeChar(in[i])) {
93 sqlite3_result_text(context, out.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
100 struct KVStore::Transaction {
101 Transaction(KVStore* kvstorePtr)
102 : mKVStorePtr(kvstorePtr)
104 if (mKVStorePtr->mTransactionCounter == 0) {
105 mKVStorePtr->mConn.exec("BEGIN EXCLUSIVE TRANSACTION");
107 mKVStorePtr->mTransactionCounter += 1;
112 mKVStorePtr->mTransactionCounter -= 1;
113 if (mKVStorePtr->mTransactionCounter == 0) {
114 if (std::uncaught_exception()) {
115 mKVStorePtr->mConn.exec("ROLLBACK TRANSACTION");
117 mKVStorePtr->mConn.exec("COMMIT TRANSACTION");
123 config::KVStore* mKVStorePtr;
127 KVStore::KVStore(const std::string& path)
136 KVStore::KVStore(const KVStore& store)
137 : mPath(store.mPath),
148 void KVStore::setupDb()
151 Transaction transaction(this);
152 mConn.exec("CREATE TABLE IF NOT EXISTS data (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
155 void KVStore::prepareStatements()
158 new sqlite3::Statement(mConn, "SELECT value FROM data WHERE key = ? LIMIT 1"));
159 mGetKeyExistsStmt.reset(
160 new sqlite3::Statement(mConn, "SELECT 1 FROM data WHERE key = ?1 OR key GLOB escapeStr(?1) || '.*' LIMIT 1"));
161 mGetIsEmptyStmt.reset(
162 new sqlite3::Statement(mConn, "SELECT 1 FROM data LIMIT 1"));
164 new sqlite3::Statement(mConn, "INSERT OR REPLACE INTO data (key, value) VALUES (?,?)"));
165 mRemoveValuesStmt.reset(
166 new sqlite3::Statement(mConn, "DELETE FROM data WHERE key = ?1 OR key GLOB escapeStr(?1) ||'.*' "));
169 void KVStore::createFunctions()
171 int ret = sqlite3_create_function(mConn.get(), "escapeStr", 1, SQLITE_ANY, 0, &sqliteEscapeStr, 0, 0);
172 if (ret != SQLITE_OK) {
173 throw ConfigException("Error during creating functions: " + mConn.getErrorMessage());
177 void KVStore::clear()
180 Transaction transaction(this);
181 mConn.exec("DELETE FROM data");
184 bool KVStore::isEmpty()
187 ScopedReset scopedReset(mGetIsEmptyStmt);
189 int ret = ::sqlite3_step(mGetIsEmptyStmt->get());
190 if (ret == SQLITE_DONE) {
192 } else if (ret == SQLITE_ROW) {
195 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
199 bool KVStore::exists(const std::string& key)
202 ScopedReset scopedReset(mGetKeyExistsStmt);
204 ::sqlite3_bind_text(mGetKeyExistsStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
206 int ret = ::sqlite3_step(mGetKeyExistsStmt->get());
207 if (ret == SQLITE_DONE) {
209 } else if (ret == SQLITE_ROW) {
212 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
216 void KVStore::remove(const std::string& key)
219 Transaction transaction(this);
223 void KVStore::removeInternal(const std::string& key)
225 ScopedReset scopedReset(mRemoveValuesStmt);
227 ::sqlite3_bind_text(mRemoveValuesStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
229 if (::sqlite3_step(mRemoveValuesStmt->get()) != SQLITE_DONE) {
230 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
234 void KVStore::setInternal(const std::string& key, const std::string& value)
238 ::sqlite3_bind_text(mSetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
239 ::sqlite3_bind_text(mSetValueStmt->get(), 2, value.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
241 Transaction transaction(this);
242 ScopedReset scopedReset(mSetValueStmt);
244 if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) {
245 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
249 void KVStore::setInternal(const std::string& key, const std::initializer_list<std::string>& values)
251 setInternal(key, std::vector<std::string>(values));
254 void KVStore::setInternal(const std::string& key, const std::vector<std::string>& values)
256 if (values.size() > std::numeric_limits<unsigned int>::max()) {
257 throw ConfigException("Too many values to insert");
261 Transaction transaction(this);
265 // Save vector's capacity
266 setInternal(key, values.size());
268 // Save vector's elements
269 for (unsigned int i = 0; i < values.size(); ++i) {
270 setInternal(config::key(key, std::to_string(i)),
275 std::string KVStore::getInternal(const std::string& key, std::string*)
279 ::sqlite3_bind_text(mGetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
281 Transaction transaction(this);
282 ScopedReset scopedReset(mGetValueStmt);
284 int ret = ::sqlite3_step(mGetValueStmt->get());
285 if (ret == SQLITE_DONE) {
286 throw ConfigException("No value corresponding to the key: " + key);
288 if (ret != SQLITE_ROW) {
289 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
292 return reinterpret_cast<const char*>(sqlite3_column_text(mGetValueStmt->get(), FIRST_COLUMN));
295 std::vector<std::string> KVStore::getInternal(const std::string& key, std::vector<std::string>*)
298 Transaction transaction(this);
300 unsigned int valuesSize = get<unsigned int>(key);
301 std::vector<std::string> values(valuesSize);
302 if (valuesSize == 0) {
306 for (unsigned int i = 0; i < values.size(); ++i) {
307 values[i] = getInternal(config::key(key, std::to_string(i)),
308 static_cast<std::string*>(nullptr));
315 } // namespace config