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