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 Transaction(sqlite3::Connection& connRef)
46 mConnRef.exec("BEGIN EXCLUSIVE TRANSACTION");
50 if (std::uncaught_exception()) {
51 mConnRef.exec("ROLLBACK TRANSACTION");
53 mConnRef.exec("COMMIT TRANSACTION");
57 sqlite3::Connection& mConnRef;
61 ScopedReset(std::unique_ptr<sqlite3::Statement>& stmtPtr)
62 : mStmtPtr(stmtPtr) {}
68 std::unique_ptr<sqlite3::Statement>& mStmtPtr;
71 std::string escape(const std::string& in)
73 const std::set<char> toEscape({'?', '*', '[', ']'});
75 // Compute the out size
76 auto isEscapeChar = [&](char c) {
77 return toEscape.count(c) == 1;
79 size_t numEscape = std::count_if(in.begin(),
87 std::string out(in.size() + 2 * numEscape, 'x');
88 for (size_t i = 0, j = 0;
91 if (isEscapeChar(in[i])) {
106 KVStore::KVStore(const std::string& path)
114 KVStore::KVStore(const KVStore& store)
115 : mPath(store.mPath),
125 void KVStore::setupDb()
128 Transaction transaction(mConn);
129 mConn.exec("CREATE TABLE IF NOT EXISTS data (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
132 void KVStore::prepareStatements()
135 new sqlite3::Statement(mConn, "SELECT value FROM data WHERE key GLOB ? LIMIT 1"));
136 mGetValueListStmt.reset(
137 new sqlite3::Statement(mConn, "SELECT value FROM data WHERE key GLOB ? ||'*' ORDER BY key"));
138 mGetValueCountStmt.reset(
139 new sqlite3::Statement(mConn, "SELECT count(key) FROM data WHERE key GLOB ? ||'*' "));
141 new sqlite3::Statement(mConn, "SELECT count(key) FROM data"));
143 new sqlite3::Statement(mConn, "INSERT OR REPLACE INTO data (key, value) VALUES (?,?)"));
144 mRemoveValuesStmt.reset(
145 new sqlite3::Statement(mConn, "DELETE FROM data WHERE key GLOB ? ||'*' "));
148 void KVStore::clear()
151 Transaction transaction(mConn);
152 mConn.exec("DELETE FROM data");
155 unsigned int KVStore::size()
158 ScopedReset scopedReset(mGetSizeStmt);
160 if (::sqlite3_step(mGetSizeStmt->get()) != SQLITE_ROW) {
161 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
164 return static_cast<unsigned int>(::sqlite3_column_int(mGetSizeStmt->get(), FIRST_COLUMN));
167 unsigned int KVStore::count(const std::string& key)
170 Transaction transaction(mConn);
171 return countInternal(key);
174 unsigned int KVStore::countInternal(const std::string& key)
176 ScopedReset scopedReset(mGetValueCountStmt);
178 ::sqlite3_bind_text(mGetValueCountStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
180 if (::sqlite3_step(mGetValueCountStmt->get()) != SQLITE_ROW) {
181 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
184 return static_cast<unsigned int>(::sqlite3_column_int(mGetValueCountStmt->get(), FIRST_COLUMN));
187 void KVStore::remove(const std::string& key)
190 Transaction transaction(mConn);
194 void KVStore::removeInternal(const std::string& key)
196 ScopedReset scopedReset(mRemoveValuesStmt);
198 ::sqlite3_bind_text(mRemoveValuesStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
200 if (::sqlite3_step(mRemoveValuesStmt->get()) != SQLITE_DONE) {
201 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
205 void KVStore::setInternal(const std::string& key, const std::string& value)
209 ::sqlite3_bind_text(mSetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
210 ::sqlite3_bind_text(mSetValueStmt->get(), 2, value.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
212 Transaction transaction(mConn);
213 ScopedReset scopedReset(mSetValueStmt);
215 if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) {
216 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
220 void KVStore::setInternal(const std::string& key, const std::initializer_list<std::string>& values)
222 setInternal(key, std::vector<std::string>(values));
225 void KVStore::setInternal(const std::string& key, const std::vector<std::string>& values)
227 if (values.size() > std::numeric_limits<unsigned int>::max()) {
228 throw ConfigException("Too many values to insert");
232 Transaction transaction(mConn);
236 for (unsigned int i = 0; i < values.size(); ++i) {
237 ScopedReset scopedReset(mSetValueStmt);
238 const std::string modifiedKey = key + "." + std::to_string(i);;
240 ::sqlite3_bind_text(mSetValueStmt->get(), 1, modifiedKey.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
241 ::sqlite3_bind_text(mSetValueStmt->get(), 2, values[i].c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
243 if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) {
244 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
249 std::string KVStore::getInternal(const std::string& key, std::string*)
253 ::sqlite3_bind_text(mGetValueStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
255 Transaction transaction(mConn);
256 ScopedReset scopedReset(mGetValueStmt);
258 int ret = ::sqlite3_step(mGetValueStmt->get());
259 if (ret == SQLITE_DONE) {
260 throw ConfigException("No value corresponding to the key");
262 if (ret != SQLITE_ROW) {
263 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
266 return reinterpret_cast<const char*>(sqlite3_column_text(mGetValueStmt->get(), FIRST_COLUMN));
269 std::vector<std::string> KVStore::getInternal(const std::string& key, std::vector<std::string>*)
273 ::sqlite3_bind_text(mGetValueListStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
275 Transaction transaction(mConn);
276 ScopedReset scopedReset(mGetValueListStmt);
278 unsigned int valuesSize = countInternal(key);
279 if (valuesSize == 0) {
280 throw ConfigException("No value corresponding to the key");
283 std::vector<std::string> values(valuesSize);
284 for (std::string& value : values) {
285 if (::sqlite3_step(mGetValueListStmt->get()) != SQLITE_ROW) {
286 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
288 value = reinterpret_cast<const char*>(
289 sqlite3_column_text(mGetValueListStmt->get(), FIRST_COLUMN));
295 } // namespace config