a2d8fcaed70fa1a547560b2119275fc4ab8afbf5
[platform/core/appfw/event-system.git] / src / modules / cion / database.hpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #ifndef DATABASE_HPP_
18 #define DATABASE_HPP_
19
20 #include <sqlite3.h>
21
22 #include <functional>
23 #include <map>
24 #include <memory>
25 #include <string>
26 #include <tuple>
27 #include <variant>
28 #include <vector>
29
30 namespace tizen_base {
31
32 template<std::size_t N>
33 struct num {
34   static const constexpr auto value = N;
35 };
36
37 template <class F, std::size_t... Is>
38 void for_(F func, std::index_sequence<Is...>) {
39   (func(num<Is>{}), ...);
40 }
41
42 template <std::size_t N, typename F>
43 void for_(F func) {
44   for_(func, std::make_index_sequence<N>());
45 }
46
47 using DbType = std::variant<int, double, std::string,
48   std::vector<unsigned char>>;
49
50 class AutoDbType {
51  public:
52   AutoDbType() = default;
53   AutoDbType(DbType db_type) : db_type_(db_type) {}
54
55   operator int () {
56     return std::get<int>(db_type_);
57   }
58
59   operator std::string () {
60     return std::get<std::string>(db_type_);
61   }
62
63   operator double () {
64     return std::get<double>(db_type_);
65   }
66
67   operator std::vector<unsigned char> () {
68     return std::get<std::vector<unsigned char>>(db_type_);
69   }
70
71  private:
72   DbType db_type_;
73 };
74
75 using _ = AutoDbType;
76
77 class Database {
78  public:
79   class TransactionGuard {
80    public:
81     TransactionGuard(const TransactionGuard&) = delete;
82     TransactionGuard& operator = (const TransactionGuard&) = delete;
83
84     TransactionGuard(TransactionGuard&& t) noexcept {
85       db_ = t.db_;
86       t.db_ = nullptr;
87     }
88
89     TransactionGuard& operator = (TransactionGuard&& t) noexcept {
90       if (this != &t) {
91         if (db_)
92           sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
93         db_ = t.db_;
94         t.db_ = nullptr;
95       }
96
97       return *this;
98     }
99
100     TransactionGuard(sqlite3* db) : db_(db) {
101       if (sqlite3_exec(db, "BEGIN DEFERRED", NULL, NULL, NULL) != SQLITE_OK) {
102         throw std::runtime_error("begin transaction failed");
103       }
104     }
105
106     ~TransactionGuard() {
107       if (db_)
108         sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
109     }
110
111     int Commit() {
112       int ret = sqlite3_exec(db_, "COMMIT", nullptr, nullptr, nullptr);
113       if (ret != SQLITE_OK) {
114         sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
115       }
116
117       db_ = nullptr;
118       return ret;
119     }
120
121    private:
122     sqlite3* db_ = nullptr;
123   };
124
125   class Sql {
126    public:
127     Sql(std::string query) : query_(std::move(query)) {}
128
129     Sql& Bind(std::string val) {
130       bindings_.push_back(DbType(std::move(val)));
131       return *this;
132     }
133
134     Sql& Bind(int val) {
135       bindings_.push_back(DbType(val));
136       return *this;
137     }
138
139     Sql& Bind(double val) {
140       bindings_.push_back(DbType(val));
141       return *this;
142     }
143
144     Sql& Bind(std::vector<unsigned char> val) {
145       bindings_.push_back(DbType(std::move(val)));
146       return *this;
147     }
148
149     Sql& Bind(int pos, std::string val) {
150       binding_map_[pos] = DbType(std::move(val));
151       return *this;
152     }
153
154     Sql& Bind(int pos, int val) {
155       binding_map_[pos] = DbType(val);
156       return *this;
157     }
158
159     Sql& Bind(int pos, double val) {
160       binding_map_[pos] = DbType(val);
161       return *this;
162     }
163
164     Sql& Bind(int pos, std::vector<unsigned char> val) {
165       binding_map_[pos] = DbType(std::move(val));
166       return *this;
167     }
168
169     Sql& Bind(std::string name, std::string val) {
170       binding_name_map_[std::move(name)] = DbType(std::move(val));
171       return *this;
172     }
173
174     Sql& Bind(std::string name, int val) {
175       binding_name_map_[std::move(name)] = DbType(val);
176       return *this;
177     }
178
179     Sql& Bind(std::string name, double val) {
180       binding_name_map_[std::move(name)] = DbType(val);
181       return *this;
182     }
183
184     Sql& Bind(std::string name, std::vector<unsigned char> val) {
185       binding_name_map_[std::move(name)] = DbType(std::move(val));
186       return *this;
187     }
188
189     const std::vector<DbType>& GetBindings() const {
190       return bindings_;
191     }
192
193     const std::map<int, DbType>& GetBindingMap() const {
194       return binding_map_;
195     }
196
197     const std::map<std::string, DbType>& GetBindingNameMap() const {
198       return binding_name_map_;
199     }
200
201     const std::string& GetQuery() const {
202       return query_;
203     }
204
205    private:
206     std::string query_;
207     std::vector<DbType> bindings_;
208     std::map<int, DbType> binding_map_;
209     std::map<std::string, DbType> binding_name_map_;
210   };
211
212   class Result {
213    public:
214     Result() = default;
215     ~Result() {
216       if (stmt_)
217         sqlite3_finalize(stmt_);
218     }
219
220     Result(const Result&) = delete;
221     Result& operator = (const Result&) = delete;
222
223     Result(Result&& r) noexcept {
224       stmt_ = r.stmt_;
225       r.stmt_ = nullptr;
226     }
227
228     Result& operator = (Result&& r) noexcept {
229       if (this != &r) {
230         if (stmt_)
231           sqlite3_finalize(stmt_);
232         stmt_ = r.stmt_;
233         r.stmt_ = nullptr;
234       }
235
236       return *this;
237     }
238
239     class Record {
240      public:
241       Record(const sqlite3_stmt* stmt) : stmt_(stmt) {}
242
243       AutoDbType Get(int pos) const {
244         sqlite3_stmt* stmt = const_cast<sqlite3_stmt*>(stmt_);
245         int type = sqlite3_column_type(stmt, pos);
246         if (type == SQLITE_NULL)
247           throw std::runtime_error("invalid column");;
248
249         DbType dbt;
250         if (type == SQLITE_TEXT) {
251           dbt = DbType(reinterpret_cast<const char*>(
252               sqlite3_column_text(stmt, pos)));
253         } else if (type == SQLITE_INTEGER) {
254           dbt = DbType(sqlite3_column_int(stmt, pos));
255         } else if (type == SQLITE_FLOAT) {
256           dbt = DbType(sqlite3_column_double(stmt, pos));
257         } else if (type == SQLITE_BLOB) {
258           const unsigned char* val = reinterpret_cast<const unsigned char*>(
259               sqlite3_column_blob(stmt, pos));
260           int len = sqlite3_column_bytes(stmt, pos);
261
262           if (!val || len < 0) {
263             throw std::runtime_error("invalid blob");;
264           } else {
265             dbt = DbType(std::vector<unsigned char>(val, val + len));
266           }
267         } else {
268           throw std::runtime_error("invalid column type");
269         }
270
271         return AutoDbType(dbt);
272       }
273
274       template <typename ...Types>
275       auto Get() const {
276         std::tuple<Types...> t;
277         int pos = 0;
278         for_<std::tuple_size_v<std::tuple<Types...>>>([&] (auto i) {
279           std::get<i.value>(t) = Get(pos++);
280         });
281
282         return t;
283       }
284
285      private:
286       const sqlite3_stmt* stmt_;
287     };
288
289     class Iterator {
290      public:
291       Iterator(sqlite3_stmt* stmt) : stmt_(stmt) {}
292
293       Record operator*() { return Record(stmt_); }
294
295       bool operator != (const Iterator& rhs) const {
296         return stmt_ != rhs.stmt_;
297       }
298
299       void operator ++() {
300         int r = sqlite3_step(stmt_);
301         if (r != SQLITE_ROW)
302           stmt_ = nullptr;
303       }
304
305      private:
306       sqlite3_stmt* stmt_ = nullptr;
307     };
308
309     Iterator begin() const {
310       return Iterator(stmt_);
311     }
312
313     Iterator end() const {
314       return Iterator(nullptr);
315     }
316
317     explicit operator bool() {
318       if (stmt_ == nullptr)
319         return false;
320       return true;
321     }
322
323     explicit operator int() {
324       if (db_ == nullptr)
325         return SQLITE_ERROR;
326       return sqlite3_errcode(db_);
327     }
328
329     operator const char*() {
330       if (db_ == nullptr)
331         return "";
332       return sqlite3_errmsg(db_);
333     }
334
335    private:
336     friend class Database;
337     Result(sqlite3_stmt* stmt, sqlite3* db) : stmt_(stmt), db_(db) {}
338
339     sqlite3_stmt* stmt_ = nullptr;
340     sqlite3* db_ = nullptr;
341   };
342
343   Database(std::string db, int flags) {
344     int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
345     if (r != SQLITE_OK)
346       throw std::runtime_error("open failed");
347   }
348
349   Database(std::string db, int flags, std::function<bool(int)> busy_handler) {
350     int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
351     if (r != SQLITE_OK)
352       throw std::runtime_error("sqlite3_open_v2() failed");
353
354     busy_handler_ = std::move(busy_handler);
355     r = sqlite3_busy_handler(db_, [](void* data, int count) {
356       Database* pDb = static_cast<Database*>(data);
357       if (pDb->busy_handler_(count))
358         return 1;
359       return 0;
360     }, this);
361
362     if (r != SQLITE_OK) {
363       sqlite3_close_v2(db_);
364       throw std::runtime_error("sqlite3_busy_handler() failed");
365     }
366   }
367
368   ~Database() {
369     if (db_)
370       sqlite3_close_v2(db_);
371   }
372
373   Database() = default;
374   Database(const Database&) = delete;
375   Database& operator = (const Database&) = delete;
376
377   Database(Database&& db) noexcept {
378     db_ = db.db_;
379     db.db_ = nullptr;
380   }
381
382   explicit operator bool() {
383     if (db_ == nullptr)
384       return false;
385     return true;
386   }
387
388   Database& operator = (Database&& db) noexcept {
389     if (this != &db) {
390         if (db_)
391           sqlite3_close_v2(db_);
392         db_ = db.db_;
393         db.db_ = nullptr;
394     }
395
396     return *this;
397   }
398
399   TransactionGuard CreateTransactionGuard() {
400     return TransactionGuard(db_);
401   }
402
403   Result Exec(const Sql& sql) const {
404     if (!db_)
405       throw std::runtime_error("Not opened");
406
407     sqlite3_stmt* stmt = nullptr;
408     int r = sqlite3_prepare_v2(db_, sql.GetQuery().c_str(),
409         -1, &stmt, nullptr);
410     if (r != SQLITE_OK) {
411       return { nullptr, nullptr };
412     }
413
414     std::unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)*> stmt_auto(stmt,
415         sqlite3_finalize);
416     int pos = 1;
417     for (const auto& i : sql.GetBindings()) {
418       Bind(pos++, i, stmt);
419     }
420
421     for (const auto& i : sql.GetBindingMap()) {
422       Bind(i.first, i.second, stmt);
423     }
424
425     for (const auto& i : sql.GetBindingNameMap()) {
426       int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
427       if (pos == 0)
428         throw std::runtime_error("Invalid binding");
429       Bind(pos, i.second, stmt);
430     }
431
432     r = sqlite3_step(stmt);
433     if (r != SQLITE_ROW && r != SQLITE_DONE) {
434       return { nullptr, db_ };
435     }
436
437     return { stmt_auto.release(), db_ };
438   }
439
440  private:
441   void Bind(int pos, const DbType& type, sqlite3_stmt* stmt) const {
442     int r;
443     if (const std::string* pstr = std::get_if<std::string>(&type)) {
444       r = sqlite3_bind_text(stmt, pos, (*pstr).c_str(), -1,
445           SQLITE_TRANSIENT);
446     } else if (const int* pint = std::get_if<int>(&type)) {
447       r = sqlite3_bind_int(stmt, pos, (*pint));
448     } else if (const double* pdouble = std::get_if<double>(&type)) {
449       r = sqlite3_bind_double(stmt, pos, (*pdouble));
450     } else if (const std::vector<unsigned char>* pvector =
451         std::get_if<std::vector<unsigned char>>(&type)) {
452       r = sqlite3_bind_blob(stmt, pos, (*pvector).data(),
453           (*pvector).size(), nullptr);
454     } else {
455       r = -1;
456     }
457
458     if (r != SQLITE_OK) {
459       throw std::runtime_error("Invalid binding");
460     }
461   }
462
463  private:
464   sqlite3* db_ = nullptr;
465   std::function<bool(int)> busy_handler_;
466 };
467
468 }  // namespace tizen_base
469
470 #endif  // DATABASE_HPP_