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_
34 namespace tizen_base {
36 template<std::size_t N>
38 static const constexpr auto value = N;
41 template <class F, std::size_t... Is>
42 void for_(F func, std::index_sequence<Is...>) {
43 (func(num<Is>{}), ...);
46 template <std::size_t N, typename F>
48 for_(func, std::make_index_sequence<N>());
51 using DbType = std::optional<std::variant<int64_t, double, std::string,
52 std::vector<unsigned char>>>;
54 class DbException : public std::runtime_error {
56 explicit DbException(const std::string& msg, int code = SQLITE_ERROR)
57 : std::runtime_error(msg), code_(code) {
60 DbException(const std::string& msg, int code, const std::string& file,
61 int line) : std::runtime_error(msg), code_(code) {
62 msg_ = msg + " at " + file + ":" + std::to_string(line);
69 const char* msg() const {
82 AutoDbType() = default;
83 explicit AutoDbType(DbType db_type, int real_type)
84 : db_type_(db_type), real_type_(real_type) {}
90 explicit operator int () {
92 throw DbException("invalid type conversion from nullopt to int");
93 if (real_type_ != SQLITE_INTEGER)
94 throw DbException("invalid type conversion to int");
96 return std::get<int64_t>(*db_type_);
99 explicit operator int64_t () {
101 throw DbException("invalid type conversion from nullopt to int64_t");
102 if (real_type_ != SQLITE_INTEGER)
103 throw DbException("invalid type conversion to int64_t");
105 return std::get<int64_t>(*db_type_);
108 explicit operator std::string () {
111 "invalid type conversion from nullopt to string");
114 if (real_type_ != SQLITE_TEXT)
115 throw DbException("invalid type conversion to string");
117 return std::get<std::string>(*db_type_);
120 explicit operator double () {
123 "invalid type conversion from nullopt to double");
126 if (real_type_ != SQLITE_FLOAT)
127 throw DbException("invalid type conversion to double");
129 return std::get<double>(*db_type_);
132 explicit operator std::vector<unsigned char> () {
135 "invalid type conversion from nullopt to std::vector<unsigned char>");
138 if (real_type_ != SQLITE_BLOB)
139 throw DbException("invalid type conversion to std::vector<unsigned char>");
141 return std::get<std::vector<unsigned char>>(*db_type_);
144 operator std::optional<int> () {
147 if (real_type_ != SQLITE_INTEGER)
148 throw DbException("invalid type conversion to int");
150 return std::get<int64_t>(*db_type_);
153 operator std::optional<int64_t> () {
156 if (real_type_ != SQLITE_INTEGER)
157 throw DbException("invalid type conversion to int64_t");
159 return std::get<int64_t>(*db_type_);
162 operator std::optional<std::string> () {
165 if (real_type_ != SQLITE_TEXT)
166 throw DbException("invalid type conversion to string");
168 return std::get<std::string>(*db_type_);
171 operator std::optional<double> () {
174 if (real_type_ != SQLITE_FLOAT)
175 throw DbException("invalid type conversion to double");
177 return std::get<double>(*db_type_);
180 operator std::optional<std::vector<unsigned char>> () {
183 if (real_type_ != SQLITE_BLOB)
184 throw DbException("invalid type conversion to std::vector<unsigned char>");
186 return std::get<std::vector<unsigned char>>(*db_type_);
191 int real_type_ = SQLITE_NULL;
194 using _ = AutoDbType;
198 class TransactionGuard {
200 TransactionGuard(const TransactionGuard&) = delete;
201 TransactionGuard& operator = (const TransactionGuard&) = delete;
203 TransactionGuard(TransactionGuard&& t) noexcept {
204 db_ = std::move(t.db_);
207 TransactionGuard& operator = (TransactionGuard&& t) noexcept {
210 sqlite3_exec(db_.lock().get(), "ROLLBACK", nullptr, nullptr, nullptr);
211 db_ = std::move(t.db_);
217 explicit TransactionGuard(const std::shared_ptr<sqlite3>& db)
219 int r = sqlite3_exec(db_.lock().get(), "BEGIN DEFERRED",
220 nullptr, nullptr, nullptr);
221 if (r != SQLITE_OK) {
222 throw DbException("begin transaction failed", r);
226 ~TransactionGuard() {
228 sqlite3_exec(db_.lock().get(), "ROLLBACK", nullptr, nullptr, nullptr);
235 auto db = db_.lock();
236 int ret = sqlite3_exec(db.get(), "COMMIT", nullptr, nullptr, nullptr);
237 if (ret != SQLITE_OK) {
238 sqlite3_exec(db.get(), "ROLLBACK", nullptr, nullptr, nullptr);
245 std::weak_ptr<sqlite3> db_;
250 Sql(std::string query) : query_(std::move(query)) {}
252 Sql& Bind(const char* val) {
253 if (val == nullptr) {
254 bindings_.push_back(DbType(std::nullopt));
256 std::string str = val;
257 if (empty_string_as_null_ && str.empty())
258 bindings_.push_back(DbType(std::nullopt));
260 bindings_.push_back(DbType(std::move(str)));
265 Sql& Bind(std::string val) {
266 if (empty_string_as_null_ && val.empty())
267 bindings_.push_back(DbType(std::nullopt));
269 bindings_.push_back(DbType(std::move(val)));
274 bindings_.push_back(DbType(static_cast<int64_t>(val)));
278 Sql& Bind(int64_t val) {
279 bindings_.push_back(DbType(val));
283 Sql& Bind(double val) {
284 bindings_.push_back(DbType(val));
288 Sql& Bind(std::vector<unsigned char> val) {
289 if (empty_vector_as_null_ && val.empty())
290 bindings_.push_back(DbType(std::nullopt));
292 bindings_.push_back(DbType(std::move(val)));
296 Sql& Bind(const std::nullopt_t val) {
297 bindings_.push_back(DbType(val));
301 Sql& Bind(int pos, const char* val) {
302 if (val == nullptr) {
303 binding_map_[pos] = DbType(std::nullopt);
305 std::string str = val;
306 if (empty_string_as_null_ && str.empty())
307 binding_map_[pos] = DbType(std::nullopt);
309 binding_map_[pos] = DbType(std::move(str));
314 Sql& Bind(int pos, std::string val) {
315 if (empty_string_as_null_ && val.empty())
316 binding_map_[pos] = DbType(std::nullopt);
318 binding_map_[pos] = DbType(std::move(val));
322 Sql& Bind(int pos, int val) {
323 binding_map_[pos] = DbType(static_cast<int64_t>(val));
327 Sql& Bind(int pos, int64_t val) {
328 binding_map_[pos] = DbType(val);
332 Sql& Bind(int pos, double val) {
333 binding_map_[pos] = DbType(val);
337 Sql& Bind(int pos, std::vector<unsigned char> val) {
338 if (empty_vector_as_null_ && val.empty())
339 binding_map_[pos] = DbType(std::nullopt);
341 binding_map_[pos] = DbType(std::move(val));
345 Sql& Bind(int pos, const std::nullopt_t& val) {
346 binding_map_[pos] = DbType(val);
350 Sql& Bind(std::string name, const char* val) {
351 if (val == nullptr) {
352 binding_name_map_[std::move(name)] = DbType(std::nullopt);
354 std::string str = val;
355 if (empty_string_as_null_ && str.empty())
356 binding_name_map_[std::move(name)] = DbType(std::nullopt);
358 binding_name_map_[std::move(name)] = DbType(std::move(str));
363 Sql& Bind(std::string name, std::string val) {
364 if (empty_string_as_null_ && val.empty())
365 binding_name_map_[std::move(name)] = DbType(std::nullopt);
367 binding_name_map_[std::move(name)] = DbType(std::move(val));
371 Sql& Bind(std::string name, int val) {
372 binding_name_map_[std::move(name)] = DbType(static_cast<int64_t>(val));
376 Sql& Bind(std::string name, int64_t val) {
377 binding_name_map_[std::move(name)] = DbType(val);
381 Sql& Bind(std::string name, double val) {
382 binding_name_map_[std::move(name)] = DbType(val);
386 Sql& Bind(std::string name, std::vector<unsigned char> val) {
387 if (empty_vector_as_null_ && val.empty())
388 binding_name_map_[std::move(name)] = DbType(std::nullopt);
390 binding_name_map_[std::move(name)] = DbType(std::move(val));
394 Sql& Bind(std::string name, const std::nullopt_t& val) {
395 binding_name_map_[std::move(name)] = DbType(val);
399 const std::vector<DbType>& GetBindings() const {
403 const std::map<int, DbType>& GetBindingMap() const {
407 const std::map<std::string, DbType>& GetBindingNameMap() const {
408 return binding_name_map_;
411 const std::string& GetQuery() const {
415 Sql& SetEmptyStringAsNull(bool as_null) {
416 empty_string_as_null_ = as_null;
420 Sql& SetEmptyVectorAsNull(bool as_null) {
421 empty_vector_as_null_ = as_null;
427 binding_map_.clear();
428 binding_name_map_.clear();
429 empty_string_as_null_ = false;
430 empty_vector_as_null_ = false;
436 std::vector<DbType> bindings_;
437 std::map<int, DbType> binding_map_;
438 std::map<std::string, DbType> binding_name_map_;
439 bool empty_string_as_null_ = false;
440 bool empty_vector_as_null_ = false;
448 sqlite3_finalize(stmt_);
451 Result(const Result&) = delete;
452 Result& operator = (const Result&) = delete;
454 Result(Result&& r) noexcept {
457 query_ = std::move(r.query_);
458 is_done_ = r.is_done_;
461 Result& operator = (Result&& r) noexcept {
464 sqlite3_finalize(stmt_);
467 query_ = std::move(r.query_);
468 is_done_ = r.is_done_;
476 explicit Record(const sqlite3_stmt* stmt) : stmt_(stmt) {}
478 std::optional<std::string> GetString(int pos) const {
479 sqlite3_stmt* stmt = const_cast<sqlite3_stmt*>(stmt_);
480 const char* text = reinterpret_cast<const char*>(
481 sqlite3_column_text(stmt, pos));
488 AutoDbType Get(int pos) const {
489 sqlite3_stmt* stmt = const_cast<sqlite3_stmt*>(stmt_);
490 int type = sqlite3_column_type(stmt, pos);
493 if (type == SQLITE_TEXT) {
494 dbt = DbType(reinterpret_cast<const char*>(
495 sqlite3_column_text(stmt, pos)));
496 } else if (type == SQLITE_INTEGER) {
497 dbt = DbType(static_cast<int64_t>(sqlite3_column_int64(stmt, pos)));
498 } else if (type == SQLITE_FLOAT) {
499 dbt = DbType(sqlite3_column_double(stmt, pos));
500 } else if (type == SQLITE_BLOB) {
501 const unsigned char* val = reinterpret_cast<const unsigned char*>(
502 sqlite3_column_blob(stmt, pos));
503 int len = sqlite3_column_bytes(stmt, pos);
505 if (!val || len < 0) {
506 throw DbException("invalid blob");;
508 dbt = DbType(std::vector<unsigned char>(val, val + len));
510 } else if (type == SQLITE_NULL) {
511 dbt = DbType(std::nullopt);
513 throw DbException("invalid column type", type);
516 return AutoDbType(dbt, type);
519 template <typename ...Types>
521 std::tuple<Types...> t;
523 for_<std::tuple_size_v<std::tuple<Types...>>>([&] (auto i) {
524 std::get<i.value>(t) = Get(pos++);
531 const sqlite3_stmt* stmt_;
536 explicit Iterator(sqlite3_stmt* stmt) : stmt_(stmt) {}
538 Record operator*() { return Record(stmt_); }
540 bool operator != (const Iterator& rhs) const {
541 return stmt_ != rhs.stmt_;
545 int r = sqlite3_step(stmt_);
551 sqlite3_stmt* stmt_ = nullptr;
554 Iterator begin() const {
556 return Iterator(nullptr);
557 return Iterator(stmt_);
560 Iterator end() const {
561 return Iterator(nullptr);
564 operator bool() const {
565 if (stmt_ == nullptr)
570 explicit operator int() const {
571 auto db = db_.lock();
574 return sqlite3_errcode(db.get());
577 explicit operator const char*() const {
578 auto db = db_.lock();
581 return sqlite3_errmsg(db.get());
585 std::vector<T> ToVector() {
586 if (stmt_ == nullptr)
590 for (const auto& rec : *this) {
593 vec.push_back(std::move(t));
600 std::list<T> ToList() {
601 if (stmt_ == nullptr)
605 for (const auto& rec : *this) {
608 l.push_back(std::move(t));
615 std::optional<T> GetFirst() {
616 if (stmt_ == nullptr)
618 for (const auto& rec : *this) {
627 std::optional<Record> GetFirstRecord() {
628 if (stmt_ == nullptr)
630 for (const auto& rec : *this) {
637 sqlite3_stmt* GetRaw() const {
641 const std::string& GetQuery() const {
645 void SetDone(bool is_done) {
649 size_t GetColumnCount() const {
650 return sqlite3_column_count(stmt_);
654 friend class Database;
655 Result(sqlite3_stmt* stmt, const std::shared_ptr<sqlite3>& db, std::string query, bool is_done)
656 : stmt_(stmt), db_(db), query_(std::move(query)), is_done_(is_done) {}
658 sqlite3_stmt* stmt_ = nullptr;
659 std::weak_ptr<sqlite3> db_;
661 bool is_done_ = false;
664 Database(std::string db_path, int flags) {
665 sqlite3* raw_db = nullptr;
666 int r = sqlite3_open_v2(db_path.c_str(), &raw_db, flags, nullptr);
668 throw DbException("open failed", r);
670 db_.reset(raw_db, sqlite3_close_v2);
673 Database(std::string db_path, int flags,
674 std::function<bool(int)> busy_handler) {
675 sqlite3* raw_db = nullptr;
676 int r = sqlite3_open_v2(db_path.c_str(), &raw_db, flags, nullptr);
678 throw DbException("sqlite3_open_v2() failed", r);
680 db_.reset(raw_db, sqlite3_close_v2);
681 busy_handler_ = std::move(busy_handler);
682 r = sqlite3_busy_handler(db_.get(), [](void* data, int count) {
683 Database* pDb = static_cast<Database*>(data);
684 if (pDb->busy_handler_ && pDb->busy_handler_(count))
690 throw DbException("sqlite3_busy_handler() failed", r);
693 ~Database() = default;
694 Database() = default;
695 Database(const Database&) = delete;
696 Database& operator = (const Database&) = delete;
698 Database(Database&& db) noexcept {
699 db_ = std::move(db.db_);
700 busy_handler_ = std::move(db.busy_handler_);
702 db.busy_handler_ = nullptr;
704 sqlite3_busy_handler(db_.get(), [](void* data, int count) {
705 Database* pDb = static_cast<Database*>(data);
706 if (pDb->busy_handler_ && pDb->busy_handler_(count))
712 explicit operator bool() const {
718 Database& operator = (Database&& db) noexcept {
720 db_ = std::move(db.db_);
721 busy_handler_ = std::move(db.busy_handler_);
723 db.busy_handler_ = nullptr;
724 sqlite3_busy_handler(db_.get(), [](void* data, int count) {
725 Database* pDb = static_cast<Database*>(data);
726 if (pDb->busy_handler_ && pDb->busy_handler_(count))
735 TransactionGuard CreateTransactionGuard() const {
736 return TransactionGuard(db_);
739 Result Prepare(const Sql& sql) const {
741 throw DbException("Not opened");
743 sqlite3_stmt* stmt = nullptr;
744 int r = sqlite3_prepare_v2(db_.get(), sql.GetQuery().c_str(),
747 return { nullptr, db_, "", true };
748 return { stmt, db_, sql.GetQuery(), false };
751 Result Exec(const Sql& sql) const {
753 throw DbException("Not opened");
755 sqlite3_stmt* stmt = nullptr;
756 int r = sqlite3_prepare_v2(db_.get(), sql.GetQuery().c_str(),
758 if (r != SQLITE_OK) {
759 return { nullptr, db_, "", true };
762 std::unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)*> stmt_auto(stmt,
765 for (const auto& i : sql.GetBindings()) {
766 Bind(pos++, i, stmt);
769 for (const auto& i : sql.GetBindingMap()) {
770 Bind(i.first, i.second, stmt);
773 for (const auto& i : sql.GetBindingNameMap()) {
774 int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
776 throw DbException("Invalid binding");
777 Bind(pos, i.second, stmt);
780 r = sqlite3_step(stmt);
781 if (r != SQLITE_ROW && r != SQLITE_DONE) {
782 return { nullptr, db_, "", true };
785 return { stmt_auto.release(), db_, sql.GetQuery(),
786 r == SQLITE_DONE ? true : false };
789 bool Exec(const Sql& sql, Result& previous_stmt) const {
790 if (sql.GetQuery() != previous_stmt.GetQuery())
791 throw DbException("Query is different");
793 sqlite3_stmt* stmt = previous_stmt.GetRaw();
797 int r = sqlite3_reset(stmt);
798 if (r != SQLITE_ROW && r != SQLITE_DONE && r != SQLITE_OK)
802 for (const auto& i : sql.GetBindings()) {
803 Bind(pos++, i, stmt);
806 for (const auto& i : sql.GetBindingMap()) {
807 Bind(i.first, i.second, stmt);
810 for (const auto& i : sql.GetBindingNameMap()) {
811 int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
813 throw DbException("Invalid binding");
814 Bind(pos, i.second, stmt);
817 r = sqlite3_step(stmt);
818 if (r != SQLITE_ROW && r != SQLITE_DONE)
821 previous_stmt.SetDone(r == SQLITE_DONE ? true : false);
825 void OneStepExec(const Sql& sql) const {
826 char* errmsg = nullptr;
827 int ret = sqlite3_exec(db_.get(), sql.GetQuery().c_str(), nullptr, nullptr,
829 if (ret != SQLITE_OK) {
830 std::unique_ptr<char, decltype(sqlite3_free)*> errmsg_auto(
831 errmsg, sqlite3_free);
832 throw DbException(errmsg);
836 sqlite3* GetRaw() const {
838 throw DbException("Not opened");
843 void Bind(int pos, const DbType& type, sqlite3_stmt* stmt) const {
846 r = sqlite3_bind_null(stmt, pos);
847 if (r != SQLITE_OK) {
848 throw DbException("Invalid binding", r);
854 if (const std::string* pstr = std::get_if<std::string>(&(*type))) {
855 r = sqlite3_bind_text(stmt, pos, (*pstr).c_str(), -1,
857 } else if (const int64_t* pint = std::get_if<int64_t>(&(*type))) {
858 r = sqlite3_bind_int64(stmt, pos, (*pint));
859 } else if (const double* pdouble = std::get_if<double>(&(*type))) {
860 r = sqlite3_bind_double(stmt, pos, (*pdouble));
861 } else if (const std::vector<unsigned char>* pvector =
862 std::get_if<std::vector<unsigned char>>(&(*type))) {
863 r = sqlite3_bind_blob(stmt, pos, (*pvector).data(),
864 (*pvector).size(), nullptr);
869 if (r != SQLITE_OK) {
870 throw DbException("Invalid binding");
875 std::shared_ptr<sqlite3> db_ = nullptr;
876 std::function<bool(int)> busy_handler_;
879 } // namespace tizen_base
881 #endif // TIZEN_DATABASE_DATABASE_HPP_