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(const char* val) {
213 if (val == nullptr) {
214 bindings_.push_back(DbType(std::nullopt));
216 std::string str = val;
217 if (empty_string_as_null_ && str.empty())
218 bindings_.push_back(DbType(std::nullopt));
220 bindings_.push_back(DbType(std::move(str)));
225 Sql& Bind(std::string val) {
226 if (empty_string_as_null_ && val.empty())
227 bindings_.push_back(DbType(std::nullopt));
229 bindings_.push_back(DbType(std::move(val)));
234 bindings_.push_back(DbType(static_cast<int64_t>(val)));
238 Sql& Bind(int64_t val) {
239 bindings_.push_back(DbType(val));
243 Sql& Bind(double val) {
244 bindings_.push_back(DbType(val));
248 Sql& Bind(std::vector<unsigned char> val) {
249 if (empty_vector_as_null_ && val.empty())
250 bindings_.push_back(DbType(std::nullopt));
252 bindings_.push_back(DbType(std::move(val)));
256 Sql& Bind(const std::nullopt_t val) {
257 bindings_.push_back(DbType(val));
261 Sql& Bind(int pos, const char* val) {
262 if (val == nullptr) {
263 binding_map_[pos] = DbType(std::nullopt);
265 std::string str = val;
266 if (empty_string_as_null_ && str.empty())
267 binding_map_[pos] = DbType(std::nullopt);
269 binding_map_[pos] = DbType(std::move(str));
274 Sql& Bind(int pos, std::string val) {
275 if (empty_string_as_null_ && val.empty())
276 binding_map_[pos] = DbType(std::nullopt);
278 binding_map_[pos] = DbType(std::move(val));
282 Sql& Bind(int pos, int val) {
283 binding_map_[pos] = DbType(static_cast<int64_t>(val));
287 Sql& Bind(int pos, int64_t val) {
288 binding_map_[pos] = DbType(val);
292 Sql& Bind(int pos, double val) {
293 binding_map_[pos] = DbType(val);
297 Sql& Bind(int pos, std::vector<unsigned char> val) {
298 if (empty_vector_as_null_ && val.empty())
299 binding_map_[pos] = DbType(std::nullopt);
301 binding_map_[pos] = DbType(std::move(val));
305 Sql& Bind(int pos, const std::nullopt_t& val) {
306 binding_map_[pos] = DbType(val);
310 Sql& Bind(std::string name, const char* val) {
311 if (val == nullptr) {
312 binding_name_map_[std::move(name)] = DbType(std::nullopt);
314 std::string str = val;
315 if (empty_string_as_null_ && str.empty())
316 binding_name_map_[std::move(name)] = DbType(std::nullopt);
318 binding_name_map_[std::move(name)] = DbType(std::move(str));
323 Sql& Bind(std::string name, std::string val) {
324 if (empty_string_as_null_ && val.empty())
325 binding_name_map_[std::move(name)] = DbType(std::nullopt);
327 binding_name_map_[std::move(name)] = DbType(std::move(val));
331 Sql& Bind(std::string name, int val) {
332 binding_name_map_[std::move(name)] = DbType(static_cast<int64_t>(val));
336 Sql& Bind(std::string name, int64_t val) {
337 binding_name_map_[std::move(name)] = DbType(val);
341 Sql& Bind(std::string name, double val) {
342 binding_name_map_[std::move(name)] = DbType(val);
346 Sql& Bind(std::string name, std::vector<unsigned char> val) {
347 if (empty_vector_as_null_ && val.empty())
348 binding_name_map_[std::move(name)] = DbType(std::nullopt);
350 binding_name_map_[std::move(name)] = DbType(std::move(val));
354 Sql& Bind(std::string name, const std::nullopt_t& val) {
355 binding_name_map_[std::move(name)] = DbType(val);
359 const std::vector<DbType>& GetBindings() const {
363 const std::map<int, DbType>& GetBindingMap() const {
367 const std::map<std::string, DbType>& GetBindingNameMap() const {
368 return binding_name_map_;
371 const std::string& GetQuery() const {
375 Sql& SetEmptyStringAsNull(bool as_null) {
376 empty_string_as_null_ = as_null;
380 Sql& SetEmptyVectorAsNull(bool as_null) {
381 empty_vector_as_null_ = as_null;
387 binding_map_.clear();
388 binding_name_map_.clear();
389 empty_string_as_null_ = false;
390 empty_vector_as_null_ = false;
396 std::vector<DbType> bindings_;
397 std::map<int, DbType> binding_map_;
398 std::map<std::string, DbType> binding_name_map_;
399 bool empty_string_as_null_ = false;
400 bool empty_vector_as_null_ = false;
408 sqlite3_finalize(stmt_);
411 Result(const Result&) = delete;
412 Result& operator = (const Result&) = delete;
414 Result(Result&& r) noexcept {
417 query_ = std::move(r.query_);
418 is_done_ = r.is_done_;
421 Result& operator = (Result&& r) noexcept {
424 sqlite3_finalize(stmt_);
427 query_ = std::move(r.query_);
428 is_done_ = r.is_done_;
436 explicit Record(const sqlite3_stmt* stmt) : stmt_(stmt) {}
438 AutoDbType Get(int pos) const {
439 sqlite3_stmt* stmt = const_cast<sqlite3_stmt*>(stmt_);
440 int type = sqlite3_column_type(stmt, pos);
443 if (type == SQLITE_TEXT) {
444 dbt = DbType(reinterpret_cast<const char*>(
445 sqlite3_column_text(stmt, pos)));
446 } else if (type == SQLITE_INTEGER) {
447 dbt = DbType(static_cast<int64_t>(sqlite3_column_int64(stmt, pos)));
448 } else if (type == SQLITE_FLOAT) {
449 dbt = DbType(sqlite3_column_double(stmt, pos));
450 } else if (type == SQLITE_BLOB) {
451 const unsigned char* val = reinterpret_cast<const unsigned char*>(
452 sqlite3_column_blob(stmt, pos));
453 int len = sqlite3_column_bytes(stmt, pos);
455 if (!val || len < 0) {
456 throw DbException("invalid blob");;
458 dbt = DbType(std::vector<unsigned char>(val, val + len));
460 } else if (type == SQLITE_NULL) {
461 dbt = DbType(std::nullopt);
463 throw DbException("invalid column type", type);
466 return AutoDbType(dbt);
469 template <typename ...Types>
471 std::tuple<Types...> t;
473 for_<std::tuple_size_v<std::tuple<Types...>>>([&] (auto i) {
474 std::get<i.value>(t) = Get(pos++);
481 const sqlite3_stmt* stmt_;
486 explicit Iterator(sqlite3_stmt* stmt) : stmt_(stmt) {}
488 Record operator*() { return Record(stmt_); }
490 bool operator != (const Iterator& rhs) const {
491 return stmt_ != rhs.stmt_;
495 int r = sqlite3_step(stmt_);
501 sqlite3_stmt* stmt_ = nullptr;
504 Iterator begin() const {
506 return Iterator(nullptr);
507 return Iterator(stmt_);
510 Iterator end() const {
511 return Iterator(nullptr);
514 operator bool() const {
515 if (stmt_ == nullptr)
520 explicit operator int() const {
523 return sqlite3_errcode(db_);
526 explicit operator const char*() const {
529 return sqlite3_errmsg(db_);
533 std::vector<T> ToVector() {
534 if (stmt_ == nullptr)
538 for (const auto& rec : *this) {
541 vec.push_back(std::move(t));
548 std::list<T> ToList() {
549 if (stmt_ == nullptr)
553 for (const auto& rec : *this) {
556 l.push_back(std::move(t));
563 std::optional<T> GetFirst() {
564 if (stmt_ == nullptr)
566 for (const auto& rec : *this) {
575 std::optional<Record> GetFirstRecord() {
576 if (stmt_ == nullptr)
578 for (const auto& rec : *this) {
585 sqlite3_stmt* GetRaw() const {
589 const std::string& GetQuery() const {
593 void SetDone(bool is_done) {
598 friend class Database;
599 Result(sqlite3_stmt* stmt, sqlite3* db, std::string query, bool is_done)
600 : stmt_(stmt), db_(db), query_(std::move(query)), is_done_(is_done) {}
602 sqlite3_stmt* stmt_ = nullptr;
603 sqlite3* db_ = nullptr;
608 Database(std::string db, int flags) {
609 int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
611 throw DbException("open failed", r);
614 Database(std::string db, int flags, std::function<bool(int)> busy_handler) {
615 int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
617 throw DbException("sqlite3_open_v2() failed", r);
619 busy_handler_ = std::move(busy_handler);
620 r = sqlite3_busy_handler(db_, [](void* data, int count) {
621 Database* pDb = static_cast<Database*>(data);
622 if (pDb->busy_handler_(count))
627 if (r != SQLITE_OK) {
628 sqlite3_close_v2(db_);
629 throw DbException("sqlite3_busy_handler() failed", r);
635 sqlite3_close_v2(db_);
638 Database() = default;
639 Database(const Database&) = delete;
640 Database& operator = (const Database&) = delete;
642 Database(Database&& db) noexcept {
647 explicit operator bool() const {
653 Database& operator = (Database&& db) noexcept {
656 sqlite3_close_v2(db_);
664 TransactionGuard CreateTransactionGuard() const {
665 return TransactionGuard(db_);
668 Result Exec(const Sql& sql) const {
670 throw DbException("Not opened");
672 sqlite3_stmt* stmt = nullptr;
673 int r = sqlite3_prepare_v2(db_, sql.GetQuery().c_str(),
675 if (r != SQLITE_OK) {
676 return { nullptr, nullptr, "", true };
679 std::unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)*> stmt_auto(stmt,
682 for (const auto& i : sql.GetBindings()) {
683 Bind(pos++, i, stmt);
686 for (const auto& i : sql.GetBindingMap()) {
687 Bind(i.first, i.second, stmt);
690 for (const auto& i : sql.GetBindingNameMap()) {
691 int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
693 throw DbException("Invalid binding");
694 Bind(pos, i.second, stmt);
697 r = sqlite3_step(stmt);
698 if (r != SQLITE_ROW && r != SQLITE_DONE) {
699 return { nullptr, db_, "", true };
702 return { stmt_auto.release(), db_, sql.GetQuery(),
703 r == SQLITE_DONE ? true : false };
706 bool Exec(const Sql& sql, Result& previous_stmt) const {
707 if (sql.GetQuery() != previous_stmt.GetQuery())
708 throw DbException("Query is different");
710 sqlite3_stmt* stmt = previous_stmt.GetRaw();
714 int r = sqlite3_reset(stmt);
715 if (r != SQLITE_ROW && r != SQLITE_DONE && r != SQLITE_OK)
719 for (const auto& i : sql.GetBindings()) {
720 Bind(pos++, i, stmt);
723 for (const auto& i : sql.GetBindingMap()) {
724 Bind(i.first, i.second, stmt);
727 for (const auto& i : sql.GetBindingNameMap()) {
728 int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
730 throw DbException("Invalid binding");
731 Bind(pos, i.second, stmt);
734 r = sqlite3_step(stmt);
735 if (r != SQLITE_ROW && r != SQLITE_DONE)
738 previous_stmt.SetDone(r == SQLITE_DONE ? true : false);
742 void OneStepExec(const Sql& sql) const {
743 char* errmsg = nullptr;
744 int ret = sqlite3_exec(db_, sql.GetQuery().c_str(), nullptr, nullptr,
746 if (ret != SQLITE_OK) {
747 std::unique_ptr<char, decltype(free)*> errmsg_auto(errmsg, free);
748 throw DbException(errmsg);
752 sqlite3* GetRaw() const {
754 throw DbException("Not opened");
759 void Bind(int pos, const DbType& type, sqlite3_stmt* stmt) const {
762 r = sqlite3_bind_null(stmt, pos);
763 if (r != SQLITE_OK) {
764 throw DbException("Invalid binding", r);
770 if (const std::string* pstr = std::get_if<std::string>(&(*type))) {
771 r = sqlite3_bind_text(stmt, pos, (*pstr).c_str(), -1,
773 } else if (const int64_t* pint = std::get_if<int64_t>(&(*type))) {
774 r = sqlite3_bind_int64(stmt, pos, (*pint));
775 } else if (const double* pdouble = std::get_if<double>(&(*type))) {
776 r = sqlite3_bind_double(stmt, pos, (*pdouble));
777 } else if (const std::vector<unsigned char>* pvector =
778 std::get_if<std::vector<unsigned char>>(&(*type))) {
779 r = sqlite3_bind_blob(stmt, pos, (*pvector).data(),
780 (*pvector).size(), nullptr);
785 if (r != SQLITE_OK) {
786 throw DbException("Invalid binding");
791 sqlite3* db_ = nullptr;
792 std::function<bool(int)> busy_handler_;
795 } // namespace tizen_base
797 #endif // TIZEN_DATABASE_DATABASE_HPP_