mf/sqlite_wrapper.h

120 lines
3.2 KiB
C
Raw Normal View History

2024-01-02 12:36:58 +00:00
#pragma once
#include <string>
#include <exception>
#include <sqlite3.h>
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<typename F>
constexpr void exec(Sqlite3Statement& stmt, F callback, size_t iterations = ~static_cast<size_t>(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<int>(text.size()), lifetime);
}
constexpr void bind_null(int column);
const char* column_text(int column) const noexcept {
return reinterpret_cast<const char*>(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<typename F>
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);
}
}