Belle II Software development
sqlite.h
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8#pragma once
9
10#include <sqlite3.h>
11
12#include <optional>
13#include <string>
14#include <vector>
15#include <tuple>
16#include <stdexcept>
17
34namespace sqlite {
36 class SQLiteError: public std::runtime_error {
37 public:
39 explicit SQLiteError(int code, const std::string& prefix = ""): std::runtime_error(prefix + sqlite3_errstr(code)), m_code(code) {}
41 int code() const { return m_code; }
42 private:
43 int m_code;
44 };
45
46 namespace detail {
51 inline void checkSQLiteError(int code, const std::string& prefix = "")
52 {
53 if (code != SQLITE_OK) throw SQLiteError(code, prefix);
54 }
55
64 public:
66 explicit ColumnFiller(sqlite3_stmt* statement): m_stmt(statement) {}
68 void operator()(int index, int& col) { col = sqlite3_column_int(m_stmt, index); }
70 void operator()(int index, int64_t& col) { col = sqlite3_column_int(m_stmt, index); }
72 void operator()(int index, double& col) { col = sqlite3_column_double(m_stmt, index); }
74 void operator()(int index, std::string& col)
75 {
76 // ptr is owned by sqlite, no need to free but we need to copy
77 if (auto ptr = reinterpret_cast<const char*>(sqlite3_column_text(m_stmt, index)); ptr != nullptr) {
78 col = ptr;
79 }
80 }
82 void operator()(int index, std::vector<std::byte>& col)
83 {
84 // ptr is owned by sqlite, no need to free but we need to copy
85 if (auto ptr = reinterpret_cast<const std::byte*>(sqlite3_column_blob(m_stmt, index)); ptr != nullptr) {
86 size_t bytes = sqlite3_column_bytes(m_stmt, index);
87 col = std::vector<std::byte>(ptr, ptr + bytes);
88 }
89 }
91 template<class T>
92 void operator()(int index, std::optional<T>& col)
93 {
94 if (sqlite3_column_type(m_stmt, index) != SQLITE_NULL) {
95 col.emplace();
96 (*this)(index, *col);
97 }
98 }
99 private:
100 sqlite3_stmt* m_stmt;
101 };
102
111 public:
113 explicit ParameterBinder(sqlite3_stmt* statement): m_stmt(statement) {}
114
116 template<class T>
117 void operator()(int index, T&& param)
118 {
119 // For some reason bind parameters start at one ...
120 checkSQLiteError(bind(index + 1, std::forward<T>(param)));
121 }
122 private:
124 int bind(int index, int param) { return sqlite3_bind_int(m_stmt, index, param);}
126 int bind(int index, int64_t param) { return sqlite3_bind_int64(m_stmt, index, param); }
128 int bind(int index, double param) { return sqlite3_bind_double(m_stmt, index, param);}
130 int bind(int index, const std::string& param)
131 {
132 return sqlite3_bind_text(m_stmt, index, param.data(), param.size(), SQLITE_TRANSIENT);
133 }
135 int bind(int index, const std::vector<std::byte>& param)
136 {
137 return sqlite3_bind_blob(m_stmt, index, param.data(), param.size(), SQLITE_TRANSIENT);
138 }
140 template<class T>
141 int bind(int index, const std::optional<T>& param)
142 {
143 if (param.has_value()) return bind(*param);
144 return sqlite3_bind_null(m_stmt, index);
145 }
146 sqlite3_stmt* m_stmt;
147 };
148
150 template<class Tuple, class Func, std::size_t ...Is>
151 void visitTupleWithIndexImpl(Tuple&& t, Func&& f, std::index_sequence<Is...>)
152 {
153 // C++17 fold expression to just do all the calls in one separated by "," operator ...
154 (f(std::integral_constant<std::size_t, Is> {}, std::get<Is>(t)), ...);
155 }
156
163 template<class ... T, class Func>
164 void visitTupleWithIndex(std::tuple<T...>& t, Func&& f)
165 {
166 visitTupleWithIndexImpl(t, std::forward<Func>(f), std::make_index_sequence<sizeof...(T)> {});
167 }
168 }
169
193 template<class ObjectType, class ... Columns>
195 public:
197 using value_type = ObjectType;
198
200 class iterator {
201 public:
202
204 using iterator_category = std::input_iterator_tag;
205
207 using value_type = ObjectType;
208
210 using difference_type = size_t;
211
214
217
219 iterator() = default;
221 explicit iterator(ObjectStatement* instance): m_instance(instance), m_row{0}
222 {
223 (*this)++;
224 }
227 {
228 if (m_row < 0) return *this;
229 ++m_row;
230 if (!m_instance->step()) m_row = -1;
231 return *this;
232 }
234 iterator operator++(int) {iterator retval = *this; ++(*this); return retval;}
236 bool operator==(const iterator& other) const { return m_row == other.m_row; }
238 bool operator!=(const iterator& other) const { return m_row != other.m_row; }
240 value_type operator*() const { return m_instance->getRow(); }
241 private:
245 int64_t m_row{ -1};
246 };
247
249 iterator begin() { return iterator(this); }
251 iterator end() const { return iterator(); }
252
254 ObjectStatement(sqlite3* db, const std::string& query, bool persistent)
255 {
256 detail::checkSQLiteError(sqlite3_prepare_v3(db, query.data(), query.size(), persistent ? SQLITE_PREPARE_PERSISTENT : 0,
257 &m_statement, nullptr), "Cannot prepare database statement: ");
258 if (auto cols = sqlite3_column_count(m_statement); cols != sizeof...(Columns)) {
259 throw std::runtime_error("Number of column (" + std::to_string(cols) + ") doesn't match number of template parameters (" +
260 std::to_string(sizeof...(Columns)) + ')');
261 }
262 }
264 ~ObjectStatement() { if (m_statement) sqlite3_finalize(m_statement); }
265
267 template<class ... Parameters>
268 ObjectStatement& execute(Parameters... parameters)
269 {
270 if (auto params = sqlite3_bind_parameter_count(m_statement); params != sizeof...(Parameters)) {
271 throw std::runtime_error("Number of arguments (" + std::to_string(sizeof...(Parameters)) +
272 ") doesn't match number of statement parameters (" + std::to_string(params) + ")");
273 }
274 sqlite3_reset(m_statement);
275 auto parameter_tuple = std::make_tuple(parameters...);
276 detail::visitTupleWithIndex(parameter_tuple, detail::ParameterBinder{m_statement});
277 return *this;
278 }
279
283 bool step()
284 {
285 if (auto ret = sqlite3_step(m_statement); ret != SQLITE_ROW) {
286 if (ret == SQLITE_DONE) return false;
287 detail::checkSQLiteError(ret);
288 }
289 return true;
290 }
291
294 {
295 std::tuple<Columns...> result;
296 detail::visitTupleWithIndex(result, detail::ColumnFiller{m_statement});
297 return std::make_from_tuple<value_type>(std::move(result));
298 }
299 private:
301 sqlite3_stmt* m_statement{nullptr};
302 };
303
305 template<class ... Columns> using Statement = ObjectStatement<std::tuple<Columns...>, Columns...>;
307 template<class T> using SimpleStatement = ObjectStatement<T, T>;
308
311 public:
313 explicit Connection(const std::string& filename)
314 {
315 detail::checkSQLiteError(sqlite3_open_v2(filename.c_str(), &m_connection, SQLITE_OPEN_READONLY, nullptr));
316 }
318 ~Connection() { if (m_connection) sqlite3_close_v2(m_connection); }
320 template<class ... Columns>
321 Statement<Columns...> prepare(const std::string& query, bool persistent = false) { return Statement<Columns...>(m_connection, query, persistent); }
323 operator sqlite3* () const { return m_connection; }
324 private:
326 sqlite3* m_connection;
327 };
328}
Simple wrapper for a SQLite database connection.
Definition: sqlite.h:310
Connection(const std::string &filename)
Create from filename.
Definition: sqlite.h:313
~Connection()
And clean up.
Definition: sqlite.h:318
sqlite3 * m_connection
Pointer to the sqlite database object.
Definition: sqlite.h:326
Statement< Columns... > prepare(const std::string &query, bool persistent=false)
Return a prepared statement for execution.
Definition: sqlite.h:321
Iterator class to allow iterating over the rows.
Definition: sqlite.h:200
bool operator!=(const iterator &other) const
as well as unequality check
Definition: sqlite.h:238
iterator(ObjectStatement *instance)
and a real constructor to a statement
Definition: sqlite.h:221
iterator()=default
with a default constructor
value_type * pointer
Pointer.
Definition: sqlite.h:213
iterator operator++(int)
also postfix increment
Definition: sqlite.h:234
bool operator==(const iterator &other) const
and equality check
Definition: sqlite.h:236
value_type & reference
Reference.
Definition: sqlite.h:216
size_t difference_type
Difference type.
Definition: sqlite.h:210
ObjectStatement * m_instance
pointer to the statement
Definition: sqlite.h:243
iterator & operator++()
increment to next row, stop in case we are done
Definition: sqlite.h:226
std::input_iterator_tag iterator_category
Iterator category.
Definition: sqlite.h:204
value_type operator*() const
and a dereference operator
Definition: sqlite.h:240
ObjectType value_type
Value type.
Definition: sqlite.h:207
int64_t m_row
current row index
Definition: sqlite.h:245
SQLite prepared statement wrapper.
Definition: sqlite.h:194
bool step()
Step to the next row in the result.
Definition: sqlite.h:283
value_type getRow() const
Return the current row.
Definition: sqlite.h:293
iterator end() const
Iterator to the end.
Definition: sqlite.h:251
sqlite3_stmt * m_statement
pointer to the statement object
Definition: sqlite.h:301
ObjectStatement(sqlite3 *db, const std::string &query, bool persistent)
Create a statement for an existing database object.
Definition: sqlite.h:254
~ObjectStatement()
Clean up the statement.
Definition: sqlite.h:264
ObjectStatement & execute(Parameters... parameters)
Execute the statement, providing all necessary parameters.
Definition: sqlite.h:268
iterator begin()
Iterator to the beginning.
Definition: sqlite.h:249
ObjectType value_type
each row gets converted to a instance of this type
Definition: sqlite.h:197
Simple error class to be thrown if there is any sqlite error on any of the operations.
Definition: sqlite.h:36
int m_code
SQLite error code.
Definition: sqlite.h:43
int code() const
Return the sqlite error code.
Definition: sqlite.h:41
SQLiteError(int code, const std::string &prefix="")
Construct an instance from a prefix string and an sqlite error code.
Definition: sqlite.h:39
Struct to fill the different columns in a sqlite result row into a std::tuple.
Definition: sqlite.h:63
ColumnFiller(sqlite3_stmt *statement)
Create a new instance for the given statement.
Definition: sqlite.h:66
sqlite3_stmt * m_stmt
statement on which to work on
Definition: sqlite.h:100
void operator()(int index, int64_t &col)
Fill 64bit integer column.
Definition: sqlite.h:70
void operator()(int index, double &col)
Fill double column.
Definition: sqlite.h:72
void operator()(int index, std::string &col)
Fill string column.
Definition: sqlite.h:74
void operator()(int index, std::optional< T > &col)
Fill an possibly null column in an optional.
Definition: sqlite.h:92
void operator()(int index, int &col)
Fill integer column.
Definition: sqlite.h:68
void operator()(int index, std::vector< std::byte > &col)
Fill blob column.
Definition: sqlite.h:82
Bind the given parameter to the sqlite statement.
Definition: sqlite.h:110
int bind(int index, const std::vector< std::byte > &param)
bind a bytestream
Definition: sqlite.h:135
ParameterBinder(sqlite3_stmt *statement)
Create a new object for the given statement.
Definition: sqlite.h:113
int bind(int index, const std::optional< T > &param)
bind an optional: if it doesn't have a value we bind NULL
Definition: sqlite.h:141
void operator()(int index, T &&param)
Bind the parameter with the given index to the statement.
Definition: sqlite.h:117
sqlite3_stmt * m_stmt
statement on which to work on
Definition: sqlite.h:146
int bind(int index, double param)
bind a double parameter
Definition: sqlite.h:128
int bind(int index, int param)
bind an integer parameter
Definition: sqlite.h:124
int bind(int index, const std::string &param)
bind a string parameter
Definition: sqlite.h:130
int bind(int index, int64_t param)
bind a 64bit integer parameter
Definition: sqlite.h:126
C++ wrapper around the sqlite C Api for convenient use of SQLite statements in C++.
Definition: sqlite.h:34
STL namespace.