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>>>;
55 AutoDbType() = default;
56 AutoDbType(DbType db_type) : db_type_(db_type) {}
58 explicit operator int () {
60 throw std::runtime_error("invalid type conversion from nullopt to int");
61 return std::get<int>(*db_type_);
64 explicit operator std::string () {
66 throw std::runtime_error(
67 "invalid type conversion from nullopt to string");
70 return std::get<std::string>(*db_type_);
73 explicit operator double () {
75 throw std::runtime_error(
76 "invalid type conversion from nullopt to double");
79 return std::get<double>(*db_type_);
82 explicit operator std::vector<unsigned char> () {
84 throw std::runtime_error(
85 "invalid type conversion from nullopt to std::vector<unsigned char>");
88 return std::get<std::vector<unsigned char>>(*db_type_);
91 operator std::optional<int> () {
94 return std::get<int>(*db_type_);
97 operator std::optional<std::string> () {
100 return std::get<std::string>(*db_type_);
103 operator std::optional<double> () {
106 return std::get<double>(*db_type_);
109 operator std::optional<std::vector<unsigned char>> () {
112 return std::get<std::vector<unsigned char>>(*db_type_);
119 using _ = AutoDbType;
123 class TransactionGuard {
125 TransactionGuard(const TransactionGuard&) = delete;
126 TransactionGuard& operator = (const TransactionGuard&) = delete;
128 TransactionGuard(TransactionGuard&& t) noexcept {
133 TransactionGuard& operator = (TransactionGuard&& t) noexcept {
136 sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
144 TransactionGuard(sqlite3* db) : db_(db) {
145 if (sqlite3_exec(db, "BEGIN DEFERRED", nullptr, nullptr, nullptr)
147 throw std::runtime_error("begin transaction failed");
151 ~TransactionGuard() {
153 sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
157 int ret = sqlite3_exec(db_, "COMMIT", nullptr, nullptr, nullptr);
158 if (ret != SQLITE_OK) {
159 sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
167 sqlite3* db_ = nullptr;
172 Sql(std::string query) : query_(std::move(query)) {}
174 Sql& Bind(std::string val) {
175 bindings_.push_back(DbType(std::move(val)));
180 bindings_.push_back(DbType(val));
184 Sql& Bind(double val) {
185 bindings_.push_back(DbType(val));
189 Sql& Bind(std::vector<unsigned char> val) {
190 bindings_.push_back(DbType(std::move(val)));
194 Sql& Bind(const std::nullopt_t val) {
195 bindings_.push_back(DbType(val));
199 Sql& Bind(int pos, std::string val) {
200 binding_map_[pos] = DbType(std::move(val));
204 Sql& Bind(int pos, int val) {
205 binding_map_[pos] = DbType(val);
209 Sql& Bind(int pos, double val) {
210 binding_map_[pos] = DbType(val);
214 Sql& Bind(int pos, std::vector<unsigned char> val) {
215 binding_map_[pos] = DbType(std::move(val));
219 Sql& Bind(int pos, const std::nullopt_t& val) {
220 binding_map_[pos] = DbType(val);
224 Sql& Bind(std::string name, std::string val) {
225 binding_name_map_[std::move(name)] = DbType(std::move(val));
229 Sql& Bind(std::string name, int val) {
230 binding_name_map_[std::move(name)] = DbType(val);
234 Sql& Bind(std::string name, double val) {
235 binding_name_map_[std::move(name)] = DbType(val);
239 Sql& Bind(std::string name, std::vector<unsigned char> val) {
240 binding_name_map_[std::move(name)] = DbType(std::move(val));
244 Sql& Bind(std::string name, const std::nullopt_t& val) {
245 binding_name_map_[std::move(name)] = DbType(val);
249 const std::vector<DbType>& GetBindings() const {
253 const std::map<int, DbType>& GetBindingMap() const {
257 const std::map<std::string, DbType>& GetBindingNameMap() const {
258 return binding_name_map_;
261 const std::string& GetQuery() const {
267 std::vector<DbType> bindings_;
268 std::map<int, DbType> binding_map_;
269 std::map<std::string, DbType> binding_name_map_;
277 sqlite3_finalize(stmt_);
280 Result(const Result&) = delete;
281 Result& operator = (const Result&) = delete;
283 Result(Result&& r) noexcept {
288 Result& operator = (Result&& r) noexcept {
291 sqlite3_finalize(stmt_);
301 Record(const sqlite3_stmt* stmt) : stmt_(stmt) {}
303 AutoDbType Get(int pos) const {
304 sqlite3_stmt* stmt = const_cast<sqlite3_stmt*>(stmt_);
305 int type = sqlite3_column_type(stmt, pos);
308 if (type == SQLITE_TEXT) {
309 dbt = DbType(reinterpret_cast<const char*>(
310 sqlite3_column_text(stmt, pos)));
311 } else if (type == SQLITE_INTEGER) {
312 dbt = DbType(sqlite3_column_int(stmt, pos));
313 } else if (type == SQLITE_FLOAT) {
314 dbt = DbType(sqlite3_column_double(stmt, pos));
315 } else if (type == SQLITE_BLOB) {
316 const unsigned char* val = reinterpret_cast<const unsigned char*>(
317 sqlite3_column_blob(stmt, pos));
318 int len = sqlite3_column_bytes(stmt, pos);
320 if (!val || len < 0) {
321 throw std::runtime_error("invalid blob");;
323 dbt = DbType(std::vector<unsigned char>(val, val + len));
325 } else if (type == SQLITE_NULL) {
326 dbt = DbType(std::nullopt);
328 throw std::runtime_error("invalid column type");
331 return AutoDbType(dbt);
334 template <typename ...Types>
336 std::tuple<Types...> t;
338 for_<std::tuple_size_v<std::tuple<Types...>>>([&] (auto i) {
339 std::get<i.value>(t) = Get(pos++);
346 const sqlite3_stmt* stmt_;
351 Iterator(sqlite3_stmt* stmt) : stmt_(stmt) {}
353 Record operator*() { return Record(stmt_); }
355 bool operator != (const Iterator& rhs) const {
356 return stmt_ != rhs.stmt_;
360 int r = sqlite3_step(stmt_);
366 sqlite3_stmt* stmt_ = nullptr;
369 Iterator begin() const {
370 return Iterator(stmt_);
373 Iterator end() const {
374 return Iterator(nullptr);
377 explicit operator bool() {
378 if (stmt_ == nullptr)
383 explicit operator int() {
386 return sqlite3_errcode(db_);
389 operator const char*() {
392 return sqlite3_errmsg(db_);
396 std::vector<T> ToVector() {
397 if (stmt_ == nullptr)
401 for (const auto& rec : *this) {
404 vec.push_back(std::move(t));
411 std::list<T> ToList() {
412 if (stmt_ == nullptr)
416 for (const auto& rec : *this) {
419 l.push_back(std::move(t));
426 std::optional<T> GetFirst() {
427 if (stmt_ == nullptr)
429 for (const auto& rec : *this) {
438 std::optional<Record> GetFirstRecord() {
439 if (stmt_ == nullptr)
441 for (const auto& rec : *this) {
449 friend class Database;
450 Result(sqlite3_stmt* stmt, sqlite3* db) : stmt_(stmt), db_(db) {}
452 sqlite3_stmt* stmt_ = nullptr;
453 sqlite3* db_ = nullptr;
456 Database(std::string db, int flags) {
457 int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
459 throw std::runtime_error("open failed");
462 Database(std::string db, int flags, std::function<bool(int)> busy_handler) {
463 int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
465 throw std::runtime_error("sqlite3_open_v2() failed");
467 busy_handler_ = std::move(busy_handler);
468 r = sqlite3_busy_handler(db_, [](void* data, int count) {
469 Database* pDb = static_cast<Database*>(data);
470 if (pDb->busy_handler_(count))
475 if (r != SQLITE_OK) {
476 sqlite3_close_v2(db_);
477 throw std::runtime_error("sqlite3_busy_handler() failed");
483 sqlite3_close_v2(db_);
486 Database() = default;
487 Database(const Database&) = delete;
488 Database& operator = (const Database&) = delete;
490 Database(Database&& db) noexcept {
495 explicit operator bool() {
501 Database& operator = (Database&& db) noexcept {
504 sqlite3_close_v2(db_);
512 TransactionGuard CreateTransactionGuard() {
513 return TransactionGuard(db_);
516 Result Exec(const Sql& sql) const {
518 throw std::runtime_error("Not opened");
520 sqlite3_stmt* stmt = nullptr;
521 int r = sqlite3_prepare_v2(db_, sql.GetQuery().c_str(),
523 if (r != SQLITE_OK) {
524 return { nullptr, nullptr };
527 std::unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)*> stmt_auto(stmt,
530 for (const auto& i : sql.GetBindings()) {
531 Bind(pos++, i, stmt);
534 for (const auto& i : sql.GetBindingMap()) {
535 Bind(i.first, i.second, stmt);
538 for (const auto& i : sql.GetBindingNameMap()) {
539 int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
541 throw std::runtime_error("Invalid binding");
542 Bind(pos, i.second, stmt);
545 r = sqlite3_step(stmt);
546 if (r != SQLITE_ROW && r != SQLITE_DONE) {
547 return { nullptr, db_ };
550 return { stmt_auto.release(), db_ };
554 void Bind(int pos, const DbType& type, sqlite3_stmt* stmt) const {
557 r = sqlite3_bind_null(stmt, pos);
558 if (r != SQLITE_OK) {
559 throw std::runtime_error("Invalid binding");
565 if (const std::string* pstr = std::get_if<std::string>(&(*type))) {
566 r = sqlite3_bind_text(stmt, pos, (*pstr).c_str(), -1,
568 } else if (const int* pint = std::get_if<int>(&(*type))) {
569 r = sqlite3_bind_int(stmt, pos, (*pint));
570 } else if (const double* pdouble = std::get_if<double>(&(*type))) {
571 r = sqlite3_bind_double(stmt, pos, (*pdouble));
572 } else if (const std::vector<unsigned char>* pvector =
573 std::get_if<std::vector<unsigned char>>(&(*type))) {
574 r = sqlite3_bind_blob(stmt, pos, (*pvector).data(),
575 (*pvector).size(), nullptr);
580 if (r != SQLITE_OK) {
581 throw std::runtime_error("Invalid binding");
586 sqlite3* db_ = nullptr;
587 std::function<bool(int)> busy_handler_;
590 } // namespace tizen_base
592 #endif // TIZEN_DATABASE_DATABASE_HPP_