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 {
353 Result& operator = (Result&& r) noexcept {
356 sqlite3_finalize(stmt_);
366 explicit Record(const sqlite3_stmt* stmt) : stmt_(stmt) {}
368 AutoDbType Get(int pos) const {
369 sqlite3_stmt* stmt = const_cast<sqlite3_stmt*>(stmt_);
370 int type = sqlite3_column_type(stmt, pos);
373 if (type == SQLITE_TEXT) {
374 dbt = DbType(reinterpret_cast<const char*>(
375 sqlite3_column_text(stmt, pos)));
376 } else if (type == SQLITE_INTEGER) {
377 dbt = DbType(sqlite3_column_int(stmt, pos));
378 } else if (type == SQLITE_FLOAT) {
379 dbt = DbType(sqlite3_column_double(stmt, pos));
380 } else if (type == SQLITE_BLOB) {
381 const unsigned char* val = reinterpret_cast<const unsigned char*>(
382 sqlite3_column_blob(stmt, pos));
383 int len = sqlite3_column_bytes(stmt, pos);
385 if (!val || len < 0) {
386 throw DbException("invalid blob");;
388 dbt = DbType(std::vector<unsigned char>(val, val + len));
390 } else if (type == SQLITE_NULL) {
391 dbt = DbType(std::nullopt);
393 throw DbException("invalid column type", type);
396 return AutoDbType(dbt);
399 template <typename ...Types>
401 std::tuple<Types...> t;
403 for_<std::tuple_size_v<std::tuple<Types...>>>([&] (auto i) {
404 std::get<i.value>(t) = Get(pos++);
411 const sqlite3_stmt* stmt_;
416 explicit Iterator(sqlite3_stmt* stmt) : stmt_(stmt) {}
418 Record operator*() { return Record(stmt_); }
420 bool operator != (const Iterator& rhs) const {
421 return stmt_ != rhs.stmt_;
425 int r = sqlite3_step(stmt_);
431 sqlite3_stmt* stmt_ = nullptr;
434 Iterator begin() const {
435 return Iterator(stmt_);
438 Iterator end() const {
439 return Iterator(nullptr);
442 explicit operator bool() {
443 if (stmt_ == nullptr)
448 explicit operator int() {
451 return sqlite3_errcode(db_);
454 operator const char*() {
457 return sqlite3_errmsg(db_);
461 std::vector<T> ToVector() {
462 if (stmt_ == nullptr)
466 for (const auto& rec : *this) {
469 vec.push_back(std::move(t));
476 std::list<T> ToList() {
477 if (stmt_ == nullptr)
481 for (const auto& rec : *this) {
484 l.push_back(std::move(t));
491 std::optional<T> GetFirst() {
492 if (stmt_ == nullptr)
494 for (const auto& rec : *this) {
503 std::optional<Record> GetFirstRecord() {
504 if (stmt_ == nullptr)
506 for (const auto& rec : *this) {
513 sqlite3_stmt* GetRaw() const {
517 const std::string& GetQuery() const {
522 friend class Database;
523 Result(sqlite3_stmt* stmt, sqlite3* db, std::string query)
524 : stmt_(stmt), db_(db), query_(std::move(query)) {}
526 sqlite3_stmt* stmt_ = nullptr;
527 sqlite3* db_ = nullptr;
531 Database(std::string db, int flags) {
532 int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
534 throw DbException("open failed", r);
537 Database(std::string db, int flags, std::function<bool(int)> busy_handler) {
538 int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
540 throw DbException("sqlite3_open_v2() failed", r);
542 busy_handler_ = std::move(busy_handler);
543 r = sqlite3_busy_handler(db_, [](void* data, int count) {
544 Database* pDb = static_cast<Database*>(data);
545 if (pDb->busy_handler_(count))
550 if (r != SQLITE_OK) {
551 sqlite3_close_v2(db_);
552 throw DbException("sqlite3_busy_handler() failed", r);
558 sqlite3_close_v2(db_);
561 Database() = default;
562 Database(const Database&) = delete;
563 Database& operator = (const Database&) = delete;
565 Database(Database&& db) noexcept {
570 explicit operator bool() {
576 Database& operator = (Database&& db) noexcept {
579 sqlite3_close_v2(db_);
587 TransactionGuard CreateTransactionGuard() const {
588 return TransactionGuard(db_);
591 Result Exec(const Sql& sql) const {
593 throw DbException("Not opened");
595 sqlite3_stmt* stmt = nullptr;
596 int r = sqlite3_prepare_v2(db_, sql.GetQuery().c_str(),
598 if (r != SQLITE_OK) {
599 return { nullptr, nullptr, "" };
602 std::unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)*> stmt_auto(stmt,
605 for (const auto& i : sql.GetBindings()) {
606 Bind(pos++, i, stmt);
609 for (const auto& i : sql.GetBindingMap()) {
610 Bind(i.first, i.second, stmt);
613 for (const auto& i : sql.GetBindingNameMap()) {
614 int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
616 throw DbException("Invalid binding");
617 Bind(pos, i.second, stmt);
620 r = sqlite3_step(stmt);
621 if (r != SQLITE_ROW && r != SQLITE_DONE) {
622 return { nullptr, db_, "" };
625 return { stmt_auto.release(), db_, sql.GetQuery() };
628 bool Exec(const Sql& sql, const Result& previous_stmt) {
629 if (sql.GetQuery() != previous_stmt.GetQuery())
630 throw DbException("Query is different");
632 sqlite3_stmt* stmt = previous_stmt.GetRaw();
636 int r = sqlite3_reset(stmt);
637 if (r != SQLITE_ROW && r != SQLITE_DONE && r != SQLITE_OK)
641 for (const auto& i : sql.GetBindings()) {
642 Bind(pos++, i, stmt);
645 for (const auto& i : sql.GetBindingMap()) {
646 Bind(i.first, i.second, stmt);
649 for (const auto& i : sql.GetBindingNameMap()) {
650 int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
652 throw DbException("Invalid binding");
653 Bind(pos, i.second, stmt);
656 r = sqlite3_step(stmt);
657 if (r != SQLITE_ROW && r != SQLITE_DONE)
664 void Bind(int pos, const DbType& type, sqlite3_stmt* stmt) const {
667 r = sqlite3_bind_null(stmt, pos);
668 if (r != SQLITE_OK) {
669 throw DbException("Invalid binding", r);
675 if (const std::string* pstr = std::get_if<std::string>(&(*type))) {
676 r = sqlite3_bind_text(stmt, pos, (*pstr).c_str(), -1,
678 } else if (const int* pint = std::get_if<int>(&(*type))) {
679 r = sqlite3_bind_int(stmt, pos, (*pint));
680 } else if (const double* pdouble = std::get_if<double>(&(*type))) {
681 r = sqlite3_bind_double(stmt, pos, (*pdouble));
682 } else if (const std::vector<unsigned char>* pvector =
683 std::get_if<std::vector<unsigned char>>(&(*type))) {
684 r = sqlite3_bind_blob(stmt, pos, (*pvector).data(),
685 (*pvector).size(), nullptr);
690 if (r != SQLITE_OK) {
691 throw DbException("Invalid binding");
696 sqlite3* db_ = nullptr;
697 std::function<bool(int)> busy_handler_;
700 } // namespace tizen_base
702 #endif // TIZEN_DATABASE_DATABASE_HPP_