File: sqlite3.hpp

package info (click to toggle)
higan 098-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 11,904 kB
  • ctags: 13,286
  • sloc: cpp: 108,285; ansic: 778; makefile: 32; sh: 18
file content (203 lines) | stat: -rw-r--r-- 6,666 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#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 <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 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<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 natural() -> uint64_t { return natural(_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;
};

}}