summaryrefslogtreecommitdiffstats
path: root/sqlstmt.h
blob: 25dd8334d69f428ee6b469241a330c9b57e45728 (plain)
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
// -*- C++ -*-

#ifndef _SQLSTMT_H_
#define _SQLSTMT_H_ 1

#include <cassert>
#include <string>
#include <tuple>
#include <stdexcept>
#include <sqlite3.h>

using i64 = sqlite3_int64;

struct sqlerr_t : public std::runtime_error {
  sqlerr_t (const std::string &msg) : std::runtime_error (msg) {}
};
/* A sqldone_t is thrown if you ask for data when no rows are left */
struct sqldone_t : public std::runtime_error {
  sqldone_t (const std::string &msg) : std::runtime_error (msg) {}
};

class sqlstmt_t {
  sqlite3_stmt *stmt_;
  int status_ = SQLITE_OK;
  sqlstmt_t &set_status (int status);
  void fail ();
  void ensure_row () { if (status_ != SQLITE_ROW) fail(); }

 public:
  explicit sqlstmt_t(sqlite3_stmt *stmt) : stmt_(stmt) {}
  explicit sqlstmt_t(sqlite3 *db, const char *fmt, ...);
  sqlstmt_t(const sqlstmt_t &r);
  sqlstmt_t(sqlstmt_t &&r) : stmt_ (r.stmt_) { r.stmt_ = nullptr; }
  ~sqlstmt_t() { sqlite3_finalize (stmt_); }

  sqlite3_stmt *get() { return stmt_; }
  sqlite3 *getdb() { return sqlite3_db_handle(stmt_); }
  int status() const { return status_; }
  bool row() {
    if (status_ == SQLITE_ROW)
      return true;
    // Something like SQLITE_OK indicates row() not used after step()
    assert (status_ == SQLITE_DONE);
    return false;
  }
  bool done() { return !row(); }
  sqlstmt_t &step() { return set_status(sqlite3_step (stmt_)); }
  sqlstmt_t &reset() { return set_status(sqlite3_reset (stmt_)); }

  /* Access columns */
  template<typename T> T column(int);
  bool null(int i) {
    ensure_row();
    return sqlite3_column_type (stmt_, i) == SQLITE_NULL;
  }
  sqlite3_int64 integer(int i) {
    ensure_row();
    return sqlite3_column_int64 (stmt_, i);
  }
  double real(int i) {
    ensure_row();
    return sqlite3_column_double (stmt_, i);
  }
  std::string str(int i) {
    ensure_row();
    return { static_cast<const char *> (sqlite3_column_blob (stmt_, i)),
	size_t (sqlite3_column_bytes (stmt_, i)) };
  }
  const char *c_str(int i) {
    ensure_row();
    return reinterpret_cast<const char *> (sqlite3_column_text (stmt_, i));
  }
  sqlite3_value *value(int i) {
    ensure_row();
    return sqlite3_column_value(stmt_, i);
  }

  /* Bind parameters */
  sqlstmt_t &bind_null(int i) {
    return set_status (sqlite3_bind_null(stmt_, i));
  }
  sqlstmt_t &bind_int(int i, sqlite3_int64 v) {
    return set_status (sqlite3_bind_int64(stmt_, i, v));
  }
  sqlstmt_t &bind_real(int i, double v) {
    return set_status (sqlite3_bind_double(stmt_, i, v));
  }
  sqlstmt_t &bind_text(int i, const std::string &v) {
    return set_status (sqlite3_bind_text(stmt_, i, v.data(), v.size(),
					 SQLITE_STATIC));
  }
  sqlstmt_t &bind_text(int i, std::string &&v) {
    return set_status (sqlite3_bind_text(stmt_, i, v.data(), v.size(),
					 SQLITE_TRANSIENT));
  }
  sqlstmt_t &bind_text(int i, const char *p, int len = -1) {
    return set_status (sqlite3_bind_text(stmt_, i, p, len, SQLITE_STATIC));
  }
  sqlstmt_t &bind_blob(int i, const void *p, int len) {
    return set_status (sqlite3_bind_blob(stmt_, i, p, len, SQLITE_STATIC));
  }
  sqlstmt_t &bind_value(int i, const sqlite3_value *v) {
    return set_status (sqlite3_bind_value (stmt_, i, v));
  }

  /* Overloaded bind */
  sqlstmt_t &bind(int i, std::nullptr_t) { return bind_null(i); }
  sqlstmt_t &bind(int i, sqlite3_int64 v) { return bind_int(i, v); }
  sqlstmt_t &bind(int i, int v) { return bind_int(i, v); }
  sqlstmt_t &bind(int i, unsigned v) { return bind_int(i, v); }
  sqlstmt_t &bind(int i, const double &v) { return bind_real(i, v); }
  sqlstmt_t &bind(int i, const std::string &v) { return bind_text(i, v); }
  sqlstmt_t &bind(int i, std::string &&v) { return bind_text(i, std::move(v)); }
  sqlstmt_t &bind(int i, const char *v) { return bind_text(i, v); }
  sqlstmt_t &bind(int i, const sqlite3_value *v) { return bind_value(i, v); }

  /* Bind multiple parameters at once */
  sqlstmt_t &_param(int) { return *this; }
  template<typename H, typename... T>
  sqlstmt_t &_param(int i, H&& h, T&&... t) {
    return this->bind(i, std::forward<H>(h))._param(i+1, std::forward<T>(t)...);
  }
  template<typename... Args> sqlstmt_t &param(Args&&... args) {
    return _param (1, std::forward<Args> (args)...);
  }

  /* Bind tuple */
  template<size_t N> struct _tparm_helper {
    template<typename... Args>
    static sqlstmt_t &go(sqlstmt_t &s, const std::tuple<Args...> &t) {
      return _tparm_helper<N-1>::go(s.bind(N, std::get<N-1>(t)), t);
    }
  };
  template<typename... Args>
  sqlstmt_t &tparam(const std::tuple<Args...> &t) {
    return _tparm_helper<sizeof...(Args)>::go(*this, t);
  }
};

template<> struct sqlstmt_t::_tparm_helper<0> {
  template<typename... Args>
  static sqlstmt_t &go(sqlstmt_t &s, const std::tuple<Args...> &t) { return s; }
};

template<> inline bool
sqlstmt_t::column(int i)
{
  return null(i);
}
template<> inline i64
sqlstmt_t::column(int i)
{
  return integer(i);
}
template<> inline double
sqlstmt_t::column(int i)
{
  return real(i);
}
template<> inline std::string
sqlstmt_t::column(int i)
{
  return str(i);
}
template<> inline const char *
sqlstmt_t::column(int i)
{
  return c_str(i);
}

void sqlexec (sqlite3 *db, const char *fmt, ...);

#endif /* !_SQLSTMT_H_ */