Add overloading Bind() methods
[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<int64_t, double, std::string,
51     std::vector<unsigned char>>>;
52
53 class DbException : public std::runtime_error {
54  public:
55   explicit DbException(const std::string& msg, int code = SQLITE_ERROR)
56       : std::runtime_error(msg), code_(code) {
57   }
58
59   DbException(const std::string& msg, int code, const std::string& file,
60       int line) : std::runtime_error(msg), code_(code) {
61     msg_ = msg + " at " + file + ":" + std::to_string(line);
62   }
63
64   int code() const {
65     return code_;
66   }
67
68   const char* msg() const {
69     if (msg_.empty())
70       return what();
71     return msg_.c_str();
72   }
73
74  private:
75   int code_;
76   std::string msg_;
77 };
78
79 class AutoDbType {
80  public:
81   AutoDbType() = default;
82   explicit AutoDbType(DbType db_type) : db_type_(db_type) {}
83
84   explicit operator int () {
85     if (!db_type_)
86       throw DbException("invalid type conversion from nullopt to int");
87     return std::get<int64_t>(*db_type_);
88   }
89
90   explicit operator int64_t () {
91     if (!db_type_)
92       throw DbException("invalid type conversion from nullopt to int");
93     return std::get<int64_t>(*db_type_);
94   }
95
96   explicit operator std::string () {
97     if (!db_type_) {
98       throw DbException(
99           "invalid type conversion from nullopt to string");
100     }
101
102     return std::get<std::string>(*db_type_);
103   }
104
105   explicit operator double () {
106     if (!db_type_) {
107       throw DbException(
108           "invalid type conversion from nullopt to double");
109     }
110
111     return std::get<double>(*db_type_);
112   }
113
114   explicit operator std::vector<unsigned char> () {
115     if (!db_type_) {
116       throw DbException(
117           "invalid type conversion from nullopt to std::vector<unsigned char>");
118     }
119
120     return std::get<std::vector<unsigned char>>(*db_type_);
121   }
122
123   operator std::optional<int> () {
124     if (!db_type_)
125       return std::nullopt;
126     return std::get<int64_t>(*db_type_);
127   }
128
129   operator std::optional<int64_t> () {
130     if (!db_type_)
131       return std::nullopt;
132     return std::get<int64_t>(*db_type_);
133   }
134
135   operator std::optional<std::string> () {
136     if (!db_type_)
137       return std::nullopt;
138     return std::get<std::string>(*db_type_);
139   }
140
141   operator std::optional<double> () {
142     if (!db_type_)
143       return std::nullopt;
144     return std::get<double>(*db_type_);
145   }
146
147   operator std::optional<std::vector<unsigned char>> () {
148     if (!db_type_)
149       return std::nullopt;
150     return std::get<std::vector<unsigned char>>(*db_type_);
151   }
152
153  private:
154   DbType db_type_;
155 };
156
157 using _ = AutoDbType;
158
159 class Database {
160  public:
161   class TransactionGuard {
162    public:
163     TransactionGuard(const TransactionGuard&) = delete;
164     TransactionGuard& operator = (const TransactionGuard&) = delete;
165
166     TransactionGuard(TransactionGuard&& t) noexcept {
167       db_ = t.db_;
168       t.db_ = nullptr;
169     }
170
171     TransactionGuard& operator = (TransactionGuard&& t) noexcept {
172       if (this != &t) {
173         if (db_)
174           sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
175         db_ = t.db_;
176         t.db_ = nullptr;
177       }
178
179       return *this;
180     }
181
182     explicit TransactionGuard(sqlite3* db) : db_(db) {
183       int r = sqlite3_exec(db, "BEGIN DEFERRED", nullptr, nullptr, nullptr);
184       if (r != SQLITE_OK) {
185         throw DbException("begin transaction failed", r);
186       }
187     }
188
189     ~TransactionGuard() {
190       if (db_)
191         sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
192     }
193
194     int Commit() {
195       int ret = sqlite3_exec(db_, "COMMIT", nullptr, nullptr, nullptr);
196       if (ret != SQLITE_OK) {
197         sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
198       }
199
200       db_ = nullptr;
201       return ret;
202     }
203
204    private:
205     sqlite3* db_ = nullptr;
206   };
207
208   class Sql {
209    public:
210     Sql(std::string query) : query_(std::move(query)) {}
211
212     Sql& Bind(const char* val) {
213       if (val == nullptr) {
214         bindings_.push_back(DbType(std::nullopt));
215       } else {
216         std::string str = val;
217         if (empty_string_as_null_ && str.empty())
218           bindings_.push_back(DbType(std::nullopt));
219         else
220           bindings_.push_back(DbType(std::move(str)));
221       }
222       return *this;
223     }
224
225     Sql& Bind(std::string val) {
226       if (empty_string_as_null_ && val.empty())
227         bindings_.push_back(DbType(std::nullopt));
228       else
229         bindings_.push_back(DbType(std::move(val)));
230       return *this;
231     }
232
233     Sql& Bind(int val) {
234       bindings_.push_back(DbType(static_cast<int64_t>(val)));
235       return *this;
236     }
237
238     Sql& Bind(int64_t val) {
239       bindings_.push_back(DbType(val));
240       return *this;
241     }
242
243     Sql& Bind(double val) {
244       bindings_.push_back(DbType(val));
245       return *this;
246     }
247
248     Sql& Bind(std::vector<unsigned char> val) {
249       if (empty_vector_as_null_ && val.empty())
250         bindings_.push_back(DbType(std::nullopt));
251       else
252         bindings_.push_back(DbType(std::move(val)));
253       return *this;
254     }
255
256     Sql& Bind(const std::nullopt_t val) {
257       bindings_.push_back(DbType(val));
258       return *this;
259     }
260
261     Sql& Bind(int pos, const char* val) {
262       if (val == nullptr) {
263         binding_map_[pos] = DbType(std::nullopt);
264       } else {
265         std::string str = val;
266         if (empty_string_as_null_ && str.empty())
267           binding_map_[pos] = DbType(std::nullopt);
268         else
269           binding_map_[pos] = DbType(std::move(str));
270       }
271       return *this;
272     }
273
274     Sql& Bind(int pos, std::string val) {
275       if (empty_string_as_null_ && val.empty())
276         binding_map_[pos] = DbType(std::nullopt);
277       else
278         binding_map_[pos] = DbType(std::move(val));
279       return *this;
280     }
281
282     Sql& Bind(int pos, int val) {
283       binding_map_[pos] = DbType(static_cast<int64_t>(val));
284       return *this;
285     }
286
287     Sql& Bind(int pos, int64_t val) {
288       binding_map_[pos] = DbType(val);
289       return *this;
290     }
291
292     Sql& Bind(int pos, double val) {
293       binding_map_[pos] = DbType(val);
294       return *this;
295     }
296
297     Sql& Bind(int pos, std::vector<unsigned char> val) {
298       if (empty_vector_as_null_ && val.empty())
299         binding_map_[pos] = DbType(std::nullopt);
300       else
301         binding_map_[pos] = DbType(std::move(val));
302       return *this;
303     }
304
305     Sql& Bind(int pos, const std::nullopt_t& val) {
306       binding_map_[pos] = DbType(val);
307       return *this;
308     }
309
310     Sql& Bind(std::string name, const char* val) {
311       if (val == nullptr) {
312         binding_name_map_[std::move(name)] = DbType(std::nullopt);
313       } else {
314         std::string str = val;
315         if (empty_string_as_null_ && str.empty())
316           binding_name_map_[std::move(name)] = DbType(std::nullopt);
317         else
318           binding_name_map_[std::move(name)] = DbType(std::move(str));
319       }
320       return *this;
321     }
322
323     Sql& Bind(std::string name, std::string val) {
324       if (empty_string_as_null_ && val.empty())
325         binding_name_map_[std::move(name)] = DbType(std::nullopt);
326       else
327         binding_name_map_[std::move(name)] = DbType(std::move(val));
328       return *this;
329     }
330
331     Sql& Bind(std::string name, int val) {
332       binding_name_map_[std::move(name)] = DbType(static_cast<int64_t>(val));
333       return *this;
334     }
335
336     Sql& Bind(std::string name, int64_t val) {
337       binding_name_map_[std::move(name)] = DbType(val);
338       return *this;
339     }
340
341     Sql& Bind(std::string name, double val) {
342       binding_name_map_[std::move(name)] = DbType(val);
343       return *this;
344     }
345
346     Sql& Bind(std::string name, std::vector<unsigned char> val) {
347       if (empty_vector_as_null_ && val.empty())
348         binding_name_map_[std::move(name)] = DbType(std::nullopt);
349       else
350         binding_name_map_[std::move(name)] = DbType(std::move(val));
351       return *this;
352     }
353
354     Sql& Bind(std::string name, const std::nullopt_t& val) {
355       binding_name_map_[std::move(name)] = DbType(val);
356       return *this;
357     }
358
359     const std::vector<DbType>& GetBindings() const {
360       return bindings_;
361     }
362
363     const std::map<int, DbType>& GetBindingMap() const {
364       return binding_map_;
365     }
366
367     const std::map<std::string, DbType>& GetBindingNameMap() const {
368       return binding_name_map_;
369     }
370
371     const std::string& GetQuery() const {
372       return query_;
373     }
374
375     Sql& SetEmptyStringAsNull(bool as_null) {
376       empty_string_as_null_ = as_null;
377       return *this;
378     }
379
380     Sql& SetEmptyVectorAsNull(bool as_null) {
381       empty_vector_as_null_ = as_null;
382       return *this;
383     }
384
385     Sql& Reset() {
386       bindings_.clear();
387       binding_map_.clear();
388       binding_name_map_.clear();
389       empty_string_as_null_ = false;
390       empty_vector_as_null_ = false;
391       return *this;
392     }
393
394    private:
395     std::string query_;
396     std::vector<DbType> bindings_;
397     std::map<int, DbType> binding_map_;
398     std::map<std::string, DbType> binding_name_map_;
399     bool empty_string_as_null_ = false;
400     bool empty_vector_as_null_ = false;
401   };
402
403   class Result {
404    public:
405     Result() = default;
406     ~Result() {
407       if (stmt_)
408         sqlite3_finalize(stmt_);
409     }
410
411     Result(const Result&) = delete;
412     Result& operator = (const Result&) = delete;
413
414     Result(Result&& r) noexcept {
415       stmt_ = r.stmt_;
416       r.stmt_ = nullptr;
417       query_ = std::move(r.query_);
418       is_done_ = r.is_done_;
419     }
420
421     Result& operator = (Result&& r) noexcept {
422       if (this != &r) {
423         if (stmt_)
424           sqlite3_finalize(stmt_);
425         stmt_ = r.stmt_;
426         r.stmt_ = nullptr;
427         query_ = std::move(r.query_);
428         is_done_ = r.is_done_;
429       }
430
431       return *this;
432     }
433
434     class Record {
435      public:
436       explicit Record(const sqlite3_stmt* stmt) : stmt_(stmt) {}
437
438       AutoDbType Get(int pos) const {
439         sqlite3_stmt* stmt = const_cast<sqlite3_stmt*>(stmt_);
440         int type = sqlite3_column_type(stmt, pos);
441
442         DbType dbt;
443         if (type == SQLITE_TEXT) {
444           dbt = DbType(reinterpret_cast<const char*>(
445               sqlite3_column_text(stmt, pos)));
446         } else if (type == SQLITE_INTEGER) {
447           dbt = DbType(static_cast<int64_t>(sqlite3_column_int64(stmt, pos)));
448         } else if (type == SQLITE_FLOAT) {
449           dbt = DbType(sqlite3_column_double(stmt, pos));
450         } else if (type == SQLITE_BLOB) {
451           const unsigned char* val = reinterpret_cast<const unsigned char*>(
452               sqlite3_column_blob(stmt, pos));
453           int len = sqlite3_column_bytes(stmt, pos);
454
455           if (!val || len < 0) {
456             throw DbException("invalid blob");;
457           } else {
458             dbt = DbType(std::vector<unsigned char>(val, val + len));
459           }
460         } else if (type == SQLITE_NULL) {
461           dbt = DbType(std::nullopt);
462         } else {
463           throw DbException("invalid column type", type);
464         }
465
466         return AutoDbType(dbt);
467       }
468
469       template <typename ...Types>
470       auto Get() const {
471         std::tuple<Types...> t;
472         int pos = 0;
473         for_<std::tuple_size_v<std::tuple<Types...>>>([&] (auto i) {
474           std::get<i.value>(t) = Get(pos++);
475         });
476
477         return t;
478       }
479
480      private:
481       const sqlite3_stmt* stmt_;
482     };
483
484     class Iterator {
485      public:
486       explicit Iterator(sqlite3_stmt* stmt) : stmt_(stmt) {}
487
488       Record operator*() { return Record(stmt_); }
489
490       bool operator != (const Iterator& rhs) const {
491         return stmt_ != rhs.stmt_;
492       }
493
494       void operator ++() {
495         int r = sqlite3_step(stmt_);
496         if (r != SQLITE_ROW)
497           stmt_ = nullptr;
498       }
499
500      private:
501       sqlite3_stmt* stmt_ = nullptr;
502     };
503
504     Iterator begin() const {
505       if (is_done_)
506         return Iterator(nullptr);
507       return Iterator(stmt_);
508     }
509
510     Iterator end() const {
511       return Iterator(nullptr);
512     }
513
514     operator bool() const {
515       if (stmt_ == nullptr)
516         return false;
517       return true;
518     }
519
520     explicit operator int() const {
521       if (db_ == nullptr)
522         return SQLITE_ERROR;
523       return sqlite3_errcode(db_);
524     }
525
526     explicit operator const char*() const {
527       if (db_ == nullptr)
528         return "";
529       return sqlite3_errmsg(db_);
530     }
531
532     template <class T>
533     std::vector<T> ToVector() {
534       if (stmt_ == nullptr)
535         return {};
536
537       std::vector<T> vec;
538       for (const auto& rec : *this) {
539         T t;
540         t(rec);
541         vec.push_back(std::move(t));
542       }
543
544       return vec;
545     }
546
547     template <class T>
548     std::list<T> ToList() {
549       if (stmt_ == nullptr)
550         return {};
551
552       std::list<T> l;
553       for (const auto& rec : *this) {
554         T t;
555         t(rec);
556         l.push_back(std::move(t));
557       }
558
559       return l;
560     }
561
562     template <class T>
563     std::optional<T> GetFirst() {
564       if (stmt_ == nullptr)
565         return std::nullopt;
566       for (const auto& rec : *this) {
567         T t;
568         t(rec);
569         return t;
570       }
571
572       return std::nullopt;
573     }
574
575     std::optional<Record> GetFirstRecord() {
576       if (stmt_ == nullptr)
577         return std::nullopt;
578       for (const auto& rec : *this) {
579         return rec;
580       }
581
582       return std::nullopt;
583     }
584
585     sqlite3_stmt* GetRaw() const {
586       return stmt_;
587     }
588
589     const std::string& GetQuery() const {
590       return query_;
591     }
592
593     void SetDone(bool is_done) {
594       is_done_ = is_done;
595     }
596
597    private:
598     friend class Database;
599     Result(sqlite3_stmt* stmt, sqlite3* db, std::string query, bool is_done)
600         : stmt_(stmt), db_(db), query_(std::move(query)), is_done_(is_done) {}
601
602     sqlite3_stmt* stmt_ = nullptr;
603     sqlite3* db_ = nullptr;
604     std::string query_;
605     bool is_done_;
606   };
607
608   Database(std::string db, int flags) {
609     int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
610     if (r != SQLITE_OK)
611       throw DbException("open failed", r);
612   }
613
614   Database(std::string db, int flags, std::function<bool(int)> busy_handler) {
615     int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
616     if (r != SQLITE_OK)
617       throw DbException("sqlite3_open_v2() failed", r);
618
619     busy_handler_ = std::move(busy_handler);
620     r = sqlite3_busy_handler(db_, [](void* data, int count) {
621       Database* pDb = static_cast<Database*>(data);
622       if (pDb->busy_handler_(count))
623         return 1;
624       return 0;
625     }, this);
626
627     if (r != SQLITE_OK) {
628       sqlite3_close_v2(db_);
629       throw DbException("sqlite3_busy_handler() failed", r);
630     }
631   }
632
633   ~Database() {
634     if (db_)
635       sqlite3_close_v2(db_);
636   }
637
638   Database() = default;
639   Database(const Database&) = delete;
640   Database& operator = (const Database&) = delete;
641
642   Database(Database&& db) noexcept {
643     db_ = db.db_;
644     db.db_ = nullptr;
645   }
646
647   explicit operator bool() const {
648     if (db_ == nullptr)
649       return false;
650     return true;
651   }
652
653   Database& operator = (Database&& db) noexcept {
654     if (this != &db) {
655         if (db_)
656           sqlite3_close_v2(db_);
657         db_ = db.db_;
658         db.db_ = nullptr;
659     }
660
661     return *this;
662   }
663
664   TransactionGuard CreateTransactionGuard() const {
665     return TransactionGuard(db_);
666   }
667
668   Result Exec(const Sql& sql) const {
669     if (!db_)
670       throw DbException("Not opened");
671
672     sqlite3_stmt* stmt = nullptr;
673     int r = sqlite3_prepare_v2(db_, sql.GetQuery().c_str(),
674         -1, &stmt, nullptr);
675     if (r != SQLITE_OK) {
676       return { nullptr, nullptr, "", true };
677     }
678
679     std::unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)*> stmt_auto(stmt,
680         sqlite3_finalize);
681     int pos = 1;
682     for (const auto& i : sql.GetBindings()) {
683       Bind(pos++, i, stmt);
684     }
685
686     for (const auto& i : sql.GetBindingMap()) {
687       Bind(i.first, i.second, stmt);
688     }
689
690     for (const auto& i : sql.GetBindingNameMap()) {
691       int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
692       if (pos == 0)
693         throw DbException("Invalid binding");
694       Bind(pos, i.second, stmt);
695     }
696
697     r = sqlite3_step(stmt);
698     if (r != SQLITE_ROW && r != SQLITE_DONE) {
699       return { nullptr, db_, "", true };
700     }
701
702     return { stmt_auto.release(), db_, sql.GetQuery(),
703         r == SQLITE_DONE ? true : false };
704   }
705
706   bool Exec(const Sql& sql, Result& previous_stmt) const {
707     if (sql.GetQuery() != previous_stmt.GetQuery())
708       throw DbException("Query is different");
709
710     sqlite3_stmt* stmt = previous_stmt.GetRaw();
711     if (!stmt)
712       return false;
713
714     int r = sqlite3_reset(stmt);
715     if (r != SQLITE_ROW && r != SQLITE_DONE && r != SQLITE_OK)
716       return false;
717
718     int pos = 1;
719     for (const auto& i : sql.GetBindings()) {
720       Bind(pos++, i, stmt);
721     }
722
723     for (const auto& i : sql.GetBindingMap()) {
724       Bind(i.first, i.second, stmt);
725     }
726
727     for (const auto& i : sql.GetBindingNameMap()) {
728       int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
729       if (pos == 0)
730         throw DbException("Invalid binding");
731       Bind(pos, i.second, stmt);
732     }
733
734     r = sqlite3_step(stmt);
735     if (r != SQLITE_ROW && r != SQLITE_DONE)
736       return false;
737
738     previous_stmt.SetDone(r == SQLITE_DONE ? true : false);
739     return true;
740   }
741
742   void OneStepExec(const Sql& sql) const {
743     char* errmsg = nullptr;
744     int ret = sqlite3_exec(db_, sql.GetQuery().c_str(), nullptr, nullptr,
745         &errmsg);
746     if (ret != SQLITE_OK) {
747        std::unique_ptr<char, decltype(free)*> errmsg_auto(errmsg, free);
748        throw DbException(errmsg);
749     }
750   }
751
752   sqlite3* GetRaw() const {
753     if (!db_)
754       throw DbException("Not opened");
755     return db_;
756   }
757
758  private:
759   void Bind(int pos, const DbType& type, sqlite3_stmt* stmt) const {
760     int r;
761     if (!type) {
762       r = sqlite3_bind_null(stmt, pos);
763       if (r != SQLITE_OK) {
764         throw DbException("Invalid binding", r);
765       }
766
767       return;
768     }
769
770     if (const std::string* pstr = std::get_if<std::string>(&(*type))) {
771       r = sqlite3_bind_text(stmt, pos, (*pstr).c_str(), -1,
772           SQLITE_TRANSIENT);
773     } else if (const int64_t* pint = std::get_if<int64_t>(&(*type))) {
774       r = sqlite3_bind_int64(stmt, pos, (*pint));
775     } else if (const double* pdouble = std::get_if<double>(&(*type))) {
776       r = sqlite3_bind_double(stmt, pos, (*pdouble));
777     } else if (const std::vector<unsigned char>* pvector =
778         std::get_if<std::vector<unsigned char>>(&(*type))) {
779       r = sqlite3_bind_blob(stmt, pos, (*pvector).data(),
780           (*pvector).size(), nullptr);
781     } else {
782       r = -1;
783     }
784
785     if (r != SQLITE_OK) {
786       throw DbException("Invalid binding");
787     }
788   }
789
790  private:
791   sqlite3* db_ = nullptr;
792   std::function<bool(int)> busy_handler_;
793 };
794
795 }  // namespace tizen_base
796
797 #endif  // TIZEN_DATABASE_DATABASE_HPP_