#pragma once #include #include #include class Sqlite3Statement; class Sqlite3 { public: Sqlite3(const Sqlite3&) = delete; Sqlite3& operator=(const Sqlite3&) = delete; Sqlite3(const char* filename, bool readonly, bool create); ~Sqlite3() { sqlite3_close_v2(this->_db); } // https://stackoverflow.com/a/6458689 template constexpr void exec(Sqlite3Statement& stmt, F callback, size_t iterations = ~static_cast(0)); constexpr void exec(Sqlite3Statement& stmt) { this->exec(stmt, []() {}); } constexpr int64_t changes() const noexcept { return sqlite3_changes64(this->_db); } constexpr sqlite3* get() const noexcept { return this->_db; } private: sqlite3* _db; }; class Sqlite3Statement { public: Sqlite3Statement(const Sqlite3Statement&) = delete; Sqlite3Statement& operator=(const Sqlite3Statement&) = delete; Sqlite3Statement(Sqlite3& db, const char* sql); ~Sqlite3Statement() { sqlite3_finalize(this->_stmt); } constexpr void reset() noexcept { sqlite3_reset(this->_stmt); } constexpr void bind_text(int column, const char* text, int bytes, void (*lifetime)(void*)); constexpr void bind_text(int column, const char* text, void (*lifetime)(void*)) { this->bind_text(column, text, -1, lifetime); } constexpr void bind_text(int column, const std::string& text, void (*lifetime)(void*)) { this->bind_text(column, text.data(), static_cast(text.size()), lifetime); } constexpr void bind_null(int column); const char* column_text(int column) const noexcept { return reinterpret_cast(sqlite3_column_text(this->_stmt, column)); } constexpr int64_t column_int64(int column) const noexcept { return sqlite3_column_int64(this->_stmt, column); } friend Sqlite3; private: sqlite3_stmt* _stmt; }; class Sqlite3Exception : public std::exception { public: Sqlite3Exception(int error_code, const Sqlite3* db) { this->_msg = db && db->get() ? sqlite3_errmsg(db->get()) : sqlite3_errstr(error_code); } const char* what() const noexcept { return this->_msg.c_str(); } private: std::string _msg; }; template constexpr void Sqlite3::exec(Sqlite3Statement& stmt, F callback, size_t iterations) { while (iterations--) { int error_code = sqlite3_step(stmt._stmt); if (error_code == SQLITE_DONE) { break; } else if (error_code == SQLITE_ROW) { callback(); } else if (error_code != SQLITE_OK) { throw Sqlite3Exception(error_code, this); } } } constexpr void Sqlite3Statement::bind_text(int column, const char* text, int bytes, void (*lifetime)(void*)) { int error_code = sqlite3_bind_text(this->_stmt, column, text, bytes, lifetime); if (error_code != SQLITE_OK) { throw Sqlite3Exception(error_code, nullptr); } } constexpr void Sqlite3Statement::bind_null(int column) { int error_code = sqlite3_bind_null(this->_stmt, column); if (error_code != SQLITE_OK) { throw Sqlite3Exception(error_code, nullptr); } }