120 lines
3.2 KiB
C
120 lines
3.2 KiB
C
|
#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);
|
||
|
}
|
||
|
}
|