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_
32 namespace tizen_base {
34 template<std::size_t N>
36 static const constexpr auto value = N;
39 template <class F, std::size_t... Is>
40 void for_(F func, std::index_sequence<Is...>) {
41 (func(num<Is>{}), ...);
44 template <std::size_t N, typename F>
46 for_(func, std::make_index_sequence<N>());
49 using DbType = std::variant<int, double, std::string,
50 std::vector<unsigned char>>;
54 AutoDbType() = default;
55 AutoDbType(DbType db_type) : db_type_(db_type) {}
58 return std::get<int>(db_type_);
61 operator std::string () {
62 return std::get<std::string>(db_type_);
66 return std::get<double>(db_type_);
69 operator std::vector<unsigned char> () {
70 return std::get<std::vector<unsigned char>>(db_type_);
81 class TransactionGuard {
83 TransactionGuard(const TransactionGuard&) = delete;
84 TransactionGuard& operator = (const TransactionGuard&) = delete;
86 TransactionGuard(TransactionGuard&& t) noexcept {
91 TransactionGuard& operator = (TransactionGuard&& t) noexcept {
94 sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
102 TransactionGuard(sqlite3* db) : db_(db) {
103 if (sqlite3_exec(db, "BEGIN DEFERRED", nullptr, nullptr, nullptr)
105 throw std::runtime_error("begin transaction failed");
109 ~TransactionGuard() {
111 sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
115 int ret = sqlite3_exec(db_, "COMMIT", nullptr, nullptr, nullptr);
116 if (ret != SQLITE_OK) {
117 sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
125 sqlite3* db_ = nullptr;
130 Sql(std::string query) : query_(std::move(query)) {}
132 Sql& Bind(std::string val) {
133 bindings_.push_back(DbType(std::move(val)));
138 bindings_.push_back(DbType(val));
142 Sql& Bind(double val) {
143 bindings_.push_back(DbType(val));
147 Sql& Bind(std::vector<unsigned char> val) {
148 bindings_.push_back(DbType(std::move(val)));
152 Sql& Bind(int pos, std::string val) {
153 binding_map_[pos] = DbType(std::move(val));
157 Sql& Bind(int pos, int val) {
158 binding_map_[pos] = DbType(val);
162 Sql& Bind(int pos, double val) {
163 binding_map_[pos] = DbType(val);
167 Sql& Bind(int pos, std::vector<unsigned char> val) {
168 binding_map_[pos] = DbType(std::move(val));
172 Sql& Bind(std::string name, std::string val) {
173 binding_name_map_[std::move(name)] = DbType(std::move(val));
177 Sql& Bind(std::string name, int val) {
178 binding_name_map_[std::move(name)] = DbType(val);
182 Sql& Bind(std::string name, double val) {
183 binding_name_map_[std::move(name)] = DbType(val);
187 Sql& Bind(std::string name, std::vector<unsigned char> val) {
188 binding_name_map_[std::move(name)] = DbType(std::move(val));
192 const std::vector<DbType>& GetBindings() const {
196 const std::map<int, DbType>& GetBindingMap() const {
200 const std::map<std::string, DbType>& GetBindingNameMap() const {
201 return binding_name_map_;
204 const std::string& GetQuery() const {
210 std::vector<DbType> bindings_;
211 std::map<int, DbType> binding_map_;
212 std::map<std::string, DbType> binding_name_map_;
220 sqlite3_finalize(stmt_);
223 Result(const Result&) = delete;
224 Result& operator = (const Result&) = delete;
226 Result(Result&& r) noexcept {
231 Result& operator = (Result&& r) noexcept {
234 sqlite3_finalize(stmt_);
244 Record(const sqlite3_stmt* stmt) : stmt_(stmt) {}
246 AutoDbType Get(int pos) const {
247 sqlite3_stmt* stmt = const_cast<sqlite3_stmt*>(stmt_);
248 int type = sqlite3_column_type(stmt, pos);
249 if (type == SQLITE_NULL)
250 throw std::runtime_error("invalid column");
253 if (type == SQLITE_TEXT) {
254 dbt = DbType(reinterpret_cast<const char*>(
255 sqlite3_column_text(stmt, pos)));
256 } else if (type == SQLITE_INTEGER) {
257 dbt = DbType(sqlite3_column_int(stmt, pos));
258 } else if (type == SQLITE_FLOAT) {
259 dbt = DbType(sqlite3_column_double(stmt, pos));
260 } else if (type == SQLITE_BLOB) {
261 const unsigned char* val = reinterpret_cast<const unsigned char*>(
262 sqlite3_column_blob(stmt, pos));
263 int len = sqlite3_column_bytes(stmt, pos);
265 if (!val || len < 0) {
266 throw std::runtime_error("invalid blob");;
268 dbt = DbType(std::vector<unsigned char>(val, val + len));
271 throw std::runtime_error("invalid column type");
274 return AutoDbType(dbt);
277 template <typename ...Types>
279 std::tuple<Types...> t;
281 for_<std::tuple_size_v<std::tuple<Types...>>>([&] (auto i) {
282 std::get<i.value>(t) = Get(pos++);
289 const sqlite3_stmt* stmt_;
294 Iterator(sqlite3_stmt* stmt) : stmt_(stmt) {}
296 Record operator*() { return Record(stmt_); }
298 bool operator != (const Iterator& rhs) const {
299 return stmt_ != rhs.stmt_;
303 int r = sqlite3_step(stmt_);
309 sqlite3_stmt* stmt_ = nullptr;
312 Iterator begin() const {
313 return Iterator(stmt_);
316 Iterator end() const {
317 return Iterator(nullptr);
320 explicit operator bool() {
321 if (stmt_ == nullptr)
326 explicit operator int() {
329 return sqlite3_errcode(db_);
332 operator const char*() {
335 return sqlite3_errmsg(db_);
339 std::vector<T> ToVector() {
340 if (stmt_ == nullptr)
344 for (const auto& rec : *this) {
347 vec.push_back(std::move(t));
354 std::list<T> ToList() {
355 if (stmt_ == nullptr)
359 for (const auto& rec : *this) {
362 l.push_back(std::move(t));
369 std::optional<T> GetFirst() {
370 if (stmt_ == nullptr)
372 for (const auto& rec : *this) {
381 std::optional<Record> GetFirstRecord() {
382 if (stmt_ == nullptr)
384 for (const auto& rec : *this) {
392 friend class Database;
393 Result(sqlite3_stmt* stmt, sqlite3* db) : stmt_(stmt), db_(db) {}
395 sqlite3_stmt* stmt_ = nullptr;
396 sqlite3* db_ = nullptr;
399 Database(std::string db, int flags) {
400 int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
402 throw std::runtime_error("open failed");
405 Database(std::string db, int flags, std::function<bool(int)> busy_handler) {
406 int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
408 throw std::runtime_error("sqlite3_open_v2() failed");
410 busy_handler_ = std::move(busy_handler);
411 r = sqlite3_busy_handler(db_, [](void* data, int count) {
412 Database* pDb = static_cast<Database*>(data);
413 if (pDb->busy_handler_(count))
418 if (r != SQLITE_OK) {
419 sqlite3_close_v2(db_);
420 throw std::runtime_error("sqlite3_busy_handler() failed");
426 sqlite3_close_v2(db_);
429 Database() = default;
430 Database(const Database&) = delete;
431 Database& operator = (const Database&) = delete;
433 Database(Database&& db) noexcept {
438 explicit operator bool() {
444 Database& operator = (Database&& db) noexcept {
447 sqlite3_close_v2(db_);
455 TransactionGuard CreateTransactionGuard() {
456 return TransactionGuard(db_);
459 Result Exec(const Sql& sql) const {
461 throw std::runtime_error("Not opened");
463 sqlite3_stmt* stmt = nullptr;
464 int r = sqlite3_prepare_v2(db_, sql.GetQuery().c_str(),
466 if (r != SQLITE_OK) {
467 return { nullptr, nullptr };
470 std::unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)*> stmt_auto(stmt,
473 for (const auto& i : sql.GetBindings()) {
474 Bind(pos++, i, stmt);
477 for (const auto& i : sql.GetBindingMap()) {
478 Bind(i.first, i.second, stmt);
481 for (const auto& i : sql.GetBindingNameMap()) {
482 int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
484 throw std::runtime_error("Invalid binding");
485 Bind(pos, i.second, stmt);
488 r = sqlite3_step(stmt);
489 if (r != SQLITE_ROW && r != SQLITE_DONE) {
490 return { nullptr, db_ };
493 return { stmt_auto.release(), db_ };
497 void Bind(int pos, const DbType& type, sqlite3_stmt* stmt) const {
499 if (const std::string* pstr = std::get_if<std::string>(&type)) {
500 r = sqlite3_bind_text(stmt, pos, (*pstr).c_str(), -1,
502 } else if (const int* pint = std::get_if<int>(&type)) {
503 r = sqlite3_bind_int(stmt, pos, (*pint));
504 } else if (const double* pdouble = std::get_if<double>(&type)) {
505 r = sqlite3_bind_double(stmt, pos, (*pdouble));
506 } else if (const std::vector<unsigned char>* pvector =
507 std::get_if<std::vector<unsigned char>>(&type)) {
508 r = sqlite3_bind_blob(stmt, pos, (*pvector).data(),
509 (*pvector).size(), nullptr);
514 if (r != SQLITE_OK) {
515 throw std::runtime_error("Invalid binding");
520 sqlite3* db_ = nullptr;
521 std::function<bool(int)> busy_handler_;
524 } // namespace tizen_base
526 #endif // TIZEN_DATABASE_DATABASE_HPP_