Belle II Software light-2406-ragdoll
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: public std::iterator<std::input_iterator_tag, value_type, size_t> {
201 public:
203 iterator() = default;
205 explicit iterator(ObjectStatement* instance): m_instance(instance), m_row{0}
206 {
207 (*this)++;
208 }
211 {
212 if (m_row < 0) return *this;
213 ++m_row;
214 if (!m_instance->step()) m_row = -1;
215 return *this;
216 }
218 iterator operator++(int) {iterator retval = *this; ++(*this); return retval;}
220 bool operator==(const iterator& other) const { return m_row == other.m_row; }
222 bool operator!=(const iterator& other) const { return m_row != other.m_row; }
224 value_type operator*() const { return m_instance->getRow(); }
225 private:
229 int64_t m_row{ -1};
230 };
231
233 iterator begin() { return iterator(this); }
235 iterator end() const { return iterator(); }
236
238 ObjectStatement(sqlite3* db, const std::string& query, bool persistent)
239 {
240 detail::checkSQLiteError(sqlite3_prepare_v3(db, query.data(), query.size(), persistent ? SQLITE_PREPARE_PERSISTENT : 0,
241 &m_statement, nullptr), "Cannot prepare database statement: ");
242 if (auto cols = sqlite3_column_count(m_statement); cols != sizeof...(Columns)) {
243 throw std::runtime_error("Number of column (" + std::to_string(cols) + ") doesn't match number of template parameters (" +
244 std::to_string(sizeof...(Columns)) + ')');
245 }
246 }
248 ~ObjectStatement() { if (m_statement) sqlite3_finalize(m_statement); }
249
251 template<class ... Parameters>
252 ObjectStatement& execute(Parameters... parameters)
253 {
254 if (auto params = sqlite3_bind_parameter_count(m_statement); params != sizeof...(Parameters)) {
255 throw std::runtime_error("Number of arguments (" + std::to_string(sizeof...(Parameters)) +
256 ") doesn't match number of statement parameters (" + std::to_string(params) + ")");
257 }
258 sqlite3_reset(m_statement);
259 auto parameter_tuple = std::make_tuple(parameters...);
260 detail::visitTupleWithIndex(parameter_tuple, detail::ParameterBinder{m_statement});
261 return *this;
262 }
263
267 bool step()
268 {
269 if (auto ret = sqlite3_step(m_statement); ret != SQLITE_ROW) {
270 if (ret == SQLITE_DONE) return false;
271 detail::checkSQLiteError(ret);
272 }
273 return true;
274 }
275
278 {
279 std::tuple<Columns...> result;
280 detail::visitTupleWithIndex(result, detail::ColumnFiller{m_statement});
281 return std::make_from_tuple<value_type>(std::move(result));
282 }
283 private:
285 sqlite3_stmt* m_statement{nullptr};
286 };
287
289 template<class ... Columns> using Statement = ObjectStatement<std::tuple<Columns...>, Columns...>;
291 template<class T> using SimpleStatement = ObjectStatement<T, T>;
292
295 public:
297 explicit Connection(const std::string& filename)
298 {
299 detail::checkSQLiteError(sqlite3_open_v2(filename.c_str(), &m_connection, SQLITE_OPEN_READONLY, nullptr));
300 }
302 ~Connection() { if (m_connection) sqlite3_close_v2(m_connection); }
304 template<class ... Columns>
305 Statement<Columns...> prepare(const std::string& query, bool persistent = false) { return Statement<Columns...>(m_connection, query, persistent); }
307 operator sqlite3* () const { return m_connection; }
308 private:
310 sqlite3* m_connection;
311 };
312}
Simple wrapper for a SQLite database connection.
Definition: sqlite.h:294
Connection(const std::string &filename)
Create from filename.
Definition: sqlite.h:297
~Connection()
And clean up.
Definition: sqlite.h:302
sqlite3 * m_connection
Pointer to the sqlite database object.
Definition: sqlite.h:310
Statement< Columns... > prepare(const std::string &query, bool persistent=false)
Return a prepared statement for execution.
Definition: sqlite.h:305
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:222
iterator(ObjectStatement *instance)
and a real constructor to a statement
Definition: sqlite.h:205
iterator()=default
with a default constructor
iterator operator++(int)
also postfix increment
Definition: sqlite.h:218
bool operator==(const iterator &other) const
and equality check
Definition: sqlite.h:220
ObjectStatement * m_instance
pointer to the statement
Definition: sqlite.h:227
iterator & operator++()
increment to next row, stop in case we are done
Definition: sqlite.h:210
value_type operator*() const
and a dereference operator
Definition: sqlite.h:224
int64_t m_row
current row index
Definition: sqlite.h:229
SQLite prepared statement wrapper.
Definition: sqlite.h:194
bool step()
Step to the next row in the result.
Definition: sqlite.h:267
value_type getRow() const
Return the current row.
Definition: sqlite.h:277
iterator end() const
Iterator to the end.
Definition: sqlite.h:235
sqlite3_stmt * m_statement
pointer to the statement object
Definition: sqlite.h:285
ObjectStatement(sqlite3 *db, const std::string &query, bool persistent)
Create a statement for an existing database object.
Definition: sqlite.h:238
~ObjectStatement()
Clean up the statement.
Definition: sqlite.h:248
ObjectStatement & execute(Parameters... parameters)
Execute the statement, providing all necessary parameters.
Definition: sqlite.h:252
iterator begin()
Iterator to the beginning.
Definition: sqlite.h:233
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.