Add some methods in class 'Sql'
[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   explicit 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     explicit 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       if (empty_string_as_null_ && val.empty())
176         bindings_.push_back(DbType(std::nullopt));
177       else
178         bindings_.push_back(DbType(std::move(val)));
179       return *this;
180     }
181
182     Sql& Bind(int val) {
183       bindings_.push_back(DbType(val));
184       return *this;
185     }
186
187     Sql& Bind(double val) {
188       bindings_.push_back(DbType(val));
189       return *this;
190     }
191
192     Sql& Bind(std::vector<unsigned char> val) {
193       if (empty_vector_as_null_ && val.empty())
194         bindings_.push_back(DbType(std::nullopt));
195       else
196         bindings_.push_back(DbType(std::move(val)));
197       return *this;
198     }
199
200     Sql& Bind(const std::nullopt_t val) {
201       bindings_.push_back(DbType(val));
202       return *this;
203     }
204
205     Sql& Bind(int pos, std::string val) {
206       if (empty_string_as_null_ && val.empty())
207         binding_map_[pos] = DbType(std::nullopt);
208       else
209         binding_map_[pos] = DbType(std::move(val));
210       return *this;
211     }
212
213     Sql& Bind(int pos, int val) {
214       binding_map_[pos] = DbType(val);
215       return *this;
216     }
217
218     Sql& Bind(int pos, double val) {
219       binding_map_[pos] = DbType(val);
220       return *this;
221     }
222
223     Sql& Bind(int pos, std::vector<unsigned char> val) {
224       if (empty_vector_as_null_ && val.empty())
225         binding_map_[pos] = DbType(std::nullopt);
226       else
227         binding_map_[pos] = DbType(std::move(val));
228       return *this;
229     }
230
231     Sql& Bind(int pos, const std::nullopt_t& val) {
232       binding_map_[pos] = DbType(val);
233       return *this;
234     }
235
236     Sql& Bind(std::string name, std::string val) {
237       if (empty_string_as_null_ && val.empty())
238         binding_name_map_[std::move(name)] = DbType(std::nullopt);
239       else
240         binding_name_map_[std::move(name)] = DbType(std::move(val));
241       return *this;
242     }
243
244     Sql& Bind(std::string name, int val) {
245       binding_name_map_[std::move(name)] = DbType(val);
246       return *this;
247     }
248
249     Sql& Bind(std::string name, double val) {
250       binding_name_map_[std::move(name)] = DbType(val);
251       return *this;
252     }
253
254     Sql& Bind(std::string name, std::vector<unsigned char> val) {
255       if (empty_vector_as_null_ && val.empty())
256         binding_name_map_[std::move(name)] = DbType(std::nullopt);
257       else
258         binding_name_map_[std::move(name)] = DbType(std::move(val));
259       return *this;
260     }
261
262     Sql& Bind(std::string name, const std::nullopt_t& val) {
263       binding_name_map_[std::move(name)] = DbType(val);
264       return *this;
265     }
266
267     const std::vector<DbType>& GetBindings() const {
268       return bindings_;
269     }
270
271     const std::map<int, DbType>& GetBindingMap() const {
272       return binding_map_;
273     }
274
275     const std::map<std::string, DbType>& GetBindingNameMap() const {
276       return binding_name_map_;
277     }
278
279     const std::string& GetQuery() const {
280       return query_;
281     }
282
283     Sql& SetEmptyStringAsNull(bool as_null) {
284       empty_string_as_null_ = as_null;
285       return *this;
286     }
287
288     Sql& SetEmptyVectorAsNull(bool as_null) {
289       empty_vector_as_null_ = as_null;
290       return *this;
291     }
292
293     Sql& Reset() {
294       bindings_.clear();
295       binding_map_.clear();
296       binding_name_map_.clear();
297       empty_string_as_null_ = false;
298       empty_vector_as_null_ = false;
299       return *this;
300     }
301
302    private:
303     std::string query_;
304     std::vector<DbType> bindings_;
305     std::map<int, DbType> binding_map_;
306     std::map<std::string, DbType> binding_name_map_;
307     bool empty_string_as_null_ = false;
308     bool empty_vector_as_null_ = false;
309   };
310
311   class Result {
312    public:
313     Result() = default;
314     ~Result() {
315       if (stmt_)
316         sqlite3_finalize(stmt_);
317     }
318
319     Result(const Result&) = delete;
320     Result& operator = (const Result&) = delete;
321
322     Result(Result&& r) noexcept {
323       stmt_ = r.stmt_;
324       r.stmt_ = nullptr;
325     }
326
327     Result& operator = (Result&& r) noexcept {
328       if (this != &r) {
329         if (stmt_)
330           sqlite3_finalize(stmt_);
331         stmt_ = r.stmt_;
332         r.stmt_ = nullptr;
333       }
334
335       return *this;
336     }
337
338     class Record {
339      public:
340       explicit Record(const sqlite3_stmt* stmt) : stmt_(stmt) {}
341
342       AutoDbType Get(int pos) const {
343         sqlite3_stmt* stmt = const_cast<sqlite3_stmt*>(stmt_);
344         int type = sqlite3_column_type(stmt, pos);
345
346         DbType dbt;
347         if (type == SQLITE_TEXT) {
348           dbt = DbType(reinterpret_cast<const char*>(
349               sqlite3_column_text(stmt, pos)));
350         } else if (type == SQLITE_INTEGER) {
351           dbt = DbType(sqlite3_column_int(stmt, pos));
352         } else if (type == SQLITE_FLOAT) {
353           dbt = DbType(sqlite3_column_double(stmt, pos));
354         } else if (type == SQLITE_BLOB) {
355           const unsigned char* val = reinterpret_cast<const unsigned char*>(
356               sqlite3_column_blob(stmt, pos));
357           int len = sqlite3_column_bytes(stmt, pos);
358
359           if (!val || len < 0) {
360             throw std::runtime_error("invalid blob");;
361           } else {
362             dbt = DbType(std::vector<unsigned char>(val, val + len));
363           }
364         } else if (type == SQLITE_NULL) {
365           dbt = DbType(std::nullopt);
366         } else {
367           throw std::runtime_error("invalid column type");
368         }
369
370         return AutoDbType(dbt);
371       }
372
373       template <typename ...Types>
374       auto Get() const {
375         std::tuple<Types...> t;
376         int pos = 0;
377         for_<std::tuple_size_v<std::tuple<Types...>>>([&] (auto i) {
378           std::get<i.value>(t) = Get(pos++);
379         });
380
381         return t;
382       }
383
384      private:
385       const sqlite3_stmt* stmt_;
386     };
387
388     class Iterator {
389      public:
390       explicit Iterator(sqlite3_stmt* stmt) : stmt_(stmt) {}
391
392       Record operator*() { return Record(stmt_); }
393
394       bool operator != (const Iterator& rhs) const {
395         return stmt_ != rhs.stmt_;
396       }
397
398       void operator ++() {
399         int r = sqlite3_step(stmt_);
400         if (r != SQLITE_ROW)
401           stmt_ = nullptr;
402       }
403
404      private:
405       sqlite3_stmt* stmt_ = nullptr;
406     };
407
408     Iterator begin() const {
409       return Iterator(stmt_);
410     }
411
412     Iterator end() const {
413       return Iterator(nullptr);
414     }
415
416     explicit operator bool() {
417       if (stmt_ == nullptr)
418         return false;
419       return true;
420     }
421
422     explicit operator int() {
423       if (db_ == nullptr)
424         return SQLITE_ERROR;
425       return sqlite3_errcode(db_);
426     }
427
428     operator const char*() {
429       if (db_ == nullptr)
430         return "";
431       return sqlite3_errmsg(db_);
432     }
433
434     template <class T>
435     std::vector<T> ToVector() {
436       if (stmt_ == nullptr)
437         return {};
438
439       std::vector<T> vec;
440       for (const auto& rec : *this) {
441         T t;
442         t(rec);
443         vec.push_back(std::move(t));
444       }
445
446       return vec;
447     }
448
449     template <class T>
450     std::list<T> ToList() {
451       if (stmt_ == nullptr)
452         return {};
453
454       std::list<T> l;
455       for (const auto& rec : *this) {
456         T t;
457         t(rec);
458         l.push_back(std::move(t));
459       }
460
461       return l;
462     }
463
464     template <class T>
465     std::optional<T> GetFirst() {
466       if (stmt_ == nullptr)
467         return std::nullopt;
468       for (const auto& rec : *this) {
469         T t;
470         t(rec);
471         return t;
472       }
473
474       return std::nullopt;
475     }
476
477     std::optional<Record> GetFirstRecord() {
478       if (stmt_ == nullptr)
479         return std::nullopt;
480       for (const auto& rec : *this) {
481         return rec;
482       }
483
484       return std::nullopt;
485     }
486
487     sqlite3_stmt* GetRaw() const {
488       return stmt_;
489     }
490
491     const std::string& GetQuery() const {
492       return query_;
493     }
494
495    private:
496     friend class Database;
497     Result(sqlite3_stmt* stmt, sqlite3* db, std::string query)
498         : stmt_(stmt), db_(db), query_(std::move(query)) {}
499
500     sqlite3_stmt* stmt_ = nullptr;
501     sqlite3* db_ = nullptr;
502     std::string query_;
503   };
504
505   Database(std::string db, int flags) {
506     int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
507     if (r != SQLITE_OK)
508       throw std::runtime_error("open failed");
509   }
510
511   Database(std::string db, int flags, std::function<bool(int)> busy_handler) {
512     int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
513     if (r != SQLITE_OK)
514       throw std::runtime_error("sqlite3_open_v2() failed");
515
516     busy_handler_ = std::move(busy_handler);
517     r = sqlite3_busy_handler(db_, [](void* data, int count) {
518       Database* pDb = static_cast<Database*>(data);
519       if (pDb->busy_handler_(count))
520         return 1;
521       return 0;
522     }, this);
523
524     if (r != SQLITE_OK) {
525       sqlite3_close_v2(db_);
526       throw std::runtime_error("sqlite3_busy_handler() failed");
527     }
528   }
529
530   ~Database() {
531     if (db_)
532       sqlite3_close_v2(db_);
533   }
534
535   Database() = default;
536   Database(const Database&) = delete;
537   Database& operator = (const Database&) = delete;
538
539   Database(Database&& db) noexcept {
540     db_ = db.db_;
541     db.db_ = nullptr;
542   }
543
544   explicit operator bool() {
545     if (db_ == nullptr)
546       return false;
547     return true;
548   }
549
550   Database& operator = (Database&& db) noexcept {
551     if (this != &db) {
552         if (db_)
553           sqlite3_close_v2(db_);
554         db_ = db.db_;
555         db.db_ = nullptr;
556     }
557
558     return *this;
559   }
560
561   TransactionGuard CreateTransactionGuard() {
562     return TransactionGuard(db_);
563   }
564
565   Result Exec(const Sql& sql) const {
566     if (!db_)
567       throw std::runtime_error("Not opened");
568
569     sqlite3_stmt* stmt = nullptr;
570     int r = sqlite3_prepare_v2(db_, sql.GetQuery().c_str(),
571         -1, &stmt, nullptr);
572     if (r != SQLITE_OK) {
573       return { nullptr, nullptr, "" };
574     }
575
576     std::unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)*> stmt_auto(stmt,
577         sqlite3_finalize);
578     int pos = 1;
579     for (const auto& i : sql.GetBindings()) {
580       Bind(pos++, i, stmt);
581     }
582
583     for (const auto& i : sql.GetBindingMap()) {
584       Bind(i.first, i.second, stmt);
585     }
586
587     for (const auto& i : sql.GetBindingNameMap()) {
588       int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
589       if (pos == 0)
590         throw std::runtime_error("Invalid binding");
591       Bind(pos, i.second, stmt);
592     }
593
594     r = sqlite3_step(stmt);
595     if (r != SQLITE_ROW && r != SQLITE_DONE) {
596       return { nullptr, db_, "" };
597     }
598
599     return { stmt_auto.release(), db_, sql.GetQuery() };
600   }
601
602   bool Exec(const Sql& sql, const Result& previous_stmt) {
603     if (sql.GetQuery() != previous_stmt.GetQuery())
604       throw std::runtime_error("Query is different");
605
606     sqlite3_stmt* stmt = previous_stmt.GetRaw();
607     if (!stmt)
608       return false;
609
610     int r = sqlite3_reset(stmt);
611     if (r != SQLITE_ROW && r != SQLITE_DONE && r != SQLITE_OK)
612       return false;
613
614     int pos = 1;
615     for (const auto& i : sql.GetBindings()) {
616       Bind(pos++, i, stmt);
617     }
618
619     for (const auto& i : sql.GetBindingMap()) {
620       Bind(i.first, i.second, stmt);
621     }
622
623     for (const auto& i : sql.GetBindingNameMap()) {
624       int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
625       if (pos == 0)
626         throw std::runtime_error("Invalid binding");
627       Bind(pos, i.second, stmt);
628     }
629
630     r = sqlite3_step(stmt);
631     if (r != SQLITE_ROW && r != SQLITE_DONE)
632       return false;
633
634     return true;
635   }
636
637  private:
638   void Bind(int pos, const DbType& type, sqlite3_stmt* stmt) const {
639     int r;
640     if (!type) {
641       r = sqlite3_bind_null(stmt, pos);
642       if (r != SQLITE_OK) {
643         throw std::runtime_error("Invalid binding");
644       }
645
646       return;
647     }
648
649     if (const std::string* pstr = std::get_if<std::string>(&(*type))) {
650       r = sqlite3_bind_text(stmt, pos, (*pstr).c_str(), -1,
651           SQLITE_TRANSIENT);
652     } else if (const int* pint = std::get_if<int>(&(*type))) {
653       r = sqlite3_bind_int(stmt, pos, (*pint));
654     } else if (const double* pdouble = std::get_if<double>(&(*type))) {
655       r = sqlite3_bind_double(stmt, pos, (*pdouble));
656     } else if (const std::vector<unsigned char>* pvector =
657         std::get_if<std::vector<unsigned char>>(&(*type))) {
658       r = sqlite3_bind_blob(stmt, pos, (*pvector).data(),
659           (*pvector).size(), nullptr);
660     } else {
661       r = -1;
662     }
663
664     if (r != SQLITE_OK) {
665       throw std::runtime_error("Invalid binding");
666     }
667   }
668
669  private:
670   sqlite3* db_ = nullptr;
671   std::function<bool(int)> busy_handler_;
672 };
673
674 }  // namespace tizen_base
675
676 #endif  // TIZEN_DATABASE_DATABASE_HPP_