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)
117 void KVStore::setupDb()
120 Transaction transaction(mConn);
121 mConn.exec("CREATE TABLE IF NOT EXISTS data (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
124 void KVStore::prepareStatements()
127 new sqlite3::Statement(mConn, "SELECT value FROM data WHERE key GLOB ? LIMIT 1"));
128 mGetValueListStmt.reset(
129 new sqlite3::Statement(mConn, "SELECT value FROM data WHERE key GLOB ? ||'*' ORDER BY key"));
130 mGetValueCountStmt.reset(
131 new sqlite3::Statement(mConn, "SELECT count(key) FROM data WHERE key GLOB ? ||'*' "));
133 new sqlite3::Statement(mConn, "SELECT count(key) FROM data"));
135 new sqlite3::Statement(mConn, "INSERT OR REPLACE INTO data (key, value) VALUES (?,?)"));
136 mRemoveValuesStmt.reset(
137 new sqlite3::Statement(mConn, "DELETE FROM data WHERE key GLOB ? ||'*' "));
140 void KVStore::clear()
143 Transaction transaction(mConn);
144 mConn.exec("DELETE FROM data");
147 unsigned int KVStore::size()
150 ScopedReset scopedReset(mGetSizeStmt);
152 if (::sqlite3_step(mGetSizeStmt->get()) != SQLITE_ROW) {
153 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
156 return static_cast<unsigned int>(::sqlite3_column_int(mGetSizeStmt->get(), FIRST_COLUMN));
159 unsigned int KVStore::count(const std::string& key)
162 Transaction transaction(mConn);
163 return countInternal(key);
166 unsigned int KVStore::countInternal(const std::string& key)
168 ScopedReset scopedReset(mGetValueCountStmt);
170 ::sqlite3_bind_text(mGetValueCountStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
172 if (::sqlite3_step(mGetValueCountStmt->get()) != SQLITE_ROW) {
173 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
176 return static_cast<unsigned int>(::sqlite3_column_int(mGetValueCountStmt->get(), FIRST_COLUMN));
179 void KVStore::remove(const std::string& key)
182 Transaction transaction(mConn);
186 void KVStore::removeInternal(const std::string& key)
188 ScopedReset scopedReset(mRemoveValuesStmt);
190 ::sqlite3_bind_text(mRemoveValuesStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
192 if (::sqlite3_step(mRemoveValuesStmt->get()) != SQLITE_DONE) {
193 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
197 void KVStore::setInternal(const std::string& key, const std::string& value)
201 ::sqlite3_bind_text(mSetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
202 ::sqlite3_bind_text(mSetValueStmt->get(), 2, value.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
204 Transaction transaction(mConn);
205 ScopedReset scopedReset(mSetValueStmt);
207 if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) {
208 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
212 void KVStore::setInternal(const std::string& key, const std::initializer_list<std::string>& values)
214 setInternal(key, std::vector<std::string>(values));
217 void KVStore::setInternal(const std::string& key, const std::vector<std::string>& values)
219 if (values.size() > std::numeric_limits<unsigned int>::max()) {
220 throw ConfigException("Too many values to insert");
224 Transaction transaction(mConn);
228 for (unsigned int i = 0; i < values.size(); ++i) {
229 ScopedReset scopedReset(mSetValueStmt);
230 const std::string modifiedKey = key + "." + std::to_string(i);;
232 ::sqlite3_bind_text(mSetValueStmt->get(), 1, modifiedKey.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
233 ::sqlite3_bind_text(mSetValueStmt->get(), 2, values[i].c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
235 if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) {
236 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
241 std::string KVStore::getInternal(const std::string& key, std::string*)
245 ::sqlite3_bind_text(mGetValueStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
247 Transaction transaction(mConn);
248 ScopedReset scopedReset(mGetValueStmt);
250 int ret = ::sqlite3_step(mGetValueStmt->get());
251 if (ret == SQLITE_DONE) {
252 throw ConfigException("No value corresponding to the key");
254 if (ret != SQLITE_ROW) {
255 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
258 return reinterpret_cast<const char*>(sqlite3_column_text(mGetValueStmt->get(), FIRST_COLUMN));
261 std::vector<std::string> KVStore::getInternal(const std::string& key, std::vector<std::string>*)
265 ::sqlite3_bind_text(mGetValueListStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
267 Transaction transaction(mConn);
268 ScopedReset scopedReset(mGetValueListStmt);
270 unsigned int valuesSize = countInternal(key);
271 if (valuesSize == 0) {
272 throw ConfigException("No value corresponding to the key");
275 std::vector<std::string> values(valuesSize);
276 for (std::string& value : values) {
277 if (::sqlite3_step(mGetValueListStmt->get()) != SQLITE_ROW) {
278 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
280 value = reinterpret_cast<const char*>(
281 sqlite3_column_text(mGetValueListStmt->get(), FIRST_COLUMN));
287 } // namespace config