Add class 'DbException'
[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     }
352
353     Result& operator = (Result&& r) noexcept {
354       if (this != &r) {
355         if (stmt_)
356           sqlite3_finalize(stmt_);
357         stmt_ = r.stmt_;
358         r.stmt_ = nullptr;
359       }
360
361       return *this;
362     }
363
364     class Record {
365      public:
366       explicit Record(const sqlite3_stmt* stmt) : stmt_(stmt) {}
367
368       AutoDbType Get(int pos) const {
369         sqlite3_stmt* stmt = const_cast<sqlite3_stmt*>(stmt_);
370         int type = sqlite3_column_type(stmt, pos);
371
372         DbType dbt;
373         if (type == SQLITE_TEXT) {
374           dbt = DbType(reinterpret_cast<const char*>(
375               sqlite3_column_text(stmt, pos)));
376         } else if (type == SQLITE_INTEGER) {
377           dbt = DbType(sqlite3_column_int(stmt, pos));
378         } else if (type == SQLITE_FLOAT) {
379           dbt = DbType(sqlite3_column_double(stmt, pos));
380         } else if (type == SQLITE_BLOB) {
381           const unsigned char* val = reinterpret_cast<const unsigned char*>(
382               sqlite3_column_blob(stmt, pos));
383           int len = sqlite3_column_bytes(stmt, pos);
384
385           if (!val || len < 0) {
386             throw DbException("invalid blob");;
387           } else {
388             dbt = DbType(std::vector<unsigned char>(val, val + len));
389           }
390         } else if (type == SQLITE_NULL) {
391           dbt = DbType(std::nullopt);
392         } else {
393           throw DbException("invalid column type", type);
394         }
395
396         return AutoDbType(dbt);
397       }
398
399       template <typename ...Types>
400       auto Get() const {
401         std::tuple<Types...> t;
402         int pos = 0;
403         for_<std::tuple_size_v<std::tuple<Types...>>>([&] (auto i) {
404           std::get<i.value>(t) = Get(pos++);
405         });
406
407         return t;
408       }
409
410      private:
411       const sqlite3_stmt* stmt_;
412     };
413
414     class Iterator {
415      public:
416       explicit Iterator(sqlite3_stmt* stmt) : stmt_(stmt) {}
417
418       Record operator*() { return Record(stmt_); }
419
420       bool operator != (const Iterator& rhs) const {
421         return stmt_ != rhs.stmt_;
422       }
423
424       void operator ++() {
425         int r = sqlite3_step(stmt_);
426         if (r != SQLITE_ROW)
427           stmt_ = nullptr;
428       }
429
430      private:
431       sqlite3_stmt* stmt_ = nullptr;
432     };
433
434     Iterator begin() const {
435       return Iterator(stmt_);
436     }
437
438     Iterator end() const {
439       return Iterator(nullptr);
440     }
441
442     explicit operator bool() {
443       if (stmt_ == nullptr)
444         return false;
445       return true;
446     }
447
448     explicit operator int() {
449       if (db_ == nullptr)
450         return SQLITE_ERROR;
451       return sqlite3_errcode(db_);
452     }
453
454     operator const char*() {
455       if (db_ == nullptr)
456         return "";
457       return sqlite3_errmsg(db_);
458     }
459
460     template <class T>
461     std::vector<T> ToVector() {
462       if (stmt_ == nullptr)
463         return {};
464
465       std::vector<T> vec;
466       for (const auto& rec : *this) {
467         T t;
468         t(rec);
469         vec.push_back(std::move(t));
470       }
471
472       return vec;
473     }
474
475     template <class T>
476     std::list<T> ToList() {
477       if (stmt_ == nullptr)
478         return {};
479
480       std::list<T> l;
481       for (const auto& rec : *this) {
482         T t;
483         t(rec);
484         l.push_back(std::move(t));
485       }
486
487       return l;
488     }
489
490     template <class T>
491     std::optional<T> GetFirst() {
492       if (stmt_ == nullptr)
493         return std::nullopt;
494       for (const auto& rec : *this) {
495         T t;
496         t(rec);
497         return t;
498       }
499
500       return std::nullopt;
501     }
502
503     std::optional<Record> GetFirstRecord() {
504       if (stmt_ == nullptr)
505         return std::nullopt;
506       for (const auto& rec : *this) {
507         return rec;
508       }
509
510       return std::nullopt;
511     }
512
513     sqlite3_stmt* GetRaw() const {
514       return stmt_;
515     }
516
517     const std::string& GetQuery() const {
518       return query_;
519     }
520
521    private:
522     friend class Database;
523     Result(sqlite3_stmt* stmt, sqlite3* db, std::string query)
524         : stmt_(stmt), db_(db), query_(std::move(query)) {}
525
526     sqlite3_stmt* stmt_ = nullptr;
527     sqlite3* db_ = nullptr;
528     std::string query_;
529   };
530
531   Database(std::string db, int flags) {
532     int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
533     if (r != SQLITE_OK)
534       throw DbException("open failed", r);
535   }
536
537   Database(std::string db, int flags, std::function<bool(int)> busy_handler) {
538     int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
539     if (r != SQLITE_OK)
540       throw DbException("sqlite3_open_v2() failed", r);
541
542     busy_handler_ = std::move(busy_handler);
543     r = sqlite3_busy_handler(db_, [](void* data, int count) {
544       Database* pDb = static_cast<Database*>(data);
545       if (pDb->busy_handler_(count))
546         return 1;
547       return 0;
548     }, this);
549
550     if (r != SQLITE_OK) {
551       sqlite3_close_v2(db_);
552       throw DbException("sqlite3_busy_handler() failed", r);
553     }
554   }
555
556   ~Database() {
557     if (db_)
558       sqlite3_close_v2(db_);
559   }
560
561   Database() = default;
562   Database(const Database&) = delete;
563   Database& operator = (const Database&) = delete;
564
565   Database(Database&& db) noexcept {
566     db_ = db.db_;
567     db.db_ = nullptr;
568   }
569
570   explicit operator bool() {
571     if (db_ == nullptr)
572       return false;
573     return true;
574   }
575
576   Database& operator = (Database&& db) noexcept {
577     if (this != &db) {
578         if (db_)
579           sqlite3_close_v2(db_);
580         db_ = db.db_;
581         db.db_ = nullptr;
582     }
583
584     return *this;
585   }
586
587   TransactionGuard CreateTransactionGuard() const {
588     return TransactionGuard(db_);
589   }
590
591   Result Exec(const Sql& sql) const {
592     if (!db_)
593       throw DbException("Not opened");
594
595     sqlite3_stmt* stmt = nullptr;
596     int r = sqlite3_prepare_v2(db_, sql.GetQuery().c_str(),
597         -1, &stmt, nullptr);
598     if (r != SQLITE_OK) {
599       return { nullptr, nullptr, "" };
600     }
601
602     std::unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)*> stmt_auto(stmt,
603         sqlite3_finalize);
604     int pos = 1;
605     for (const auto& i : sql.GetBindings()) {
606       Bind(pos++, i, stmt);
607     }
608
609     for (const auto& i : sql.GetBindingMap()) {
610       Bind(i.first, i.second, stmt);
611     }
612
613     for (const auto& i : sql.GetBindingNameMap()) {
614       int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
615       if (pos == 0)
616         throw DbException("Invalid binding");
617       Bind(pos, i.second, stmt);
618     }
619
620     r = sqlite3_step(stmt);
621     if (r != SQLITE_ROW && r != SQLITE_DONE) {
622       return { nullptr, db_, "" };
623     }
624
625     return { stmt_auto.release(), db_, sql.GetQuery() };
626   }
627
628   bool Exec(const Sql& sql, const Result& previous_stmt) {
629     if (sql.GetQuery() != previous_stmt.GetQuery())
630       throw DbException("Query is different");
631
632     sqlite3_stmt* stmt = previous_stmt.GetRaw();
633     if (!stmt)
634       return false;
635
636     int r = sqlite3_reset(stmt);
637     if (r != SQLITE_ROW && r != SQLITE_DONE && r != SQLITE_OK)
638       return false;
639
640     int pos = 1;
641     for (const auto& i : sql.GetBindings()) {
642       Bind(pos++, i, stmt);
643     }
644
645     for (const auto& i : sql.GetBindingMap()) {
646       Bind(i.first, i.second, stmt);
647     }
648
649     for (const auto& i : sql.GetBindingNameMap()) {
650       int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
651       if (pos == 0)
652         throw DbException("Invalid binding");
653       Bind(pos, i.second, stmt);
654     }
655
656     r = sqlite3_step(stmt);
657     if (r != SQLITE_ROW && r != SQLITE_DONE)
658       return false;
659
660     return true;
661   }
662
663  private:
664   void Bind(int pos, const DbType& type, sqlite3_stmt* stmt) const {
665     int r;
666     if (!type) {
667       r = sqlite3_bind_null(stmt, pos);
668       if (r != SQLITE_OK) {
669         throw DbException("Invalid binding", r);
670       }
671
672       return;
673     }
674
675     if (const std::string* pstr = std::get_if<std::string>(&(*type))) {
676       r = sqlite3_bind_text(stmt, pos, (*pstr).c_str(), -1,
677           SQLITE_TRANSIENT);
678     } else if (const int* pint = std::get_if<int>(&(*type))) {
679       r = sqlite3_bind_int(stmt, pos, (*pint));
680     } else if (const double* pdouble = std::get_if<double>(&(*type))) {
681       r = sqlite3_bind_double(stmt, pos, (*pdouble));
682     } else if (const std::vector<unsigned char>* pvector =
683         std::get_if<std::vector<unsigned char>>(&(*type))) {
684       r = sqlite3_bind_blob(stmt, pos, (*pvector).data(),
685           (*pvector).size(), nullptr);
686     } else {
687       r = -1;
688     }
689
690     if (r != SQLITE_OK) {
691       throw DbException("Invalid binding");
692     }
693   }
694
695  private:
696   sqlite3* db_ = nullptr;
697   std::function<bool(int)> busy_handler_;
698 };
699
700 }  // namespace tizen_base
701
702 #endif  // TIZEN_DATABASE_DATABASE_HPP_