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
26 #include "config/kvstore.hpp"
27 #include "config/exception.hpp"
38 const int AUTO_DETERM_SIZE = -1;
39 const int FIRST_COLUMN = 0;
42 Transaction(sqlite3::Connection& connRef)
45 mConnRef.exec("BEGIN EXCLUSIVE TRANSACTION");
49 mConnRef.exec("COMMIT TRANSACTION");
52 sqlite3::Connection& mConnRef;
56 std::string escape(const std::string& in)
58 const std::set<char> toEscape({'?', '*', '[', ']'});
60 // Compute the out size
61 auto isEscapeChar = [&](char c) {
62 return toEscape.count(c) == 1;
64 size_t numEscape = std::count_if(in.begin(),
72 std::string out(in.size() + 2 * numEscape, 'x');
73 for (size_t i = 0, j = 0;
76 if (isEscapeChar(in[i])) {
91 KVStore::KVStore(const std::string& path)
103 void KVStore::setupDb()
106 const std::string setupScript = R"setupScript(
107 BEGIN EXCLUSIVE TRANSACTION;
109 CREATE TABLE IF NOT EXISTS data (
110 key TEXT PRIMARY KEY,
117 mConn.exec(setupScript);
120 void KVStore::prepareStatements()
123 new sqlite3::Statement(mConn, "SELECT value FROM data WHERE key GLOB ? LIMIT 1"));
124 mGetValueListStmt.reset(
125 new sqlite3::Statement(mConn, "SELECT value FROM data WHERE key GLOB ? ||'*' ORDER BY key"));
126 mGetValueCountStmt.reset(
127 new sqlite3::Statement(mConn, "SELECT count(key) FROM data WHERE key GLOB ? ||'*' "));
129 new sqlite3::Statement(mConn, "SELECT count(key) FROM data"));
131 new sqlite3::Statement(mConn, "INSERT OR REPLACE INTO data (key, value) VALUES (?,?)"));
132 mRemoveValuesStmt.reset(
133 new sqlite3::Statement(mConn, "DELETE FROM data WHERE key GLOB ? ||'*' "));
137 void KVStore::clear()
140 Transaction transaction(mConn);
141 mConn.exec("DELETE FROM data");
144 unsigned int KVStore::size()
146 mGetSizeStmt->reset();
148 if (::sqlite3_step(mGetSizeStmt->get()) != SQLITE_ROW) {
149 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
152 return static_cast<unsigned int>(::sqlite3_column_int(mGetSizeStmt->get(), FIRST_COLUMN));
155 unsigned int KVStore::count(const std::string& key)
158 Transaction transaction(mConn);
159 return countInternal(key);
162 unsigned int KVStore::countInternal(const std::string& key)
164 mGetValueCountStmt->reset();
166 ::sqlite3_bind_text(mGetValueCountStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, 0);
168 if (::sqlite3_step(mGetValueCountStmt->get()) != SQLITE_ROW) {
169 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
172 return static_cast<unsigned int>(::sqlite3_column_int(mGetValueCountStmt->get(), FIRST_COLUMN));
175 void KVStore::remove(const std::string& key)
178 Transaction transaction(mConn);
182 void KVStore::removeInternal(const std::string& key)
184 mRemoveValuesStmt->reset();
185 ::sqlite3_bind_text(mRemoveValuesStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, 0);
187 if (::sqlite3_step(mRemoveValuesStmt->get()) != SQLITE_DONE) {
188 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
192 void KVStore::set(const std::string& key, const std::string& value)
195 mSetValueStmt->reset();
197 ::sqlite3_bind_text(mSetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, 0);
198 ::sqlite3_bind_text(mSetValueStmt->get(), 2, value.c_str(), AUTO_DETERM_SIZE, 0);
200 Transaction transaction(mConn);
201 if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) {
202 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
206 void KVStore::set(const std::string& key, const std::initializer_list<std::string>& values)
208 set(key, std::vector<std::string>(values));
211 void KVStore::set(const std::string& key, const std::vector<std::string>& values)
213 if (values.size() > std::numeric_limits<unsigned int>::max()) {
214 throw ConfigException("Too many values to insert");
218 Transaction transaction(mConn);
222 for (unsigned int i = 0; i < values.size(); ++i) {
223 mSetValueStmt->reset();
224 const std::string modifiedKey = key + "." + std::to_string(i);;
226 ::sqlite3_bind_text(mSetValueStmt->get(), 1, modifiedKey.c_str(), AUTO_DETERM_SIZE, 0);
227 ::sqlite3_bind_text(mSetValueStmt->get(), 2, values[i].c_str(), AUTO_DETERM_SIZE, 0);
229 if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) {
230 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
235 std::string KVStore::get(const std::string& key)
239 mGetValueStmt->reset();
240 ::sqlite3_bind_text(mGetValueStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, 0);
242 Transaction transaction(mConn);
244 int ret = ::sqlite3_step(mGetValueStmt->get());
245 if (ret == SQLITE_DONE) {
246 throw ConfigException("No value corresponding to the key");
248 if (ret != SQLITE_ROW) {
249 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
252 return reinterpret_cast<const char*>(sqlite3_column_text(mGetValueStmt->get(), FIRST_COLUMN));
255 std::vector<std::string> KVStore::list(const std::string& key)
259 mGetValueListStmt->reset();
260 ::sqlite3_bind_text(mGetValueListStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, 0);
262 Transaction transaction(mConn);
264 unsigned int valuesSize = countInternal(key);
265 if (valuesSize == 0) {
266 throw ConfigException("No value corresponding to the key");
269 std::vector<std::string> values(valuesSize);
270 for (std::string& value : values) {
271 if (::sqlite3_step(mGetValueListStmt->get()) != SQLITE_ROW) {
272 throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
274 value = reinterpret_cast<const char*>(
275 sqlite3_column_text(mGetValueListStmt->get(), FIRST_COLUMN));
280 } // namespace config