cc174e4e13e282aec7941b62575100a083ad1777
[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     : mConn(path)
108 {
109     setupDb();
110     prepareStatements();
111 }
112
113 KVStore::~KVStore()
114 {
115 }
116
117 void KVStore::setupDb()
118 {
119     Lock lock(mConnMtx);
120     Transaction transaction(mConn);
121     mConn.exec("CREATE TABLE IF NOT EXISTS data (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
122 }
123
124 void KVStore::prepareStatements()
125 {
126     mGetValueStmt.reset(
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 ? ||'*' "));
132     mGetSizeStmt.reset(
133         new sqlite3::Statement(mConn, "SELECT count(key) FROM data"));
134     mSetValueStmt.reset(
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 ? ||'*' "));
138 }
139
140 void KVStore::clear()
141 {
142     Lock lock(mConnMtx);
143     Transaction transaction(mConn);
144     mConn.exec("DELETE FROM data");
145 }
146
147 unsigned int KVStore::size()
148 {
149     Lock lock(mConnMtx);
150     ScopedReset scopedReset(mGetSizeStmt);
151
152     if (::sqlite3_step(mGetSizeStmt->get()) != SQLITE_ROW) {
153         throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
154     }
155
156     return static_cast<unsigned int>(::sqlite3_column_int(mGetSizeStmt->get(), FIRST_COLUMN));
157 }
158
159 unsigned int KVStore::count(const std::string& key)
160 {
161     Lock lock(mConnMtx);
162     Transaction transaction(mConn);
163     return countInternal(key);
164 }
165
166 unsigned int KVStore::countInternal(const std::string& key)
167 {
168     ScopedReset scopedReset(mGetValueCountStmt);
169
170     ::sqlite3_bind_text(mGetValueCountStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
171
172     if (::sqlite3_step(mGetValueCountStmt->get()) != SQLITE_ROW) {
173         throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
174     }
175
176     return static_cast<unsigned int>(::sqlite3_column_int(mGetValueCountStmt->get(), FIRST_COLUMN));
177 }
178
179 void KVStore::remove(const std::string& key)
180 {
181     Lock lock(mConnMtx);
182     Transaction transaction(mConn);
183     removeInternal(key);
184 }
185
186 void KVStore::removeInternal(const std::string& key)
187 {
188     ScopedReset scopedReset(mRemoveValuesStmt);
189
190     ::sqlite3_bind_text(mRemoveValuesStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
191
192     if (::sqlite3_step(mRemoveValuesStmt->get()) != SQLITE_DONE) {
193         throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
194     }
195 }
196
197 void KVStore::setInternal(const std::string& key, const std::string& value)
198 {
199     Lock lock(mConnMtx);
200
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);
203
204     Transaction transaction(mConn);
205     ScopedReset scopedReset(mSetValueStmt);
206
207     if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) {
208         throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
209     }
210 }
211
212 void KVStore::setInternal(const std::string& key, const std::initializer_list<std::string>& values)
213 {
214     setInternal(key, std::vector<std::string>(values));
215 }
216
217 void KVStore::setInternal(const std::string& key, const std::vector<std::string>& values)
218 {
219     if (values.size() > std::numeric_limits<unsigned int>::max()) {
220         throw ConfigException("Too many values to insert");
221     }
222
223     Lock lock(mConnMtx);
224     Transaction transaction(mConn);
225
226     removeInternal(key);
227
228     for (unsigned int i = 0; i < values.size(); ++i) {
229         ScopedReset scopedReset(mSetValueStmt);
230         const std::string modifiedKey = key + "." + std::to_string(i);;
231
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);
234
235         if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) {
236             throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
237         }
238     }
239 }
240
241 std::string KVStore::getInternal(const std::string& key, std::string*)
242 {
243     Lock lock(mConnMtx);
244
245     ::sqlite3_bind_text(mGetValueStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
246
247     Transaction transaction(mConn);
248     ScopedReset scopedReset(mGetValueStmt);
249
250     int ret = ::sqlite3_step(mGetValueStmt->get());
251     if (ret == SQLITE_DONE) {
252         throw ConfigException("No value corresponding to the key");
253     }
254     if (ret != SQLITE_ROW) {
255         throw ConfigException("Error during stepping: " + mConn.getErrorMessage());
256     }
257
258     return reinterpret_cast<const char*>(sqlite3_column_text(mGetValueStmt->get(), FIRST_COLUMN));
259 }
260
261 std::vector<std::string> KVStore::getInternal(const std::string& key, std::vector<std::string>*)
262 {
263     Lock lock(mConnMtx);
264
265     ::sqlite3_bind_text(mGetValueListStmt->get(), 1, escape(key).c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
266
267     Transaction transaction(mConn);
268     ScopedReset scopedReset(mGetValueListStmt);
269
270     unsigned int valuesSize = countInternal(key);
271     if (valuesSize == 0) {
272         throw ConfigException("No value corresponding to the key");
273     }
274
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());
279         }
280         value = reinterpret_cast<const char*>(
281                     sqlite3_column_text(mGetValueListStmt->get(), FIRST_COLUMN));
282     }
283
284     return values;
285 }
286
287 } // namespace config