bsnes/nall/database/sqlite3.hpp

207 lines
6.6 KiB
C++

#ifndef NALL_DATABASE_SQLITE3_HPP
#define NALL_DATABASE_SQLITE3_HPP
/* SQLite3 C++ RAII wrapper for nall
*
* Note on code below: it is safe (no-op) to call sqlite3_* functions on null sqlite3 objects
*/
#include <sqlite3.h>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall { namespace Database {
struct SQLite3 {
struct Statement {
Statement(const Statement& source) = delete;
auto operator=(const Statement& source) -> Statement& = delete;
Statement(sqlite3_stmt* statement) : _statement(statement) {}
Statement(Statement&& source) { operator=(move(source)); }
auto operator=(Statement&& source) -> Statement& {
_statement = source._statement;
_response = source._response;
_output = source._output;
source._statement = nullptr;
source._response = SQLITE_OK;
source._output = 0;
return *this;
}
explicit operator bool() {
return sqlite3_data_count(statement());
}
auto columns() -> unsigned {
return sqlite3_column_count(statement());
}
auto integer(unsigned column) -> int64_t {
return sqlite3_column_int64(statement(), column);
}
auto decimal(unsigned column) -> uint64_t {
return sqlite3_column_int64(statement(), column);
}
auto real(unsigned column) -> double {
return sqlite3_column_double(statement(), column);
}
auto text(unsigned column) -> string {
string result;
if(auto text = sqlite3_column_text(statement(), column)) {
result.resize(sqlite3_column_bytes(statement(), column));
memory::copy(result.get(), text, result.size());
}
return result;
}
auto data(unsigned column) -> vector<uint8_t> {
vector<uint8_t> result;
if(auto data = sqlite3_column_blob(statement(), column)) {
result.resize(sqlite3_column_bytes(statement(), column));
memory::copy(result.data(), data, result.size());
}
return result;
}
auto integer() -> int64_t { return integer(_output++); }
auto decimal() -> uint64_t { return decimal(_output++); }
auto real() -> double { return real(_output++); }
auto text() -> string { return text(_output++); }
auto data() -> vector<uint8_t> { return data(_output++); }
protected:
virtual auto statement() -> sqlite3_stmt* { return _statement; }
sqlite3_stmt* _statement = nullptr;
signed _response = SQLITE_OK;
unsigned _output = 0;
};
struct Query : Statement {
Query(const Query& source) = delete;
auto operator=(const Query& source) -> Query& = delete;
Query(sqlite3_stmt* statement) : Statement(statement) {}
Query(Query&& source) : Statement(source._statement) { operator=(move(source)); }
~Query() {
sqlite3_finalize(statement());
_statement = nullptr;
}
auto operator=(Query&& source) -> Query& {
_statement = source._statement;
_input = source._input;
source._statement = nullptr;
source._input = 0;
return *this;
}
auto& bind(unsigned column, nullptr_t) { sqlite3_bind_null(_statement, 1 + column); return *this; }
auto& bind(unsigned column, int32_t value) { sqlite3_bind_int(_statement, 1 + column, value); return *this; }
auto& bind(unsigned column, uint32_t value) { sqlite3_bind_int(_statement, 1 + column, value); return *this; }
auto& bind(unsigned column, int64_t value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
auto& bind(unsigned column, uint64_t value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
auto& bind(unsigned column, double value) { sqlite3_bind_double(_statement, 1 + column, value); return *this; }
auto& bind(unsigned column, const string& value) { sqlite3_bind_text(_statement, 1 + column, value.data(), value.size(), SQLITE_TRANSIENT); return *this; }
auto& bind(unsigned column, const vector<uint8_t>& value) { sqlite3_bind_blob(_statement, 1 + column, value.data(), value.size(), SQLITE_TRANSIENT); return *this; }
auto& bind(nullptr_t) { return bind(_input++, nullptr); }
auto& bind(int32_t value) { return bind(_input++, value); }
auto& bind(uint32_t value) { return bind(_input++, value); }
auto& bind(int64_t value) { return bind(_input++, value); }
auto& bind(uint64_t value) { return bind(_input++, value); }
auto& bind(double value) { return bind(_input++, value); }
auto& bind(const string& value) { return bind(_input++, value); }
auto& bind(const vector<uint8_t>& value) { return bind(_input++, value); }
auto step() -> bool {
_stepped = true;
return sqlite3_step(_statement) == SQLITE_ROW;
}
struct Iterator {
Iterator(Query& query, bool finished) : query(query), finished(finished) {}
auto operator*() -> Statement { return query._statement; }
auto operator!=(const Iterator& source) const -> bool { return finished != source.finished; }
auto operator++() -> Iterator& { finished = !query.step(); return *this; }
protected:
Query& query;
bool finished = false;
};
auto begin() -> Iterator { return Iterator(*this, !step()); }
auto end() -> Iterator { return Iterator(*this, true); }
private:
auto statement() -> sqlite3_stmt* override {
if(!_stepped) step();
return _statement;
}
unsigned _input = 0;
bool _stepped = false;
};
SQLite3() = default;
SQLite3(const string& filename) { open(filename); }
~SQLite3() { close(); }
explicit operator bool() const { return _database; }
auto open(const string& filename) -> bool {
close();
sqlite3_open(filename, &_database);
return _database;
}
auto close() -> void {
sqlite3_close(_database);
_database = nullptr;
}
template<typename... P> auto execute(const string& statement, P&&... p) -> Query {
if(!_database) return {nullptr};
sqlite3_stmt* _statement = nullptr;
sqlite3_prepare_v2(_database, statement.data(), statement.size(), &_statement, nullptr);
if(!_statement) {
if(_debug) print("[sqlite3_prepare_v2] ", sqlite3_errmsg(_database), "\n");
return {nullptr};
}
Query query{_statement};
bind(query, forward<P>(p)...);
return query;
}
auto lastInsertID() const -> uint64_t {
return _database ? sqlite3_last_insert_rowid(_database) : 0;
}
auto setDebug(bool debug = true) -> void {
_debug = debug;
}
protected:
auto bind(Query&) -> void {}
template<typename T, typename... P> auto bind(Query& query, const T& value, P&&... p) -> void {
query.bind(value);
bind(query, forward<P>(p)...);
}
bool _debug = false;
sqlite3* _database = nullptr;
};
}}
#endif