2 * Copyright (c) 2022 Samsung Electronics Co., Ltd. All rights reserved.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #ifndef TIZEN_DATABASE_DATABASE_HPP_
18 #define TIZEN_DATABASE_DATABASE_HPP_
33 namespace tizen_base {
35 template<std::size_t N>
37 static const constexpr auto value = N;
40 template <class F, std::size_t... Is>
41 void for_(F func, std::index_sequence<Is...>) {
42 (func(num<Is>{}), ...);
45 template <std::size_t N, typename F>
47 for_(func, std::make_index_sequence<N>());
50 using DbType = std::optional<std::variant<int64_t, double, std::string,
51 std::vector<unsigned char>>>;
53 class DbException : public std::runtime_error {
55 explicit DbException(const std::string& msg, int code = SQLITE_ERROR)
56 : std::runtime_error(msg), code_(code) {
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);
68 const char* msg() const {
81 AutoDbType() = default;
82 explicit AutoDbType(DbType db_type, int real_type)
83 : db_type_(db_type), real_type_(real_type) {}
89 explicit operator int () {
91 throw DbException("invalid type conversion from nullopt to int");
92 if (real_type_ != SQLITE_INTEGER)
93 throw DbException("invalid type conversion to int");
95 return std::get<int64_t>(*db_type_);
98 explicit operator int64_t () {
100 throw DbException("invalid type conversion from nullopt to int64_t");
101 if (real_type_ != SQLITE_INTEGER)
102 throw DbException("invalid type conversion to int64_t");
104 return std::get<int64_t>(*db_type_);
107 explicit operator std::string () {
110 "invalid type conversion from nullopt to string");
113 if (real_type_ != SQLITE_TEXT)
114 throw DbException("invalid type conversion to string");
116 return std::get<std::string>(*db_type_);
119 explicit operator double () {
122 "invalid type conversion from nullopt to double");
125 if (real_type_ != SQLITE_FLOAT)
126 throw DbException("invalid type conversion to double");
128 return std::get<double>(*db_type_);
131 explicit operator std::vector<unsigned char> () {
134 "invalid type conversion from nullopt to std::vector<unsigned char>");
137 if (real_type_ != SQLITE_BLOB)
138 throw DbException("invalid type conversion to std::vector<unsigned char>");
140 return std::get<std::vector<unsigned char>>(*db_type_);
143 operator std::optional<int> () {
146 if (real_type_ != SQLITE_INTEGER)
147 throw DbException("invalid type conversion to int");
149 return std::get<int64_t>(*db_type_);
152 operator std::optional<int64_t> () {
155 if (real_type_ != SQLITE_INTEGER)
156 throw DbException("invalid type conversion to int64_t");
158 return std::get<int64_t>(*db_type_);
161 operator std::optional<std::string> () {
164 if (real_type_ != SQLITE_TEXT)
165 throw DbException("invalid type conversion to string");
167 return std::get<std::string>(*db_type_);
170 operator std::optional<double> () {
173 if (real_type_ != SQLITE_FLOAT)
174 throw DbException("invalid type conversion to double");
176 return std::get<double>(*db_type_);
179 operator std::optional<std::vector<unsigned char>> () {
182 if (real_type_ != SQLITE_BLOB)
183 throw DbException("invalid type conversion to std::vector<unsigned char>");
185 return std::get<std::vector<unsigned char>>(*db_type_);
193 using _ = AutoDbType;
197 class TransactionGuard {
199 TransactionGuard(const TransactionGuard&) = delete;
200 TransactionGuard& operator = (const TransactionGuard&) = delete;
202 TransactionGuard(TransactionGuard&& t) noexcept {
207 TransactionGuard& operator = (TransactionGuard&& t) noexcept {
210 sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
218 explicit TransactionGuard(sqlite3* db) : db_(db) {
219 int r = sqlite3_exec(db, "BEGIN DEFERRED", nullptr, nullptr, nullptr);
220 if (r != SQLITE_OK) {
221 throw DbException("begin transaction failed", r);
225 ~TransactionGuard() {
227 sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
231 int ret = sqlite3_exec(db_, "COMMIT", nullptr, nullptr, nullptr);
232 if (ret != SQLITE_OK) {
233 sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
241 sqlite3* db_ = nullptr;
246 Sql(std::string query) : query_(std::move(query)) {}
248 Sql& Bind(const char* val) {
249 if (val == nullptr) {
250 bindings_.push_back(DbType(std::nullopt));
252 std::string str = val;
253 if (empty_string_as_null_ && str.empty())
254 bindings_.push_back(DbType(std::nullopt));
256 bindings_.push_back(DbType(std::move(str)));
261 Sql& Bind(std::string val) {
262 if (empty_string_as_null_ && val.empty())
263 bindings_.push_back(DbType(std::nullopt));
265 bindings_.push_back(DbType(std::move(val)));
270 bindings_.push_back(DbType(static_cast<int64_t>(val)));
274 Sql& Bind(int64_t val) {
275 bindings_.push_back(DbType(val));
279 Sql& Bind(double val) {
280 bindings_.push_back(DbType(val));
284 Sql& Bind(std::vector<unsigned char> val) {
285 if (empty_vector_as_null_ && val.empty())
286 bindings_.push_back(DbType(std::nullopt));
288 bindings_.push_back(DbType(std::move(val)));
292 Sql& Bind(const std::nullopt_t val) {
293 bindings_.push_back(DbType(val));
297 Sql& Bind(int pos, const char* val) {
298 if (val == nullptr) {
299 binding_map_[pos] = DbType(std::nullopt);
301 std::string str = val;
302 if (empty_string_as_null_ && str.empty())
303 binding_map_[pos] = DbType(std::nullopt);
305 binding_map_[pos] = DbType(std::move(str));
310 Sql& Bind(int pos, std::string val) {
311 if (empty_string_as_null_ && val.empty())
312 binding_map_[pos] = DbType(std::nullopt);
314 binding_map_[pos] = DbType(std::move(val));
318 Sql& Bind(int pos, int val) {
319 binding_map_[pos] = DbType(static_cast<int64_t>(val));
323 Sql& Bind(int pos, int64_t val) {
324 binding_map_[pos] = DbType(val);
328 Sql& Bind(int pos, double val) {
329 binding_map_[pos] = DbType(val);
333 Sql& Bind(int pos, std::vector<unsigned char> val) {
334 if (empty_vector_as_null_ && val.empty())
335 binding_map_[pos] = DbType(std::nullopt);
337 binding_map_[pos] = DbType(std::move(val));
341 Sql& Bind(int pos, const std::nullopt_t& val) {
342 binding_map_[pos] = DbType(val);
346 Sql& Bind(std::string name, const char* val) {
347 if (val == nullptr) {
348 binding_name_map_[std::move(name)] = DbType(std::nullopt);
350 std::string str = val;
351 if (empty_string_as_null_ && str.empty())
352 binding_name_map_[std::move(name)] = DbType(std::nullopt);
354 binding_name_map_[std::move(name)] = DbType(std::move(str));
359 Sql& Bind(std::string name, std::string val) {
360 if (empty_string_as_null_ && val.empty())
361 binding_name_map_[std::move(name)] = DbType(std::nullopt);
363 binding_name_map_[std::move(name)] = DbType(std::move(val));
367 Sql& Bind(std::string name, int val) {
368 binding_name_map_[std::move(name)] = DbType(static_cast<int64_t>(val));
372 Sql& Bind(std::string name, int64_t val) {
373 binding_name_map_[std::move(name)] = DbType(val);
377 Sql& Bind(std::string name, double val) {
378 binding_name_map_[std::move(name)] = DbType(val);
382 Sql& Bind(std::string name, std::vector<unsigned char> val) {
383 if (empty_vector_as_null_ && val.empty())
384 binding_name_map_[std::move(name)] = DbType(std::nullopt);
386 binding_name_map_[std::move(name)] = DbType(std::move(val));
390 Sql& Bind(std::string name, const std::nullopt_t& val) {
391 binding_name_map_[std::move(name)] = DbType(val);
395 const std::vector<DbType>& GetBindings() const {
399 const std::map<int, DbType>& GetBindingMap() const {
403 const std::map<std::string, DbType>& GetBindingNameMap() const {
404 return binding_name_map_;
407 const std::string& GetQuery() const {
411 Sql& SetEmptyStringAsNull(bool as_null) {
412 empty_string_as_null_ = as_null;
416 Sql& SetEmptyVectorAsNull(bool as_null) {
417 empty_vector_as_null_ = as_null;
423 binding_map_.clear();
424 binding_name_map_.clear();
425 empty_string_as_null_ = false;
426 empty_vector_as_null_ = false;
432 std::vector<DbType> bindings_;
433 std::map<int, DbType> binding_map_;
434 std::map<std::string, DbType> binding_name_map_;
435 bool empty_string_as_null_ = false;
436 bool empty_vector_as_null_ = false;
444 sqlite3_finalize(stmt_);
447 Result(const Result&) = delete;
448 Result& operator = (const Result&) = delete;
450 Result(Result&& r) noexcept {
453 query_ = std::move(r.query_);
454 is_done_ = r.is_done_;
457 Result& operator = (Result&& r) noexcept {
460 sqlite3_finalize(stmt_);
463 query_ = std::move(r.query_);
464 is_done_ = r.is_done_;
472 explicit Record(const sqlite3_stmt* stmt) : stmt_(stmt) {}
474 std::optional<std::string> GetString(int pos) const {
475 sqlite3_stmt* stmt = const_cast<sqlite3_stmt*>(stmt_);
476 const char* text = reinterpret_cast<const char*>(
477 sqlite3_column_text(stmt, pos));
484 AutoDbType Get(int pos) const {
485 sqlite3_stmt* stmt = const_cast<sqlite3_stmt*>(stmt_);
486 int type = sqlite3_column_type(stmt, pos);
489 if (type == SQLITE_TEXT) {
490 dbt = DbType(reinterpret_cast<const char*>(
491 sqlite3_column_text(stmt, pos)));
492 } else if (type == SQLITE_INTEGER) {
493 dbt = DbType(static_cast<int64_t>(sqlite3_column_int64(stmt, pos)));
494 } else if (type == SQLITE_FLOAT) {
495 dbt = DbType(sqlite3_column_double(stmt, pos));
496 } else if (type == SQLITE_BLOB) {
497 const unsigned char* val = reinterpret_cast<const unsigned char*>(
498 sqlite3_column_blob(stmt, pos));
499 int len = sqlite3_column_bytes(stmt, pos);
501 if (!val || len < 0) {
502 throw DbException("invalid blob");;
504 dbt = DbType(std::vector<unsigned char>(val, val + len));
506 } else if (type == SQLITE_NULL) {
507 dbt = DbType(std::nullopt);
509 throw DbException("invalid column type", type);
512 return AutoDbType(dbt, type);
515 template <typename ...Types>
517 std::tuple<Types...> t;
519 for_<std::tuple_size_v<std::tuple<Types...>>>([&] (auto i) {
520 std::get<i.value>(t) = Get(pos++);
527 const sqlite3_stmt* stmt_;
532 explicit Iterator(sqlite3_stmt* stmt) : stmt_(stmt) {}
534 Record operator*() { return Record(stmt_); }
536 bool operator != (const Iterator& rhs) const {
537 return stmt_ != rhs.stmt_;
541 int r = sqlite3_step(stmt_);
547 sqlite3_stmt* stmt_ = nullptr;
550 Iterator begin() const {
552 return Iterator(nullptr);
553 return Iterator(stmt_);
556 Iterator end() const {
557 return Iterator(nullptr);
560 operator bool() const {
561 if (stmt_ == nullptr)
566 explicit operator int() const {
569 return sqlite3_errcode(db_);
572 explicit operator const char*() const {
575 return sqlite3_errmsg(db_);
579 std::vector<T> ToVector() {
580 if (stmt_ == nullptr)
584 for (const auto& rec : *this) {
587 vec.push_back(std::move(t));
594 std::list<T> ToList() {
595 if (stmt_ == nullptr)
599 for (const auto& rec : *this) {
602 l.push_back(std::move(t));
609 std::optional<T> GetFirst() {
610 if (stmt_ == nullptr)
612 for (const auto& rec : *this) {
621 std::optional<Record> GetFirstRecord() {
622 if (stmt_ == nullptr)
624 for (const auto& rec : *this) {
631 sqlite3_stmt* GetRaw() const {
635 const std::string& GetQuery() const {
639 void SetDone(bool is_done) {
643 size_t GetColumnCount() const {
644 return sqlite3_column_count(stmt_);
648 friend class Database;
649 Result(sqlite3_stmt* stmt, sqlite3* db, std::string query, bool is_done)
650 : stmt_(stmt), db_(db), query_(std::move(query)), is_done_(is_done) {}
652 sqlite3_stmt* stmt_ = nullptr;
653 sqlite3* db_ = nullptr;
658 Database(std::string db, int flags) {
659 int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
661 throw DbException("open failed", r);
664 Database(std::string db, int flags, std::function<bool(int)> busy_handler) {
665 int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
667 throw DbException("sqlite3_open_v2() failed", r);
669 busy_handler_ = std::move(busy_handler);
670 r = sqlite3_busy_handler(db_, [](void* data, int count) {
671 Database* pDb = static_cast<Database*>(data);
672 if (pDb->busy_handler_(count))
677 if (r != SQLITE_OK) {
678 sqlite3_close_v2(db_);
679 throw DbException("sqlite3_busy_handler() failed", r);
685 sqlite3_close_v2(db_);
688 Database() = default;
689 Database(const Database&) = delete;
690 Database& operator = (const Database&) = delete;
692 Database(Database&& db) noexcept {
697 explicit operator bool() const {
703 Database& operator = (Database&& db) noexcept {
706 sqlite3_close_v2(db_);
714 TransactionGuard CreateTransactionGuard() const {
715 return TransactionGuard(db_);
718 Result Prepare(const Sql& sql) const {
720 throw DbException("Not opened");
722 sqlite3_stmt* stmt = nullptr;
723 int r = sqlite3_prepare_v2(db_, sql.GetQuery().c_str(),
726 return { nullptr, nullptr, "", true };
727 return { stmt, db_, sql.GetQuery(), false };
730 Result Exec(const Sql& sql) const {
732 throw DbException("Not opened");
734 sqlite3_stmt* stmt = nullptr;
735 int r = sqlite3_prepare_v2(db_, sql.GetQuery().c_str(),
737 if (r != SQLITE_OK) {
738 return { nullptr, nullptr, "", true };
741 std::unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)*> stmt_auto(stmt,
744 for (const auto& i : sql.GetBindings()) {
745 Bind(pos++, i, stmt);
748 for (const auto& i : sql.GetBindingMap()) {
749 Bind(i.first, i.second, stmt);
752 for (const auto& i : sql.GetBindingNameMap()) {
753 int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
755 throw DbException("Invalid binding");
756 Bind(pos, i.second, stmt);
759 r = sqlite3_step(stmt);
760 if (r != SQLITE_ROW && r != SQLITE_DONE) {
761 return { nullptr, db_, "", true };
764 return { stmt_auto.release(), db_, sql.GetQuery(),
765 r == SQLITE_DONE ? true : false };
768 bool Exec(const Sql& sql, Result& previous_stmt) const {
769 if (sql.GetQuery() != previous_stmt.GetQuery())
770 throw DbException("Query is different");
772 sqlite3_stmt* stmt = previous_stmt.GetRaw();
776 int r = sqlite3_reset(stmt);
777 if (r != SQLITE_ROW && r != SQLITE_DONE && r != SQLITE_OK)
781 for (const auto& i : sql.GetBindings()) {
782 Bind(pos++, i, stmt);
785 for (const auto& i : sql.GetBindingMap()) {
786 Bind(i.first, i.second, stmt);
789 for (const auto& i : sql.GetBindingNameMap()) {
790 int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
792 throw DbException("Invalid binding");
793 Bind(pos, i.second, stmt);
796 r = sqlite3_step(stmt);
797 if (r != SQLITE_ROW && r != SQLITE_DONE)
800 previous_stmt.SetDone(r == SQLITE_DONE ? true : false);
804 void OneStepExec(const Sql& sql) const {
805 char* errmsg = nullptr;
806 int ret = sqlite3_exec(db_, sql.GetQuery().c_str(), nullptr, nullptr,
808 if (ret != SQLITE_OK) {
809 std::unique_ptr<char, decltype(free)*> errmsg_auto(errmsg, free);
810 throw DbException(errmsg);
814 sqlite3* GetRaw() const {
816 throw DbException("Not opened");
821 void Bind(int pos, const DbType& type, sqlite3_stmt* stmt) const {
824 r = sqlite3_bind_null(stmt, pos);
825 if (r != SQLITE_OK) {
826 throw DbException("Invalid binding", r);
832 if (const std::string* pstr = std::get_if<std::string>(&(*type))) {
833 r = sqlite3_bind_text(stmt, pos, (*pstr).c_str(), -1,
835 } else if (const int64_t* pint = std::get_if<int64_t>(&(*type))) {
836 r = sqlite3_bind_int64(stmt, pos, (*pint));
837 } else if (const double* pdouble = std::get_if<double>(&(*type))) {
838 r = sqlite3_bind_double(stmt, pos, (*pdouble));
839 } else if (const std::vector<unsigned char>* pvector =
840 std::get_if<std::vector<unsigned char>>(&(*type))) {
841 r = sqlite3_bind_blob(stmt, pos, (*pvector).data(),
842 (*pvector).size(), nullptr);
847 if (r != SQLITE_OK) {
848 throw DbException("Invalid binding");
853 sqlite3* db_ = nullptr;
854 std::function<bool(int)> busy_handler_;
857 } // namespace tizen_base
859 #endif // TIZEN_DATABASE_DATABASE_HPP_