bsnes/nall/database/sqlite3.hpp
Tim Allen 092cac9073 Update to v094r37 release.
byuu says:

Changelog:
- synchronizes lots of nall changes
- changes displayed program title from tomoko to higan(*)
- browser dialog sort is case-insensitive
- .sys folders look at user-selected library path; no longer hard-coded

Tried to get rid of the file modes from the Windows browser dialog, but
it was being a bitch so I left it on for now.

- The storage locations and binary still use tomoko. I'm not really sure
  what to do here. The idea is there may be more than one "higan" UI in
  the future, but I don't want people to go around calling the entire
  program by the UI name. For official Windows releases, I can rename
  the binaries to "higan-{profile}.exe", and by putting the config files
  with the binary, they won't ever see the tomoko folder. Linux is of
  course trickier.

Note: Windows users will need to edit hiro/components.hpp and comment
out these lines:

 #define Hiro_Console
 #define Hiro_IconView
 #define Hiro_SourceView
 #define Hiro_TreeView

I forgot to do that, and too lazy to upload another WIP.
2015-07-14 19:32:43 +10:00

198 lines
6.4 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;
}
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.pointer(), 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