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.
30 namespace tizen_base {
32 template<std::size_t N>
34 static const constexpr auto value = N;
37 template <class F, std::size_t... Is>
38 void for_(F func, std::index_sequence<Is...>) {
39 (func(num<Is>{}), ...);
42 template <std::size_t N, typename F>
44 for_(func, std::make_index_sequence<N>());
47 using DbType = std::variant<int, double, std::string,
48 std::vector<unsigned char>>;
52 AutoDbType() = default;
53 AutoDbType(DbType db_type) : db_type_(db_type) {}
56 return std::get<int>(db_type_);
59 operator std::string () {
60 return std::get<std::string>(db_type_);
64 return std::get<double>(db_type_);
67 operator std::vector<unsigned char> () {
68 return std::get<std::vector<unsigned char>>(db_type_);
79 class TransactionGuard {
81 TransactionGuard(const TransactionGuard&) = delete;
82 TransactionGuard& operator = (const TransactionGuard&) = delete;
84 TransactionGuard(TransactionGuard&& t) noexcept {
89 TransactionGuard& operator = (TransactionGuard&& t) noexcept {
92 sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
100 TransactionGuard(sqlite3* db) : db_(db) {
101 if (sqlite3_exec(db, "BEGIN DEFERRED", NULL, NULL, NULL) != SQLITE_OK) {
102 throw std::runtime_error("begin transaction failed");
106 ~TransactionGuard() {
108 sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
112 int ret = sqlite3_exec(db_, "COMMIT", nullptr, nullptr, nullptr);
113 if (ret != SQLITE_OK) {
114 sqlite3_exec(db_, "ROLLBACK", nullptr, nullptr, nullptr);
122 sqlite3* db_ = nullptr;
127 Sql(std::string query) : query_(std::move(query)) {}
129 Sql& Bind(std::string val) {
130 bindings_.push_back(DbType(std::move(val)));
135 bindings_.push_back(DbType(val));
139 Sql& Bind(double val) {
140 bindings_.push_back(DbType(val));
144 Sql& Bind(std::vector<unsigned char> val) {
145 bindings_.push_back(DbType(std::move(val)));
149 Sql& Bind(int pos, std::string val) {
150 binding_map_[pos] = DbType(std::move(val));
154 Sql& Bind(int pos, int val) {
155 binding_map_[pos] = DbType(val);
159 Sql& Bind(int pos, double val) {
160 binding_map_[pos] = DbType(val);
164 Sql& Bind(int pos, std::vector<unsigned char> val) {
165 binding_map_[pos] = DbType(std::move(val));
169 Sql& Bind(std::string name, std::string val) {
170 binding_name_map_[std::move(name)] = DbType(std::move(val));
174 Sql& Bind(std::string name, int val) {
175 binding_name_map_[std::move(name)] = DbType(val);
179 Sql& Bind(std::string name, double val) {
180 binding_name_map_[std::move(name)] = DbType(val);
184 Sql& Bind(std::string name, std::vector<unsigned char> val) {
185 binding_name_map_[std::move(name)] = DbType(std::move(val));
189 const std::vector<DbType>& GetBindings() const {
193 const std::map<int, DbType>& GetBindingMap() const {
197 const std::map<std::string, DbType>& GetBindingNameMap() const {
198 return binding_name_map_;
201 const std::string& GetQuery() const {
207 std::vector<DbType> bindings_;
208 std::map<int, DbType> binding_map_;
209 std::map<std::string, DbType> binding_name_map_;
217 sqlite3_finalize(stmt_);
220 Result(const Result&) = delete;
221 Result& operator = (const Result&) = delete;
223 Result(Result&& r) noexcept {
228 Result& operator = (Result&& r) noexcept {
231 sqlite3_finalize(stmt_);
241 Record(const sqlite3_stmt* stmt) : stmt_(stmt) {}
243 AutoDbType Get(int pos) const {
244 sqlite3_stmt* stmt = const_cast<sqlite3_stmt*>(stmt_);
245 int type = sqlite3_column_type(stmt, pos);
246 if (type == SQLITE_NULL)
247 throw std::runtime_error("invalid column");;
250 if (type == SQLITE_TEXT) {
251 dbt = DbType(reinterpret_cast<const char*>(
252 sqlite3_column_text(stmt, pos)));
253 } else if (type == SQLITE_INTEGER) {
254 dbt = DbType(sqlite3_column_int(stmt, pos));
255 } else if (type == SQLITE_FLOAT) {
256 dbt = DbType(sqlite3_column_double(stmt, pos));
257 } else if (type == SQLITE_BLOB) {
258 const unsigned char* val = reinterpret_cast<const unsigned char*>(
259 sqlite3_column_blob(stmt, pos));
260 int len = sqlite3_column_bytes(stmt, pos);
262 if (!val || len < 0) {
263 throw std::runtime_error("invalid blob");;
265 dbt = DbType(std::vector<unsigned char>(val, val + len));
268 throw std::runtime_error("invalid column type");
271 return AutoDbType(dbt);
274 template <typename ...Types>
276 std::tuple<Types...> t;
278 for_<std::tuple_size_v<std::tuple<Types...>>>([&] (auto i) {
279 std::get<i.value>(t) = Get(pos++);
286 const sqlite3_stmt* stmt_;
291 Iterator(sqlite3_stmt* stmt) : stmt_(stmt) {}
293 Record operator*() { return Record(stmt_); }
295 bool operator != (const Iterator& rhs) const {
296 return stmt_ != rhs.stmt_;
300 int r = sqlite3_step(stmt_);
306 sqlite3_stmt* stmt_ = nullptr;
309 Iterator begin() const {
310 return Iterator(stmt_);
313 Iterator end() const {
314 return Iterator(nullptr);
317 explicit operator bool() {
318 if (stmt_ == nullptr)
323 explicit operator int() {
326 return sqlite3_errcode(db_);
329 operator const char*() {
332 return sqlite3_errmsg(db_);
336 friend class Database;
337 Result(sqlite3_stmt* stmt, sqlite3* db) : stmt_(stmt), db_(db) {}
339 sqlite3_stmt* stmt_ = nullptr;
340 sqlite3* db_ = nullptr;
343 Database(std::string db, int flags) {
344 int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
346 throw std::runtime_error("open failed");
349 Database(std::string db, int flags, std::function<bool(int)> busy_handler) {
350 int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
352 throw std::runtime_error("sqlite3_open_v2() failed");
354 busy_handler_ = std::move(busy_handler);
355 r = sqlite3_busy_handler(db_, [](void* data, int count) {
356 Database* pDb = static_cast<Database*>(data);
357 if (pDb->busy_handler_(count))
362 if (r != SQLITE_OK) {
363 sqlite3_close_v2(db_);
364 throw std::runtime_error("sqlite3_busy_handler() failed");
370 sqlite3_close_v2(db_);
373 Database() = default;
374 Database(const Database&) = delete;
375 Database& operator = (const Database&) = delete;
377 Database(Database&& db) noexcept {
382 explicit operator bool() {
388 Database& operator = (Database&& db) noexcept {
391 sqlite3_close_v2(db_);
399 TransactionGuard CreateTransactionGuard() {
400 return TransactionGuard(db_);
403 Result Exec(const Sql& sql) const {
405 throw std::runtime_error("Not opened");
407 sqlite3_stmt* stmt = nullptr;
408 int r = sqlite3_prepare_v2(db_, sql.GetQuery().c_str(),
410 if (r != SQLITE_OK) {
411 return { nullptr, nullptr };
414 std::unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)*> stmt_auto(stmt,
417 for (const auto& i : sql.GetBindings()) {
418 Bind(pos++, i, stmt);
421 for (const auto& i : sql.GetBindingMap()) {
422 Bind(i.first, i.second, stmt);
425 for (const auto& i : sql.GetBindingNameMap()) {
426 int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
428 throw std::runtime_error("Invalid binding");
429 Bind(pos, i.second, stmt);
432 r = sqlite3_step(stmt);
433 if (r != SQLITE_ROW && r != SQLITE_DONE) {
434 return { nullptr, db_ };
437 return { stmt_auto.release(), db_ };
441 void Bind(int pos, const DbType& type, sqlite3_stmt* stmt) const {
443 if (const std::string* pstr = std::get_if<std::string>(&type)) {
444 r = sqlite3_bind_text(stmt, pos, (*pstr).c_str(), -1,
446 } else if (const int* pint = std::get_if<int>(&type)) {
447 r = sqlite3_bind_int(stmt, pos, (*pint));
448 } else if (const double* pdouble = std::get_if<double>(&type)) {
449 r = sqlite3_bind_double(stmt, pos, (*pdouble));
450 } else if (const std::vector<unsigned char>* pvector =
451 std::get_if<std::vector<unsigned char>>(&type)) {
452 r = sqlite3_bind_blob(stmt, pos, (*pvector).data(),
453 (*pvector).size(), nullptr);
458 if (r != SQLITE_OK) {
459 throw std::runtime_error("Invalid binding");
464 sqlite3* db_ = nullptr;
465 std::function<bool(int)> busy_handler_;
468 } // namespace tizen_base
470 #endif // DATABASE_HPP_