Belle II Software  release-05-02-19
sqlite.h
1 /**************************************************************************
2  * BASF2 (Belle Analysis Framework 2) *
3  * Copyright(C) 2019 - Belle II Collaboration *
4  * *
5  * Author: The Belle II Collaboration *
6  * Contributors: Martin Ritter *
7  * *
8  * This software is provided "as is" without any warranty. *
9  **************************************************************************/
10 #pragma once
11 
12 #include <sqlite3.h>
13 
14 #include <optional>
15 #include <string>
16 #include <vector>
17 #include <tuple>
18 
35 namespace sqlite {
37  class SQLiteError: public std::runtime_error {
38  public:
40  explicit SQLiteError(int code, const std::string& prefix = ""): std::runtime_error(prefix + sqlite3_errstr(code)), m_code(code) {}
42  int code() const { return m_code; }
43  private:
44  int m_code;
45  };
46 
47  namespace detail {
52  inline void checkSQLiteError(int code, const std::string& prefix = "")
53  {
54  if (code != SQLITE_OK) throw SQLiteError(code, prefix);
55  }
56 
64  class ColumnFiller {
65  public:
67  explicit ColumnFiller(sqlite3_stmt* statement): m_stmt(statement) {}
69  void operator()(int index, int& col) { col = sqlite3_column_int(m_stmt, index); }
71  void operator()(int index, int64_t& col) { col = sqlite3_column_int(m_stmt, index); }
73  void operator()(int index, double& col) { col = sqlite3_column_double(m_stmt, index); }
75  void operator()(int index, std::string& col)
76  {
77  // ptr is owned by sqlite, no need to free but we need to copy
78  if (auto ptr = reinterpret_cast<const char*>(sqlite3_column_text(m_stmt, index)); ptr != nullptr) {
79  col = ptr;
80  }
81  }
83  void operator()(int index, std::vector<std::byte>& col)
84  {
85  // ptr is owned by sqlite, no need to free but we need to copy
86  if (auto ptr = reinterpret_cast<const std::byte*>(sqlite3_column_blob(m_stmt, index)); ptr != nullptr) {
87  size_t bytes = sqlite3_column_bytes(m_stmt, index);
88  col = std::vector<std::byte>(ptr, ptr + bytes);
89  }
90  }
92  template<class T>
93  void operator()(int index, std::optional<T>& col)
94  {
95  if (sqlite3_column_type(m_stmt, index) != SQLITE_NULL) {
96  col.emplace();
97  (*this)(index, *col);
98  }
99  }
100  private:
101  sqlite3_stmt* m_stmt;
102  };
103 
111  class ParameterBinder {
112  public:
114  explicit ParameterBinder(sqlite3_stmt* statement): m_stmt(statement) {}
115 
117  template<class T>
118  void operator()(int index, T&& param)
119  {
120  // For some reason bind parameters start at one ...
121  checkSQLiteError(bind(index + 1, std::forward<T>(param)));
122  }
123  private:
125  int bind(int index, int param) { return sqlite3_bind_int(m_stmt, index, param);}
127  int bind(int index, int64_t param) { return sqlite3_bind_int64(m_stmt, index, param); }
129  int bind(int index, double param) { return sqlite3_bind_double(m_stmt, index, param);}
131  int bind(int index, const std::string& param)
132  {
133  return sqlite3_bind_text(m_stmt, index, param.data(), param.size(), SQLITE_TRANSIENT);
134  }
136  int bind(int index, const std::vector<std::byte>& param)
137  {
138  return sqlite3_bind_blob(m_stmt, index, param.data(), param.size(), SQLITE_TRANSIENT);
139  }
141  template<class T>
142  int bind(int index, const std::optional<T>& param)
143  {
144  if (param.has_value()) return bind(*param);
145  return sqlite3_bind_null(m_stmt, index);
146  }
147  sqlite3_stmt* m_stmt;
148  };
149 
151  template<class Tuple, class Func, std::size_t ...Is>
152  void visitTupleWithIndexImpl(Tuple&& t, Func&& f, std::index_sequence<Is...>)
153  {
154  // C++17 fold expression to just do all the calls in one separated by "," operator ...
155  (f(std::integral_constant<std::size_t, Is> {}, std::get<Is>(t)), ...);
156  }
157 
164  template<class ... T, class Func>
165  void visitTupleWithIndex(std::tuple<T...>& t, Func&& f)
166  {
167  visitTupleWithIndexImpl(t, std::forward<Func>(f), std::make_index_sequence<sizeof...(T)> {});
168  }
169  }
170 
194  template<class ObjectType, class ... Columns>
195  class ObjectStatement {
196  public:
198  using value_type = ObjectType;
199 
201  class iterator: public std::iterator<std::input_iterator_tag, value_type, size_t> {
202  public:
204  iterator() = default;
206  explicit iterator(ObjectStatement* instance): m_instance(instance), m_row{0}
207  {
208  (*this)++;
209  }
212  {
213  if (m_row < 0) return *this;
214  ++m_row;
215  if (!m_instance->step()) m_row = -1;
216  return *this;
217  }
219  iterator operator++(int) {iterator retval = *this; ++(*this); return retval;}
221  bool operator==(const iterator& other) const { return m_row == other.m_row; }
223  bool operator!=(const iterator& other) const { return m_row != other.m_row; }
225  value_type operator*() const { return m_instance->getRow(); }
226  private:
228  ObjectStatement* m_instance{nullptr};
230  int64_t m_row{ -1};
231  };
232 
234  iterator begin() { return iterator(this); }
236  iterator end() const { return iterator(); }
237 
239  ObjectStatement(sqlite3* db, const std::string& query, bool persistent)
240  {
241  detail::checkSQLiteError(sqlite3_prepare_v3(db, query.data(), query.size(), persistent ? SQLITE_PREPARE_PERSISTENT : 0,
242  &m_statement, nullptr), "Cannot prepare database statement: ");
243  if (auto cols = sqlite3_column_count(m_statement); cols != sizeof...(Columns)) {
244  throw std::runtime_error("Number of column (" + std::to_string(cols) + ") doesn't match number of template parameters (" +
245  std::to_string(sizeof...(Columns)) + ')');
246  }
247  }
249  ~ObjectStatement() { if (m_statement) sqlite3_finalize(m_statement); }
250 
252  template<class ... Parameters>
253  ObjectStatement& execute(Parameters... parameters)
254  {
255  if (auto params = sqlite3_bind_parameter_count(m_statement); params != sizeof...(Parameters)) {
256  throw std::runtime_error("Number of arguments (" + std::to_string(sizeof...(Parameters)) +
257  ") doesn't match number of statement parameters (" + std::to_string(params) + ")");
258  }
259  sqlite3_reset(m_statement);
260  auto parameter_tuple = std::make_tuple(parameters...);
261  detail::visitTupleWithIndex(parameter_tuple, detail::ParameterBinder{m_statement});
262  return *this;
263  }
264 
268  bool step()
269  {
270  if (auto ret = sqlite3_step(m_statement); ret != SQLITE_ROW) {
271  if (ret == SQLITE_DONE) return false;
272  detail::checkSQLiteError(ret);
273  }
274  return true;
275  }
276 
278  value_type getRow() const
279  {
280  std::tuple<Columns...> result;
281  detail::visitTupleWithIndex(result, detail::ColumnFiller{m_statement});
282  return std::make_from_tuple<value_type>(std::move(result));
283  }
284  private:
286  sqlite3_stmt* m_statement{nullptr};
287  };
288 
290  template<class ... Columns> using Statement = ObjectStatement<std::tuple<Columns...>, Columns...>;
292  template<class T> using SimpleStatement = ObjectStatement<T, T>;
293 
295  class Connection {
296  public:
298  explicit Connection(const std::string& filename)
299  {
300  detail::checkSQLiteError(sqlite3_open_v2(filename.c_str(), &m_connection, SQLITE_OPEN_READONLY, nullptr));
301  }
303  ~Connection() { if (m_connection) sqlite3_close_v2(m_connection); }
305  template<class ... Columns>
306  Statement<Columns...> prepare(const std::string& query, bool persistent = false) { return Statement<Columns...>(m_connection, query, persistent); }
308  operator sqlite3* () const { return m_connection; }
309  private:
311  sqlite3* m_connection;
312  };
313 }
sqlite::ObjectStatement::value_type
ObjectType value_type
each row gets converted to a instance of this type
Definition: sqlite.h:206
sqlite::ObjectStatement::execute
ObjectStatement & execute(Parameters... parameters)
Execute the statement, providing all necessary parameters.
Definition: sqlite.h:261
sqlite::ObjectStatement::getRow
value_type getRow() const
Return the current row.
Definition: sqlite.h:286
sqlite::ObjectStatement::iterator::operator==
bool operator==(const iterator &other) const
and equality check
Definition: sqlite.h:229
sqlite::ObjectStatement::iterator::iterator
iterator()=default
with a default constructor
sqlite::ObjectStatement::end
iterator end() const
Iterator to the end.
Definition: sqlite.h:244
sqlite::detail::ParameterBinder::operator()
void operator()(int index, T &&param)
Bind the parameter with the given index to the statement.
Definition: sqlite.h:126
sqlite::detail::ParameterBinder::bind
int bind(int index, const std::vector< std::byte > &param)
bind a bytestream
Definition: sqlite.h:144
sqlite::ObjectStatement::iterator::m_row
int64_t m_row
current row index
Definition: sqlite.h:238
sqlite::ObjectStatement::iterator::operator++
iterator & operator++()
increment to next row, stop in case we are done
Definition: sqlite.h:219
sqlite::detail::ColumnFiller
Struct to fill the different columns in a sqlite result row into a std::tuple.
Definition: sqlite.h:72
sqlite::ObjectStatement
SQLite prepared statement wrapper.
Definition: sqlite.h:203
sqlite::ObjectStatement::iterator::operator!=
bool operator!=(const iterator &other) const
as well as unequality check
Definition: sqlite.h:231
sqlite::ObjectStatement::iterator
Iterator class to allow iterating over the rows.
Definition: sqlite.h:209
sqlite::ObjectStatement::m_statement
sqlite3_stmt * m_statement
pointer to the statement object
Definition: sqlite.h:294
sqlite::detail::ColumnFiller::m_stmt
sqlite3_stmt * m_stmt
statement on which to work on
Definition: sqlite.h:109
sqlite::detail::ParameterBinder::bind
int bind(int index, int param)
bind an integer parameter
Definition: sqlite.h:133
sqlite::Connection::prepare
Statement< Columns... > prepare(const std::string &query, bool persistent=false)
Return a prepared statement for execution.
Definition: sqlite.h:314
sqlite::detail::ColumnFiller::operator()
void operator()(int index, std::string &col)
Fill string column.
Definition: sqlite.h:83
sqlite::Statement
ObjectStatement< std::tuple< Columns... >, Columns... > Statement
Basic Statement returning a tuple of columns.
Definition: sqlite.h:298
sqlite::SQLiteError
Simple error class to be thrown if there is any sqlite error on any of the operations.
Definition: sqlite.h:45
sqlite::SQLiteError::SQLiteError
SQLiteError(int code, const std::string &prefix="")
Construct an instance from a prefix string and an sqlite error code.
Definition: sqlite.h:56
Belle2::Conditions::PayloadMetadata
Simple struct to group all information necessary for a single payload.
Definition: PayloadMetadata.h:25
sqlite::Connection::Connection
Connection(const std::string &filename)
Create from filename.
Definition: sqlite.h:306
sqlite::detail::ParameterBinder::m_stmt
sqlite3_stmt * m_stmt
statement on which to work on
Definition: sqlite.h:155
sqlite::ObjectStatement::step
bool step()
Step to the next row in the result.
Definition: sqlite.h:276
sqlite::detail::ColumnFiller::operator()
void operator()(int index, int &col)
Fill integer column.
Definition: sqlite.h:77
sqlite::SimpleStatement
ObjectStatement< T, T > SimpleStatement
Simple statement for only one column where the value is returned as is.
Definition: sqlite.h:300
sqlite::Connection::~Connection
~Connection()
And clean up.
Definition: sqlite.h:311
sqlite::ObjectStatement::begin
iterator begin()
Iterator to the beginning.
Definition: sqlite.h:242
sqlite::detail::ParameterBinder
Bind the given parameter to the sqlite statement.
Definition: sqlite.h:119
sqlite::detail::ParameterBinder::ParameterBinder
ParameterBinder(sqlite3_stmt *statement)
Create a new object for the given statement.
Definition: sqlite.h:122
sqlite::SQLiteError::code
int code() const
Return the sqlite error code.
Definition: sqlite.h:58
sqlite
C++ wrapper around the sqlite C Api for convenient use of SQLite statements in C++.
Definition: sqlite.h:35
sqlite::ObjectStatement::iterator::m_instance
ObjectStatement * m_instance
pointer to the statement
Definition: sqlite.h:236
sqlite::Connection::m_connection
sqlite3 * m_connection
Pointer to the sqlite database object.
Definition: sqlite.h:319
sqlite::ObjectStatement::iterator::operator*
value_type operator*() const
and a dereference operator
Definition: sqlite.h:233
sqlite::detail::ColumnFiller::ColumnFiller
ColumnFiller(sqlite3_stmt *statement)
Create a new instance for the given statement.
Definition: sqlite.h:75
sqlite::SQLiteError::m_code
int m_code
SQLite error code.
Definition: sqlite.h:60
sqlite::ObjectStatement::~ObjectStatement
~ObjectStatement()
Clean up the statement.
Definition: sqlite.h:257
sqlite::ObjectStatement::ObjectStatement
ObjectStatement(sqlite3 *db, const std::string &query, bool persistent)
Create a statement for an existing database object.
Definition: sqlite.h:247