51 inline void checkSQLiteError(
int code,
const std::string& prefix =
"")
53 if (code != SQLITE_OK)
throw SQLiteError(code, prefix);
72 void operator()(
int index,
double& col) { col = sqlite3_column_double(
m_stmt, index); }
77 if (
auto ptr =
reinterpret_cast<const char*
>(sqlite3_column_text(
m_stmt, index)); ptr !=
nullptr) {
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);
94 if (sqlite3_column_type(
m_stmt, index) != SQLITE_NULL) {
120 checkSQLiteError(
bind(index + 1, std::forward<T>(param)));
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)
132 return sqlite3_bind_text(
m_stmt, index, param.data(), param.size(), SQLITE_TRANSIENT);
135 int bind(
int index,
const std::vector<std::byte>& param)
137 return sqlite3_bind_blob(
m_stmt, index, param.data(), param.size(), SQLITE_TRANSIENT);
141 int bind(
int index,
const std::optional<T>& param)
143 if (param.has_value())
return bind(*param);
144 return sqlite3_bind_null(
m_stmt, index);
150 template<
class Tuple,
class Func, std::size_t ...Is>
151 void visitTupleWithIndexImpl(Tuple&& t, Func&& f, std::index_sequence<Is...>)
154 (f(std::integral_constant<std::size_t, Is> {}, std::get<Is>(t)), ...);
163 template<
class ... T,
class Func>
164 void visitTupleWithIndex(std::tuple<T...>& t, Func&& f)
166 visitTupleWithIndexImpl(t, std::forward<Func>(f), std::make_index_sequence<
sizeof...(T)> {});
193 template<
class ObjectType,
class ... Columns>
200 class iterator:
public std::iterator<std::input_iterator_tag, value_type, size_t> {
212 if (
m_row < 0)
return *
this;
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)) +
')');
251 template<
class ... Parameters>
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) +
")");
259 auto parameter_tuple = std::make_tuple(parameters...);
269 if (
auto ret = sqlite3_step(
m_statement); ret != SQLITE_ROW) {
270 if (ret == SQLITE_DONE)
return false;
271 detail::checkSQLiteError(ret);
279 std::tuple<Columns...> result;
281 return std::make_from_tuple<value_type>(std::move(result));
299 detail::checkSQLiteError(sqlite3_open_v2(filename.c_str(), &
m_connection, SQLITE_OPEN_READONLY,
nullptr));
304 template<
class ... Columns>
Simple wrapper for a SQLite database connection.
Connection(const std::string &filename)
Create from filename.
~Connection()
And clean up.
sqlite3 * m_connection
Pointer to the sqlite database object.
Statement< Columns... > prepare(const std::string &query, bool persistent=false)
Return a prepared statement for execution.
Iterator class to allow iterating over the rows.
bool operator!=(const iterator &other) const
as well as unequality check
iterator(ObjectStatement *instance)
and a real constructor to a statement
iterator()=default
with a default constructor
iterator operator++(int)
also postfix increment
bool operator==(const iterator &other) const
and equality check
ObjectStatement * m_instance
pointer to the statement
iterator & operator++()
increment to next row, stop in case we are done
value_type operator*() const
and a dereference operator
int64_t m_row
current row index
SQLite prepared statement wrapper.
bool step()
Step to the next row in the result.
value_type getRow() const
Return the current row.
iterator end() const
Iterator to the end.
sqlite3_stmt * m_statement
pointer to the statement object
ObjectStatement(sqlite3 *db, const std::string &query, bool persistent)
Create a statement for an existing database object.
~ObjectStatement()
Clean up the statement.
ObjectStatement & execute(Parameters... parameters)
Execute the statement, providing all necessary parameters.
iterator begin()
Iterator to the beginning.
ObjectType value_type
each row gets converted to a instance of this type
Simple error class to be thrown if there is any sqlite error on any of the operations.
int m_code
SQLite error code.
int code() const
Return the sqlite error code.
SQLiteError(int code, const std::string &prefix="")
Construct an instance from a prefix string and an sqlite error code.
Struct to fill the different columns in a sqlite result row into a std::tuple.
ColumnFiller(sqlite3_stmt *statement)
Create a new instance for the given statement.
sqlite3_stmt * m_stmt
statement on which to work on
void operator()(int index, int64_t &col)
Fill 64bit integer column.
void operator()(int index, double &col)
Fill double column.
void operator()(int index, std::string &col)
Fill string column.
void operator()(int index, std::optional< T > &col)
Fill an possibly null column in an optional.
void operator()(int index, int &col)
Fill integer column.
void operator()(int index, std::vector< std::byte > &col)
Fill blob column.
Bind the given parameter to the sqlite statement.
int bind(int index, const std::vector< std::byte > ¶m)
bind a bytestream
ParameterBinder(sqlite3_stmt *statement)
Create a new object for the given statement.
int bind(int index, const std::optional< T > ¶m)
bind an optional: if it doesn't have a value we bind NULL
void operator()(int index, T &¶m)
Bind the parameter with the given index to the statement.
sqlite3_stmt * m_stmt
statement on which to work on
int bind(int index, double param)
bind a double parameter
int bind(int index, int param)
bind an integer parameter
int bind(int index, const std::string ¶m)
bind a string parameter
int bind(int index, int64_t param)
bind a 64bit integer parameter
C++ wrapper around the sqlite C Api for convenient use of SQLite statements in C++.