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