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<int, 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<int>(*db_type_);
90 explicit operator std::string () {
93 "invalid type conversion from nullopt to string");
96 return std::get<std::string>(*db_type_);
99 explicit operator double () {
102 "invalid type conversion from nullopt to double");
105 return std::get<double>(*db_type_);
108 explicit operator std::vector<unsigned char> () {
111 "invalid type conversion from nullopt to std::vector<unsigned char>");
114 return std::get<std::vector<unsigned char>>(*db_type_);
117 operator std::optional<int> () {
120 return std::get<int>(*db_type_);
123 operator std::optional<std::string> () {
126 return std::get<std::string>(*db_type_);
129 operator std::optional<double> () {
132 return std::get<double>(*db_type_);
135 operator std::optional<std::vector<unsigned char>> () {
138 return std::get<std::vector<unsigned char>>(*db_type_);
145 using _ = AutoDbType;
149 class TransactionGuard {
151 TransactionGuard(const TransactionGuard&) = delete;
152 TransactionGuard& operator = (const TransactionGuard&) = delete;
154 TransactionGuard(TransactionGuard&& t) noexcept {
159 TransactionGuard& operator = (TransactionGuard&& t) noexcept {
162 sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
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);
177 ~TransactionGuard() {
179 sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
183 int ret = sqlite3_exec(db_, "COMMIT", nullptr, nullptr, nullptr);
184 if (ret != SQLITE_OK) {
185 sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
193 sqlite3* db_ = nullptr;
198 Sql(std::string query) : query_(std::move(query)) {}
200 Sql& Bind(std::string val) {
201 if (empty_string_as_null_ && val.empty())
202 bindings_.push_back(DbType(std::nullopt));
204 bindings_.push_back(DbType(std::move(val)));
209 bindings_.push_back(DbType(val));
213 Sql& Bind(double val) {
214 bindings_.push_back(DbType(val));
218 Sql& Bind(std::vector<unsigned char> val) {
219 if (empty_vector_as_null_ && val.empty())
220 bindings_.push_back(DbType(std::nullopt));
222 bindings_.push_back(DbType(std::move(val)));
226 Sql& Bind(const std::nullopt_t val) {
227 bindings_.push_back(DbType(val));
231 Sql& Bind(int pos, std::string val) {
232 if (empty_string_as_null_ && val.empty())
233 binding_map_[pos] = DbType(std::nullopt);
235 binding_map_[pos] = DbType(std::move(val));
239 Sql& Bind(int pos, int val) {
240 binding_map_[pos] = DbType(val);
244 Sql& Bind(int pos, double val) {
245 binding_map_[pos] = DbType(val);
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);
253 binding_map_[pos] = DbType(std::move(val));
257 Sql& Bind(int pos, const std::nullopt_t& val) {
258 binding_map_[pos] = DbType(val);
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);
266 binding_name_map_[std::move(name)] = DbType(std::move(val));
270 Sql& Bind(std::string name, int val) {
271 binding_name_map_[std::move(name)] = DbType(val);
275 Sql& Bind(std::string name, double val) {
276 binding_name_map_[std::move(name)] = DbType(val);
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);
284 binding_name_map_[std::move(name)] = DbType(std::move(val));
288 Sql& Bind(std::string name, const std::nullopt_t& val) {
289 binding_name_map_[std::move(name)] = DbType(val);
293 const std::vector<DbType>& GetBindings() const {
297 const std::map<int, DbType>& GetBindingMap() const {
301 const std::map<std::string, DbType>& GetBindingNameMap() const {
302 return binding_name_map_;
305 const std::string& GetQuery() const {
309 Sql& SetEmptyStringAsNull(bool as_null) {
310 empty_string_as_null_ = as_null;
314 Sql& SetEmptyVectorAsNull(bool as_null) {
315 empty_vector_as_null_ = as_null;
321 binding_map_.clear();
322 binding_name_map_.clear();
323 empty_string_as_null_ = false;
324 empty_vector_as_null_ = false;
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;
342 sqlite3_finalize(stmt_);
345 Result(const Result&) = delete;
346 Result& operator = (const Result&) = delete;
348 Result(Result&& r) noexcept {
351 query_ = std::move(r.query_);
352 is_done_ = r.is_done_;
355 Result& operator = (Result&& r) noexcept {
358 sqlite3_finalize(stmt_);
361 query_ = std::move(r.query_);
362 is_done_ = r.is_done_;
370 explicit Record(const sqlite3_stmt* stmt) : stmt_(stmt) {}
372 AutoDbType Get(int pos) const {
373 sqlite3_stmt* stmt = const_cast<sqlite3_stmt*>(stmt_);
374 int type = sqlite3_column_type(stmt, pos);
377 if (type == SQLITE_TEXT) {
378 dbt = DbType(reinterpret_cast<const char*>(
379 sqlite3_column_text(stmt, pos)));
380 } else if (type == SQLITE_INTEGER) {
381 dbt = DbType(sqlite3_column_int(stmt, pos));
382 } else if (type == SQLITE_FLOAT) {
383 dbt = DbType(sqlite3_column_double(stmt, pos));
384 } else if (type == SQLITE_BLOB) {
385 const unsigned char* val = reinterpret_cast<const unsigned char*>(
386 sqlite3_column_blob(stmt, pos));
387 int len = sqlite3_column_bytes(stmt, pos);
389 if (!val || len < 0) {
390 throw DbException("invalid blob");;
392 dbt = DbType(std::vector<unsigned char>(val, val + len));
394 } else if (type == SQLITE_NULL) {
395 dbt = DbType(std::nullopt);
397 throw DbException("invalid column type", type);
400 return AutoDbType(dbt);
403 template <typename ...Types>
405 std::tuple<Types...> t;
407 for_<std::tuple_size_v<std::tuple<Types...>>>([&] (auto i) {
408 std::get<i.value>(t) = Get(pos++);
415 const sqlite3_stmt* stmt_;
420 explicit Iterator(sqlite3_stmt* stmt) : stmt_(stmt) {}
422 Record operator*() { return Record(stmt_); }
424 bool operator != (const Iterator& rhs) const {
425 return stmt_ != rhs.stmt_;
429 int r = sqlite3_step(stmt_);
435 sqlite3_stmt* stmt_ = nullptr;
438 Iterator begin() const {
440 return Iterator(nullptr);
441 return Iterator(stmt_);
444 Iterator end() const {
445 return Iterator(nullptr);
448 operator bool() const {
449 if (stmt_ == nullptr)
454 explicit operator int() const {
457 return sqlite3_errcode(db_);
460 explicit operator const char*() const {
463 return sqlite3_errmsg(db_);
467 std::vector<T> ToVector() {
468 if (stmt_ == nullptr)
472 for (const auto& rec : *this) {
475 vec.push_back(std::move(t));
482 std::list<T> ToList() {
483 if (stmt_ == nullptr)
487 for (const auto& rec : *this) {
490 l.push_back(std::move(t));
497 std::optional<T> GetFirst() {
498 if (stmt_ == nullptr)
500 for (const auto& rec : *this) {
509 std::optional<Record> GetFirstRecord() {
510 if (stmt_ == nullptr)
512 for (const auto& rec : *this) {
519 sqlite3_stmt* GetRaw() const {
523 const std::string& GetQuery() const {
527 void SetDone(bool is_done) {
532 friend class Database;
533 Result(sqlite3_stmt* stmt, sqlite3* db, std::string query, bool is_done)
534 : stmt_(stmt), db_(db), query_(std::move(query)), is_done_(is_done) {}
536 sqlite3_stmt* stmt_ = nullptr;
537 sqlite3* db_ = nullptr;
542 Database(std::string db, int flags) {
543 int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
545 throw DbException("open failed", r);
548 Database(std::string db, int flags, std::function<bool(int)> busy_handler) {
549 int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
551 throw DbException("sqlite3_open_v2() failed", r);
553 busy_handler_ = std::move(busy_handler);
554 r = sqlite3_busy_handler(db_, [](void* data, int count) {
555 Database* pDb = static_cast<Database*>(data);
556 if (pDb->busy_handler_(count))
561 if (r != SQLITE_OK) {
562 sqlite3_close_v2(db_);
563 throw DbException("sqlite3_busy_handler() failed", r);
569 sqlite3_close_v2(db_);
572 Database() = default;
573 Database(const Database&) = delete;
574 Database& operator = (const Database&) = delete;
576 Database(Database&& db) noexcept {
581 explicit operator bool() const {
587 Database& operator = (Database&& db) noexcept {
590 sqlite3_close_v2(db_);
598 TransactionGuard CreateTransactionGuard() const {
599 return TransactionGuard(db_);
602 Result Exec(const Sql& sql) const {
604 throw DbException("Not opened");
606 sqlite3_stmt* stmt = nullptr;
607 int r = sqlite3_prepare_v2(db_, sql.GetQuery().c_str(),
609 if (r != SQLITE_OK) {
610 return { nullptr, nullptr, "", true };
613 std::unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)*> stmt_auto(stmt,
616 for (const auto& i : sql.GetBindings()) {
617 Bind(pos++, i, stmt);
620 for (const auto& i : sql.GetBindingMap()) {
621 Bind(i.first, i.second, stmt);
624 for (const auto& i : sql.GetBindingNameMap()) {
625 int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
627 throw DbException("Invalid binding");
628 Bind(pos, i.second, stmt);
631 r = sqlite3_step(stmt);
632 if (r != SQLITE_ROW && r != SQLITE_DONE) {
633 return { nullptr, db_, "", true };
636 return { stmt_auto.release(), db_, sql.GetQuery(),
637 r == SQLITE_DONE ? true : false };
640 bool Exec(const Sql& sql, Result& previous_stmt) {
641 if (sql.GetQuery() != previous_stmt.GetQuery())
642 throw DbException("Query is different");
644 sqlite3_stmt* stmt = previous_stmt.GetRaw();
648 int r = sqlite3_reset(stmt);
649 if (r != SQLITE_ROW && r != SQLITE_DONE && r != SQLITE_OK)
653 for (const auto& i : sql.GetBindings()) {
654 Bind(pos++, i, stmt);
657 for (const auto& i : sql.GetBindingMap()) {
658 Bind(i.first, i.second, stmt);
661 for (const auto& i : sql.GetBindingNameMap()) {
662 int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
664 throw DbException("Invalid binding");
665 Bind(pos, i.second, stmt);
668 r = sqlite3_step(stmt);
669 if (r != SQLITE_ROW && r != SQLITE_DONE)
672 previous_stmt.SetDone(r == SQLITE_DONE ? true : false);
676 void OneStepExec(const Sql& sql) const {
677 char* errmsg = nullptr;
678 int ret = sqlite3_exec(db_, sql.GetQuery().c_str(), nullptr, nullptr,
680 if (ret != SQLITE_OK) {
681 std::unique_ptr<char, decltype(free)*> errmsg_auto(errmsg, free);
682 throw DbException(errmsg);
687 void Bind(int pos, const DbType& type, sqlite3_stmt* stmt) const {
690 r = sqlite3_bind_null(stmt, pos);
691 if (r != SQLITE_OK) {
692 throw DbException("Invalid binding", r);
698 if (const std::string* pstr = std::get_if<std::string>(&(*type))) {
699 r = sqlite3_bind_text(stmt, pos, (*pstr).c_str(), -1,
701 } else if (const int* pint = std::get_if<int>(&(*type))) {
702 r = sqlite3_bind_int(stmt, pos, (*pint));
703 } else if (const double* pdouble = std::get_if<double>(&(*type))) {
704 r = sqlite3_bind_double(stmt, pos, (*pdouble));
705 } else if (const std::vector<unsigned char>* pvector =
706 std::get_if<std::vector<unsigned char>>(&(*type))) {
707 r = sqlite3_bind_blob(stmt, pos, (*pvector).data(),
708 (*pvector).size(), nullptr);
713 if (r != SQLITE_OK) {
714 throw DbException("Invalid binding");
719 sqlite3* db_ = nullptr;
720 std::function<bool(int)> busy_handler_;
723 } // namespace tizen_base
725 #endif // TIZEN_DATABASE_DATABASE_HPP_