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) : db_type_(db_type) {}
84 explicit operator int () {
86 throw DbException("invalid type conversion from nullopt to int");
87 return std::get<int64_t>(*db_type_);
90 explicit operator int64_t () {
92 throw DbException("invalid type conversion from nullopt to int");
93 return std::get<int64_t>(*db_type_);
96 explicit operator std::string () {
99 "invalid type conversion from nullopt to string");
102 return std::get<std::string>(*db_type_);
105 explicit operator double () {
108 "invalid type conversion from nullopt to double");
111 return std::get<double>(*db_type_);
114 explicit operator std::vector<unsigned char> () {
117 "invalid type conversion from nullopt to std::vector<unsigned char>");
120 return std::get<std::vector<unsigned char>>(*db_type_);
123 operator std::optional<int> () {
126 return std::get<int64_t>(*db_type_);
129 operator std::optional<int64_t> () {
132 return std::get<int64_t>(*db_type_);
135 operator std::optional<std::string> () {
138 return std::get<std::string>(*db_type_);
141 operator std::optional<double> () {
144 return std::get<double>(*db_type_);
147 operator std::optional<std::vector<unsigned char>> () {
150 return std::get<std::vector<unsigned char>>(*db_type_);
157 using _ = AutoDbType;
161 class TransactionGuard {
163 TransactionGuard(const TransactionGuard&) = delete;
164 TransactionGuard& operator = (const TransactionGuard&) = delete;
166 TransactionGuard(TransactionGuard&& t) noexcept {
171 TransactionGuard& operator = (TransactionGuard&& t) noexcept {
174 sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
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);
189 ~TransactionGuard() {
191 sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
195 int ret = sqlite3_exec(db_, "COMMIT", nullptr, nullptr, nullptr);
196 if (ret != SQLITE_OK) {
197 sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
205 sqlite3* db_ = nullptr;
210 Sql(std::string query) : query_(std::move(query)) {}
212 Sql& Bind(std::string val) {
213 if (empty_string_as_null_ && val.empty())
214 bindings_.push_back(DbType(std::nullopt));
216 bindings_.push_back(DbType(std::move(val)));
221 bindings_.push_back(DbType(static_cast<int64_t>(val)));
225 Sql& Bind(int64_t val) {
226 bindings_.push_back(DbType(val));
230 Sql& Bind(double val) {
231 bindings_.push_back(DbType(val));
235 Sql& Bind(std::vector<unsigned char> val) {
236 if (empty_vector_as_null_ && val.empty())
237 bindings_.push_back(DbType(std::nullopt));
239 bindings_.push_back(DbType(std::move(val)));
243 Sql& Bind(const std::nullopt_t val) {
244 bindings_.push_back(DbType(val));
248 Sql& Bind(int pos, std::string val) {
249 if (empty_string_as_null_ && val.empty())
250 binding_map_[pos] = DbType(std::nullopt);
252 binding_map_[pos] = DbType(std::move(val));
256 Sql& Bind(int pos, int val) {
257 binding_map_[pos] = DbType(static_cast<int64_t>(val));
261 Sql& Bind(int pos, int64_t val) {
262 binding_map_[pos] = DbType(val);
266 Sql& Bind(int pos, double val) {
267 binding_map_[pos] = DbType(val);
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);
275 binding_map_[pos] = DbType(std::move(val));
279 Sql& Bind(int pos, const std::nullopt_t& val) {
280 binding_map_[pos] = DbType(val);
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);
288 binding_name_map_[std::move(name)] = DbType(std::move(val));
292 Sql& Bind(std::string name, int val) {
293 binding_name_map_[std::move(name)] = DbType(static_cast<int64_t>(val));
297 Sql& Bind(std::string name, int64_t val) {
298 binding_name_map_[std::move(name)] = DbType(val);
302 Sql& Bind(std::string name, double val) {
303 binding_name_map_[std::move(name)] = DbType(val);
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);
311 binding_name_map_[std::move(name)] = DbType(std::move(val));
315 Sql& Bind(std::string name, const std::nullopt_t& val) {
316 binding_name_map_[std::move(name)] = DbType(val);
320 const std::vector<DbType>& GetBindings() const {
324 const std::map<int, DbType>& GetBindingMap() const {
328 const std::map<std::string, DbType>& GetBindingNameMap() const {
329 return binding_name_map_;
332 const std::string& GetQuery() const {
336 Sql& SetEmptyStringAsNull(bool as_null) {
337 empty_string_as_null_ = as_null;
341 Sql& SetEmptyVectorAsNull(bool as_null) {
342 empty_vector_as_null_ = as_null;
348 binding_map_.clear();
349 binding_name_map_.clear();
350 empty_string_as_null_ = false;
351 empty_vector_as_null_ = false;
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;
369 sqlite3_finalize(stmt_);
372 Result(const Result&) = delete;
373 Result& operator = (const Result&) = delete;
375 Result(Result&& r) noexcept {
378 query_ = std::move(r.query_);
379 is_done_ = r.is_done_;
382 Result& operator = (Result&& r) noexcept {
385 sqlite3_finalize(stmt_);
388 query_ = std::move(r.query_);
389 is_done_ = r.is_done_;
397 explicit Record(const sqlite3_stmt* stmt) : stmt_(stmt) {}
399 AutoDbType Get(int pos) const {
400 sqlite3_stmt* stmt = const_cast<sqlite3_stmt*>(stmt_);
401 int type = sqlite3_column_type(stmt, pos);
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);
416 if (!val || len < 0) {
417 throw DbException("invalid blob");;
419 dbt = DbType(std::vector<unsigned char>(val, val + len));
421 } else if (type == SQLITE_NULL) {
422 dbt = DbType(std::nullopt);
424 throw DbException("invalid column type", type);
427 return AutoDbType(dbt);
430 template <typename ...Types>
432 std::tuple<Types...> t;
434 for_<std::tuple_size_v<std::tuple<Types...>>>([&] (auto i) {
435 std::get<i.value>(t) = Get(pos++);
442 const sqlite3_stmt* stmt_;
447 explicit Iterator(sqlite3_stmt* stmt) : stmt_(stmt) {}
449 Record operator*() { return Record(stmt_); }
451 bool operator != (const Iterator& rhs) const {
452 return stmt_ != rhs.stmt_;
456 int r = sqlite3_step(stmt_);
462 sqlite3_stmt* stmt_ = nullptr;
465 Iterator begin() const {
467 return Iterator(nullptr);
468 return Iterator(stmt_);
471 Iterator end() const {
472 return Iterator(nullptr);
475 operator bool() const {
476 if (stmt_ == nullptr)
481 explicit operator int() const {
484 return sqlite3_errcode(db_);
487 explicit operator const char*() const {
490 return sqlite3_errmsg(db_);
494 std::vector<T> ToVector() {
495 if (stmt_ == nullptr)
499 for (const auto& rec : *this) {
502 vec.push_back(std::move(t));
509 std::list<T> ToList() {
510 if (stmt_ == nullptr)
514 for (const auto& rec : *this) {
517 l.push_back(std::move(t));
524 std::optional<T> GetFirst() {
525 if (stmt_ == nullptr)
527 for (const auto& rec : *this) {
536 std::optional<Record> GetFirstRecord() {
537 if (stmt_ == nullptr)
539 for (const auto& rec : *this) {
546 sqlite3_stmt* GetRaw() const {
550 const std::string& GetQuery() const {
554 void SetDone(bool is_done) {
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) {}
563 sqlite3_stmt* stmt_ = nullptr;
564 sqlite3* db_ = nullptr;
569 Database(std::string db, int flags) {
570 int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
572 throw DbException("open failed", r);
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);
578 throw DbException("sqlite3_open_v2() failed", r);
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))
588 if (r != SQLITE_OK) {
589 sqlite3_close_v2(db_);
590 throw DbException("sqlite3_busy_handler() failed", r);
596 sqlite3_close_v2(db_);
599 Database() = default;
600 Database(const Database&) = delete;
601 Database& operator = (const Database&) = delete;
603 Database(Database&& db) noexcept {
608 explicit operator bool() const {
614 Database& operator = (Database&& db) noexcept {
617 sqlite3_close_v2(db_);
625 TransactionGuard CreateTransactionGuard() const {
626 return TransactionGuard(db_);
629 Result Exec(const Sql& sql) const {
631 throw DbException("Not opened");
633 sqlite3_stmt* stmt = nullptr;
634 int r = sqlite3_prepare_v2(db_, sql.GetQuery().c_str(),
636 if (r != SQLITE_OK) {
637 return { nullptr, nullptr, "", true };
640 std::unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)*> stmt_auto(stmt,
643 for (const auto& i : sql.GetBindings()) {
644 Bind(pos++, i, stmt);
647 for (const auto& i : sql.GetBindingMap()) {
648 Bind(i.first, i.second, stmt);
651 for (const auto& i : sql.GetBindingNameMap()) {
652 int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
654 throw DbException("Invalid binding");
655 Bind(pos, i.second, stmt);
658 r = sqlite3_step(stmt);
659 if (r != SQLITE_ROW && r != SQLITE_DONE) {
660 return { nullptr, db_, "", true };
663 return { stmt_auto.release(), db_, sql.GetQuery(),
664 r == SQLITE_DONE ? true : false };
667 bool Exec(const Sql& sql, Result& previous_stmt) {
668 if (sql.GetQuery() != previous_stmt.GetQuery())
669 throw DbException("Query is different");
671 sqlite3_stmt* stmt = previous_stmt.GetRaw();
675 int r = sqlite3_reset(stmt);
676 if (r != SQLITE_ROW && r != SQLITE_DONE && r != SQLITE_OK)
680 for (const auto& i : sql.GetBindings()) {
681 Bind(pos++, i, stmt);
684 for (const auto& i : sql.GetBindingMap()) {
685 Bind(i.first, i.second, stmt);
688 for (const auto& i : sql.GetBindingNameMap()) {
689 int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
691 throw DbException("Invalid binding");
692 Bind(pos, i.second, stmt);
695 r = sqlite3_step(stmt);
696 if (r != SQLITE_ROW && r != SQLITE_DONE)
699 previous_stmt.SetDone(r == SQLITE_DONE ? true : false);
703 void OneStepExec(const Sql& sql) const {
704 char* errmsg = nullptr;
705 int ret = sqlite3_exec(db_, sql.GetQuery().c_str(), nullptr, nullptr,
707 if (ret != SQLITE_OK) {
708 std::unique_ptr<char, decltype(free)*> errmsg_auto(errmsg, free);
709 throw DbException(errmsg);
714 void Bind(int pos, const DbType& type, sqlite3_stmt* stmt) const {
717 r = sqlite3_bind_null(stmt, pos);
718 if (r != SQLITE_OK) {
719 throw DbException("Invalid binding", r);
725 if (const std::string* pstr = std::get_if<std::string>(&(*type))) {
726 r = sqlite3_bind_text(stmt, pos, (*pstr).c_str(), -1,
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);
740 if (r != SQLITE_OK) {
741 throw DbException("Invalid binding");
746 sqlite3* db_ = nullptr;
747 std::function<bool(int)> busy_handler_;
750 } // namespace tizen_base
752 #endif // TIZEN_DATABASE_DATABASE_HPP_