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