Add sub package 'tizen-database'
[platform/core/base/bundle.git] / tizen-database / 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 TIZEN_DATABASE_DATABASE_HPP_
18 #define TIZEN_DATABASE_DATABASE_HPP_
19
20 #include <sqlite3.h>
21
22 #include <functional>
23 #include <list>
24 #include <map>
25 #include <memory>
26 #include <string>
27 #include <tuple>
28 #include <utility>
29 #include <variant>
30 #include <vector>
31
32 namespace tizen_base {
33
34 template<std::size_t N>
35 struct num {
36   static const constexpr auto value = N;
37 };
38
39 template <class F, std::size_t... Is>
40 void for_(F func, std::index_sequence<Is...>) {
41   (func(num<Is>{}), ...);
42 }
43
44 template <std::size_t N, typename F>
45 void for_(F func) {
46   for_(func, std::make_index_sequence<N>());
47 }
48
49 using DbType = std::variant<int, double, std::string,
50   std::vector<unsigned char>>;
51
52 class AutoDbType {
53  public:
54   AutoDbType() = default;
55   AutoDbType(DbType db_type) : db_type_(db_type) {}
56
57   operator int () {
58     return std::get<int>(db_type_);
59   }
60
61   operator std::string () {
62     return std::get<std::string>(db_type_);
63   }
64
65   operator double () {
66     return std::get<double>(db_type_);
67   }
68
69   operator std::vector<unsigned char> () {
70     return std::get<std::vector<unsigned char>>(db_type_);
71   }
72
73  private:
74   DbType db_type_;
75 };
76
77 using _ = AutoDbType;
78
79 class Database {
80  public:
81   class TransactionGuard {
82    public:
83     TransactionGuard(const TransactionGuard&) = delete;
84     TransactionGuard& operator = (const TransactionGuard&) = delete;
85
86     TransactionGuard(TransactionGuard&& t) noexcept {
87       db_ = t.db_;
88       t.db_ = nullptr;
89     }
90
91     TransactionGuard& operator = (TransactionGuard&& t) noexcept {
92       if (this != &t) {
93         if (db_)
94           sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
95         db_ = t.db_;
96         t.db_ = nullptr;
97       }
98
99       return *this;
100     }
101
102     TransactionGuard(sqlite3* db) : db_(db) {
103       if (sqlite3_exec(db, "BEGIN DEFERRED", nullptr, nullptr, nullptr)
104           != SQLITE_OK) {
105         throw std::runtime_error("begin transaction failed");
106       }
107     }
108
109     ~TransactionGuard() {
110       if (db_)
111         sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
112     }
113
114     int Commit() {
115       int ret = sqlite3_exec(db_, "COMMIT", nullptr, nullptr, nullptr);
116       if (ret != SQLITE_OK) {
117         sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
118       }
119
120       db_ = nullptr;
121       return ret;
122     }
123
124    private:
125     sqlite3* db_ = nullptr;
126   };
127
128   class Sql {
129    public:
130     Sql(std::string query) : query_(std::move(query)) {}
131
132     Sql& Bind(std::string val) {
133       bindings_.push_back(DbType(std::move(val)));
134       return *this;
135     }
136
137     Sql& Bind(int val) {
138       bindings_.push_back(DbType(val));
139       return *this;
140     }
141
142     Sql& Bind(double val) {
143       bindings_.push_back(DbType(val));
144       return *this;
145     }
146
147     Sql& Bind(std::vector<unsigned char> val) {
148       bindings_.push_back(DbType(std::move(val)));
149       return *this;
150     }
151
152     Sql& Bind(int pos, std::string val) {
153       binding_map_[pos] = DbType(std::move(val));
154       return *this;
155     }
156
157     Sql& Bind(int pos, int val) {
158       binding_map_[pos] = DbType(val);
159       return *this;
160     }
161
162     Sql& Bind(int pos, double val) {
163       binding_map_[pos] = DbType(val);
164       return *this;
165     }
166
167     Sql& Bind(int pos, std::vector<unsigned char> val) {
168       binding_map_[pos] = DbType(std::move(val));
169       return *this;
170     }
171
172     Sql& Bind(std::string name, std::string val) {
173       binding_name_map_[std::move(name)] = DbType(std::move(val));
174       return *this;
175     }
176
177     Sql& Bind(std::string name, int val) {
178       binding_name_map_[std::move(name)] = DbType(val);
179       return *this;
180     }
181
182     Sql& Bind(std::string name, double val) {
183       binding_name_map_[std::move(name)] = DbType(val);
184       return *this;
185     }
186
187     Sql& Bind(std::string name, std::vector<unsigned char> val) {
188       binding_name_map_[std::move(name)] = DbType(std::move(val));
189       return *this;
190     }
191
192     const std::vector<DbType>& GetBindings() const {
193       return bindings_;
194     }
195
196     const std::map<int, DbType>& GetBindingMap() const {
197       return binding_map_;
198     }
199
200     const std::map<std::string, DbType>& GetBindingNameMap() const {
201       return binding_name_map_;
202     }
203
204     const std::string& GetQuery() const {
205       return query_;
206     }
207
208    private:
209     std::string query_;
210     std::vector<DbType> bindings_;
211     std::map<int, DbType> binding_map_;
212     std::map<std::string, DbType> binding_name_map_;
213   };
214
215   class Result {
216    public:
217     Result() = default;
218     ~Result() {
219       if (stmt_)
220         sqlite3_finalize(stmt_);
221     }
222
223     Result(const Result&) = delete;
224     Result& operator = (const Result&) = delete;
225
226     Result(Result&& r) noexcept {
227       stmt_ = r.stmt_;
228       r.stmt_ = nullptr;
229     }
230
231     Result& operator = (Result&& r) noexcept {
232       if (this != &r) {
233         if (stmt_)
234           sqlite3_finalize(stmt_);
235         stmt_ = r.stmt_;
236         r.stmt_ = nullptr;
237       }
238
239       return *this;
240     }
241
242     class Record {
243      public:
244       Record(const sqlite3_stmt* stmt) : stmt_(stmt) {}
245
246       AutoDbType Get(int pos) const {
247         sqlite3_stmt* stmt = const_cast<sqlite3_stmt*>(stmt_);
248         int type = sqlite3_column_type(stmt, pos);
249         if (type == SQLITE_NULL)
250           throw std::runtime_error("invalid column");
251
252         DbType dbt;
253         if (type == SQLITE_TEXT) {
254           dbt = DbType(reinterpret_cast<const char*>(
255               sqlite3_column_text(stmt, pos)));
256         } else if (type == SQLITE_INTEGER) {
257           dbt = DbType(sqlite3_column_int(stmt, pos));
258         } else if (type == SQLITE_FLOAT) {
259           dbt = DbType(sqlite3_column_double(stmt, pos));
260         } else if (type == SQLITE_BLOB) {
261           const unsigned char* val = reinterpret_cast<const unsigned char*>(
262               sqlite3_column_blob(stmt, pos));
263           int len = sqlite3_column_bytes(stmt, pos);
264
265           if (!val || len < 0) {
266             throw std::runtime_error("invalid blob");;
267           } else {
268             dbt = DbType(std::vector<unsigned char>(val, val + len));
269           }
270         } else {
271           throw std::runtime_error("invalid column type");
272         }
273
274         return AutoDbType(dbt);
275       }
276
277       template <typename ...Types>
278       auto Get() const {
279         std::tuple<Types...> t;
280         int pos = 0;
281         for_<std::tuple_size_v<std::tuple<Types...>>>([&] (auto i) {
282           std::get<i.value>(t) = Get(pos++);
283         });
284
285         return t;
286       }
287
288      private:
289       const sqlite3_stmt* stmt_;
290     };
291
292     class Iterator {
293      public:
294       Iterator(sqlite3_stmt* stmt) : stmt_(stmt) {}
295
296       Record operator*() { return Record(stmt_); }
297
298       bool operator != (const Iterator& rhs) const {
299         return stmt_ != rhs.stmt_;
300       }
301
302       void operator ++() {
303         int r = sqlite3_step(stmt_);
304         if (r != SQLITE_ROW)
305           stmt_ = nullptr;
306       }
307
308      private:
309       sqlite3_stmt* stmt_ = nullptr;
310     };
311
312     Iterator begin() const {
313       return Iterator(stmt_);
314     }
315
316     Iterator end() const {
317       return Iterator(nullptr);
318     }
319
320     explicit operator bool() {
321       if (stmt_ == nullptr)
322         return false;
323       return true;
324     }
325
326     explicit operator int() {
327       if (db_ == nullptr)
328         return SQLITE_ERROR;
329       return sqlite3_errcode(db_);
330     }
331
332     operator const char*() {
333       if (db_ == nullptr)
334         return "";
335       return sqlite3_errmsg(db_);
336     }
337
338     template <class T>
339     std::vector<T> ToVector() {
340       if (stmt_ == nullptr)
341         return {};
342
343       std::vector<T> vec;
344       for (const auto& rec : *this) {
345         T t;
346         t(rec);
347         vec.push_back(std::move(t));
348       }
349
350       return vec;
351     }
352
353     template <class T>
354     std::list<T> ToList() {
355       if (stmt_ == nullptr)
356         return {};
357
358       std::list<T> l;
359       for (const auto& rec : *this) {
360         T t;
361         t(rec);
362         l.push_back(std::move(t));
363       }
364
365       return l;
366     }
367
368     template <class T>
369     std::optional<T> GetFirst() {
370       if (stmt_ == nullptr)
371         return std::nullopt;
372       for (const auto& rec : *this) {
373         T t;
374         t(rec);
375         return t;
376       }
377
378       return std::nullopt;
379     }
380
381     std::optional<Record> GetFirstRecord() {
382       if (stmt_ == nullptr)
383         return std::nullopt;
384       for (const auto& rec : *this) {
385         return rec;
386       }
387
388       return std::nullopt;
389     }
390
391    private:
392     friend class Database;
393     Result(sqlite3_stmt* stmt, sqlite3* db) : stmt_(stmt), db_(db) {}
394
395     sqlite3_stmt* stmt_ = nullptr;
396     sqlite3* db_ = nullptr;
397   };
398
399   Database(std::string db, int flags) {
400     int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
401     if (r != SQLITE_OK)
402       throw std::runtime_error("open failed");
403   }
404
405   Database(std::string db, int flags, std::function<bool(int)> busy_handler) {
406     int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
407     if (r != SQLITE_OK)
408       throw std::runtime_error("sqlite3_open_v2() failed");
409
410     busy_handler_ = std::move(busy_handler);
411     r = sqlite3_busy_handler(db_, [](void* data, int count) {
412       Database* pDb = static_cast<Database*>(data);
413       if (pDb->busy_handler_(count))
414         return 1;
415       return 0;
416     }, this);
417
418     if (r != SQLITE_OK) {
419       sqlite3_close_v2(db_);
420       throw std::runtime_error("sqlite3_busy_handler() failed");
421     }
422   }
423
424   ~Database() {
425     if (db_)
426       sqlite3_close_v2(db_);
427   }
428
429   Database() = default;
430   Database(const Database&) = delete;
431   Database& operator = (const Database&) = delete;
432
433   Database(Database&& db) noexcept {
434     db_ = db.db_;
435     db.db_ = nullptr;
436   }
437
438   explicit operator bool() {
439     if (db_ == nullptr)
440       return false;
441     return true;
442   }
443
444   Database& operator = (Database&& db) noexcept {
445     if (this != &db) {
446         if (db_)
447           sqlite3_close_v2(db_);
448         db_ = db.db_;
449         db.db_ = nullptr;
450     }
451
452     return *this;
453   }
454
455   TransactionGuard CreateTransactionGuard() {
456     return TransactionGuard(db_);
457   }
458
459   Result Exec(const Sql& sql) const {
460     if (!db_)
461       throw std::runtime_error("Not opened");
462
463     sqlite3_stmt* stmt = nullptr;
464     int r = sqlite3_prepare_v2(db_, sql.GetQuery().c_str(),
465         -1, &stmt, nullptr);
466     if (r != SQLITE_OK) {
467       return { nullptr, nullptr };
468     }
469
470     std::unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)*> stmt_auto(stmt,
471         sqlite3_finalize);
472     int pos = 1;
473     for (const auto& i : sql.GetBindings()) {
474       Bind(pos++, i, stmt);
475     }
476
477     for (const auto& i : sql.GetBindingMap()) {
478       Bind(i.first, i.second, stmt);
479     }
480
481     for (const auto& i : sql.GetBindingNameMap()) {
482       int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
483       if (pos == 0)
484         throw std::runtime_error("Invalid binding");
485       Bind(pos, i.second, stmt);
486     }
487
488     r = sqlite3_step(stmt);
489     if (r != SQLITE_ROW && r != SQLITE_DONE) {
490       return { nullptr, db_ };
491     }
492
493     return { stmt_auto.release(), db_ };
494   }
495
496  private:
497   void Bind(int pos, const DbType& type, sqlite3_stmt* stmt) const {
498     int r;
499     if (const std::string* pstr = std::get_if<std::string>(&type)) {
500       r = sqlite3_bind_text(stmt, pos, (*pstr).c_str(), -1,
501           SQLITE_TRANSIENT);
502     } else if (const int* pint = std::get_if<int>(&type)) {
503       r = sqlite3_bind_int(stmt, pos, (*pint));
504     } else if (const double* pdouble = std::get_if<double>(&type)) {
505       r = sqlite3_bind_double(stmt, pos, (*pdouble));
506     } else if (const std::vector<unsigned char>* pvector =
507         std::get_if<std::vector<unsigned char>>(&type)) {
508       r = sqlite3_bind_blob(stmt, pos, (*pvector).data(),
509           (*pvector).size(), nullptr);
510     } else {
511       r = -1;
512     }
513
514     if (r != SQLITE_OK) {
515       throw std::runtime_error("Invalid binding");
516     }
517   }
518
519  private:
520   sqlite3* db_ = nullptr;
521   std::function<bool(int)> busy_handler_;
522 };
523
524 }  // namespace tizen_base
525
526 #endif  // TIZEN_DATABASE_DATABASE_HPP_