6c26e6457d90f38416043e8ebd99dc71949b8218
[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 ScopedReset {
43     ScopedReset(std::unique_ptr<sqlite3::Statement>& stmtPtr)
44         : mStmtPtr(stmtPtr) {}
45     ~ScopedReset()
46     {
47         mStmtPtr->reset();
48     }
49 private:
50     std::unique_ptr<sqlite3::Statement>& mStmtPtr;
51 };
52
53 /**
54  * Escape characters used by the GLOB function.
55  */
56 void sqliteEscapeStr(::sqlite3_context* context, int argc, ::sqlite3_value** values)
57 {
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);
61         return;
62     }
63
64     std::string in(inBuff);
65     static const std::set<char> toEscape({'?', '*', '[', ']'});
66
67     // Compute the out size
68     auto isEscapeChar = [&](char c) {
69         return toEscape.count(c) == 1;
70     };
71     size_t numEscape = std::count_if(in.begin(),
72                                      in.end(),
73                                      isEscapeChar);
74     if (numEscape == 0) {
75         sqlite3_result_text(context, in.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
76     }
77
78     // Escape characters
79     std::string out(in.size() + 2 * numEscape, 'x');
80     for (size_t i = 0, j = 0;
81             i < in.size();
82             ++i, ++j) {
83         if (isEscapeChar(in[i])) {
84             out[j] = '[';
85             ++j;
86             out[j] = in[i];
87             ++j;
88             out[j] = ']';
89         } else {
90             out[j] = in[i];
91         }
92     }
93     sqlite3_result_text(context, out.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
94 }
95
96 } // namespace
97
98
99
100 struct KVStore::Transaction {
101     Transaction(KVStore* kvstorePtr)
102         : mKVStorePtr(kvstorePtr)
103     {
104         if (mKVStorePtr->mTransactionCounter == 0) {
105             mKVStorePtr->mConn.exec("BEGIN EXCLUSIVE TRANSACTION");
106         }
107         mKVStorePtr->mTransactionCounter += 1;
108     }
109
110     ~Transaction()
111     {
112         mKVStorePtr->mTransactionCounter -= 1;
113         if (mKVStorePtr->mTransactionCounter == 0) {
114             if (std::uncaught_exception()) {
115                 mKVStorePtr->mConn.exec("ROLLBACK TRANSACTION");
116             } else {
117                 mKVStorePtr->mConn.exec("COMMIT TRANSACTION");
118             }
119         }
120     }
121
122 private:
123     config::KVStore* mKVStorePtr;
124 };
125
126
127 KVStore::KVStore(const std::string& path)
128     : mPath(path),
129       mConn(path)
130 {
131     setupDb();
132     createFunctions();
133     prepareStatements();
134 }
135
136 KVStore::KVStore(const KVStore& store)
137     : mPath(store.mPath),
138       mConn(mPath)
139 {
140     createFunctions();
141     prepareStatements();
142 }
143
144 KVStore::~KVStore()
145 {
146 }
147
148 void KVStore::setupDb()
149 {
150     Lock lock(mConnMtx);
151     Transaction transaction(this);
152     mConn.exec("CREATE TABLE IF NOT EXISTS data (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
153 }
154
155 void KVStore::prepareStatements()
156 {
157     mGetValueStmt.reset(
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"));
163     mSetValueStmt.reset(
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) ||'.*' "));
167 }
168
169 void KVStore::createFunctions()
170 {
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());
174     }
175 }
176
177 void KVStore::clear()
178 {
179     Lock lock(mConnMtx);
180     Transaction transaction(this);
181     mConn.exec("DELETE FROM data");
182 }
183
184 bool KVStore::isEmpty()
185 {
186     Lock lock(mConnMtx);
187     ScopedReset scopedReset(mGetIsEmptyStmt);
188
189     int ret = ::sqlite3_step(mGetIsEmptyStmt->get());
190     if (ret == SQLITE_DONE) {
191         return true;
192     } else if (ret == SQLITE_ROW) {
193         return false;
194     } else {
195         throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
196     }
197 }
198
199 bool KVStore::exists(const std::string& key)
200 {
201     Lock lock(mConnMtx);
202     ScopedReset scopedReset(mGetKeyExistsStmt);
203
204     ::sqlite3_bind_text(mGetKeyExistsStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
205
206     int ret = ::sqlite3_step(mGetKeyExistsStmt->get());
207     if (ret == SQLITE_DONE) {
208         return false;
209     } else if (ret == SQLITE_ROW) {
210         return true;
211     } else {
212         throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
213     }
214 }
215
216 void KVStore::remove(const std::string& key)
217 {
218     Lock lock(mConnMtx);
219     Transaction transaction(this);
220     removeInternal(key);
221 }
222
223 void KVStore::removeInternal(const std::string& key)
224 {
225     ScopedReset scopedReset(mRemoveValuesStmt);
226
227     ::sqlite3_bind_text(mRemoveValuesStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
228
229     if (::sqlite3_step(mRemoveValuesStmt->get()) != SQLITE_DONE) {
230         throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
231     }
232 }
233
234 void KVStore::setInternal(const std::string& key, const std::string& value)
235 {
236     Lock lock(mConnMtx);
237
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);
240
241     Transaction transaction(this);
242     ScopedReset scopedReset(mSetValueStmt);
243
244     if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) {
245         throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
246     }
247 }
248
249 void KVStore::setInternal(const std::string& key, const std::initializer_list<std::string>& values)
250 {
251     setInternal(key, std::vector<std::string>(values));
252 }
253
254 void KVStore::setInternal(const std::string& key, const std::vector<std::string>& values)
255 {
256     if (values.size() > std::numeric_limits<unsigned int>::max()) {
257         throw ConfigException("Too many values to insert");
258     }
259
260     Lock lock(mConnMtx);
261     Transaction transaction(this);
262
263     removeInternal(key);
264
265     // Save vector's capacity
266     setInternal(key, values.size());
267
268     // Save vector's elements
269     for (unsigned int i = 0; i < values.size(); ++i) {
270         setInternal(config::key(key, std::to_string(i)),
271                     values[i]);
272     }
273 }
274
275 std::string KVStore::getInternal(const std::string& key, std::string*)
276 {
277     Lock lock(mConnMtx);
278
279     ::sqlite3_bind_text(mGetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
280
281     Transaction transaction(this);
282     ScopedReset scopedReset(mGetValueStmt);
283
284     int ret = ::sqlite3_step(mGetValueStmt->get());
285     if (ret == SQLITE_DONE) {
286         throw ConfigException("No value corresponding to the key: " + key);
287     }
288     if (ret != SQLITE_ROW) {
289         throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
290     }
291
292     return reinterpret_cast<const char*>(sqlite3_column_text(mGetValueStmt->get(), FIRST_COLUMN));
293 }
294
295 std::vector<std::string> KVStore::getInternal(const std::string& key, std::vector<std::string>*)
296 {
297     Lock lock(mConnMtx);
298     Transaction transaction(this);
299
300     unsigned int valuesSize = get<unsigned int>(key);
301     std::vector<std::string> values(valuesSize);
302     if (valuesSize == 0) {
303         return values;
304     }
305
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));
309
310     }
311
312     return values;
313 }
314
315 } // namespace config