Saving and loading configurations from KVStore
[archive/platform/core/system/libConfig.git] / src / config / kvstore.cpp
1 /*
2  *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Contact: Jan Olszak <j.olszak@samsung.com>
5  *
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
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
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
17  */
18
19 /**
20  * @file
21  * @author Jan Olszak (j.olszak@samsung.com)
22  * @brief  Definition of a class for key-value storage in a sqlite3 database
23  */
24
25 #include "config.hpp"
26
27 #include "config/kvstore.hpp"
28 #include "config/exception.hpp"
29
30 #include <exception>
31 #include <limits>
32 #include <memory>
33 #include <set>
34
35 namespace config {
36
37 namespace {
38
39 const int AUTO_DETERM_SIZE = -1;
40 const int FIRST_COLUMN = 0;
41
42 struct Transaction {
43     Transaction(sqlite3::Connection& connRef)
44         : mConnRef(connRef)
45     {
46         mConnRef.exec("BEGIN EXCLUSIVE TRANSACTION");
47     }
48     ~Transaction()
49     {
50         if (std::uncaught_exception()) {
51             mConnRef.exec("ROLLBACK TRANSACTION");
52         } else {
53             mConnRef.exec("COMMIT TRANSACTION");
54         }
55     }
56 private:
57     sqlite3::Connection& mConnRef;
58 };
59
60 struct ScopedReset {
61     ScopedReset(std::unique_ptr<sqlite3::Statement>& stmtPtr)
62         : mStmtPtr(stmtPtr) {}
63     ~ScopedReset()
64     {
65         mStmtPtr->reset();
66     }
67 private:
68     std::unique_ptr<sqlite3::Statement>& mStmtPtr;
69 };
70
71 std::string escape(const std::string& in)
72 {
73     const std::set<char> toEscape({'?', '*', '[', ']'});
74
75     // Compute the out size
76     auto isEscapeChar = [&](char c) {
77         return toEscape.count(c) == 1;
78     };
79     size_t numEscape = std::count_if(in.begin(),
80                                      in.end(),
81                                      isEscapeChar);
82     if (numEscape == 0) {
83         return in;
84     }
85
86     // Escape characters
87     std::string out(in.size() + 2 * numEscape, 'x');
88     for (size_t i = 0, j = 0;
89             i < in.size();
90             ++i, ++j) {
91         if (isEscapeChar(in[i])) {
92             out[j] = '[';
93             ++j;
94             out[j] = in[i];
95             ++j;
96             out[j] = ']';
97         } else {
98             out[j] = in[i];
99         }
100     }
101     return out;
102 }
103 } // namespace
104
105
106 KVStore::KVStore(const std::string& path)
107     : mPath(path),
108       mConn(path)
109 {
110     setupDb();
111     prepareStatements();
112 }
113
114 KVStore::KVStore(const KVStore& store)
115     : mPath(store.mPath),
116       mConn(mPath)
117 {
118     prepareStatements();
119 }
120
121 KVStore::~KVStore()
122 {
123 }
124
125 void KVStore::setupDb()
126 {
127     Lock lock(mConnMtx);
128     Transaction transaction(mConn);
129     mConn.exec("CREATE TABLE IF NOT EXISTS data (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
130 }
131
132 void KVStore::prepareStatements()
133 {
134     mGetValueStmt.reset(
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 ? ||'*' "));
140     mGetSizeStmt.reset(
141         new sqlite3::Statement(mConn, "SELECT count(key) FROM data"));
142     mSetValueStmt.reset(
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 ? ||'*' "));
146 }
147
148 void KVStore::clear()
149 {
150     Lock lock(mConnMtx);
151     Transaction transaction(mConn);
152     mConn.exec("DELETE FROM data");
153 }
154
155 unsigned int KVStore::size()
156 {
157     Lock lock(mConnMtx);
158     ScopedReset scopedReset(mGetSizeStmt);
159
160     if (::sqlite3_step(mGetSizeStmt->get()) != SQLITE_ROW) {
161         throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
162     }
163
164     return static_cast<unsigned int>(::sqlite3_column_int(mGetSizeStmt->get(), FIRST_COLUMN));
165 }
166
167 unsigned int KVStore::count(const std::string& key)
168 {
169     Lock lock(mConnMtx);
170     Transaction transaction(mConn);
171     return countInternal(key);
172 }
173
174 unsigned int KVStore::countInternal(const std::string& key)
175 {
176     ScopedReset scopedReset(mGetValueCountStmt);
177
178     ::sqlite3_bind_text(mGetValueCountStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
179
180     if (::sqlite3_step(mGetValueCountStmt->get()) != SQLITE_ROW) {
181         throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
182     }
183
184     return static_cast<unsigned int>(::sqlite3_column_int(mGetValueCountStmt->get(), FIRST_COLUMN));
185 }
186
187 void KVStore::remove(const std::string& key)
188 {
189     Lock lock(mConnMtx);
190     Transaction transaction(mConn);
191     removeInternal(key);
192 }
193
194 void KVStore::removeInternal(const std::string& key)
195 {
196     ScopedReset scopedReset(mRemoveValuesStmt);
197
198     ::sqlite3_bind_text(mRemoveValuesStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
199
200     if (::sqlite3_step(mRemoveValuesStmt->get()) != SQLITE_DONE) {
201         throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
202     }
203 }
204
205 void KVStore::setInternal(const std::string& key, const std::string& value)
206 {
207     Lock lock(mConnMtx);
208
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);
211
212     Transaction transaction(mConn);
213     ScopedReset scopedReset(mSetValueStmt);
214
215     if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) {
216         throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
217     }
218 }
219
220 void KVStore::setInternal(const std::string& key, const std::initializer_list<std::string>& values)
221 {
222     setInternal(key, std::vector<std::string>(values));
223 }
224
225 void KVStore::setInternal(const std::string& key, const std::vector<std::string>& values)
226 {
227     if (values.size() > std::numeric_limits<unsigned int>::max()) {
228         throw ConfigException("Too many values to insert");
229     }
230
231     Lock lock(mConnMtx);
232     Transaction transaction(mConn);
233
234     removeInternal(key);
235
236     for (unsigned int i = 0; i < values.size(); ++i) {
237         ScopedReset scopedReset(mSetValueStmt);
238         const std::string modifiedKey = key + "." + std::to_string(i);;
239
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);
242
243         if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) {
244             throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
245         }
246     }
247 }
248
249 std::string KVStore::getInternal(const std::string& key, std::string*)
250 {
251     Lock lock(mConnMtx);
252
253     ::sqlite3_bind_text(mGetValueStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
254
255     Transaction transaction(mConn);
256     ScopedReset scopedReset(mGetValueStmt);
257
258     int ret = ::sqlite3_step(mGetValueStmt->get());
259     if (ret == SQLITE_DONE) {
260         throw ConfigException("No value corresponding to the key");
261     }
262     if (ret != SQLITE_ROW) {
263         throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
264     }
265
266     return reinterpret_cast<const char*>(sqlite3_column_text(mGetValueStmt->get(), FIRST_COLUMN));
267 }
268
269 std::vector<std::string> KVStore::getInternal(const std::string& key, std::vector<std::string>*)
270 {
271     Lock lock(mConnMtx);
272
273     ::sqlite3_bind_text(mGetValueListStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
274
275     Transaction transaction(mConn);
276     ScopedReset scopedReset(mGetValueListStmt);
277
278     unsigned int valuesSize = countInternal(key);
279     if (valuesSize == 0) {
280         throw ConfigException("No value corresponding to the key");
281     }
282
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());
287         }
288         value = reinterpret_cast<const char*>(
289                     sqlite3_column_text(mGetValueListStmt->get(), FIRST_COLUMN));
290     }
291
292     return values;
293 }
294
295 } // namespace config