#pragma once /* SQLite3 C++ RAII wrapper for nall * * Note on code below: it is safe (no-op) to call sqlite3_* functions on null sqlite3 objects */ #include #include #include namespace nall::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 natural(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 { vector 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 natural() -> uint64_t { return natural(_output++); } auto real() -> double { return real(_output++); } auto text() -> string { return text(_output++); } auto data() -> vector { 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& 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& 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 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)...); 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 auto bind(Query& query, const T& value, P&&... p) -> void { query.bind(value); bind(query, forward

(p)...); } bool _debug = false; sqlite3* _database = nullptr; }; }