mirror of
https://github.com/ZmnSCPxj/clboss.git
synced 2026-08-13 12:33:20 +02:00
Sqlite3*: Complete SQLITE3 support.
This commit is contained in:
parent
c332ce88bf
commit
83b09ed680
15 changed files with 652 additions and 11 deletions
|
|
@ -121,8 +121,17 @@ libclboss_la_SOURCES = \
|
|||
S/Bus.hpp \
|
||||
S/Detail/Signal.hpp \
|
||||
S/Detail/SignalBase.hpp \
|
||||
Sqlite3.hpp \
|
||||
Sqlite3/Db.cpp \
|
||||
Sqlite3/Db.hpp \
|
||||
Sqlite3/Detail/binds.cpp \
|
||||
Sqlite3/Detail/binds.hpp \
|
||||
Sqlite3/Detail/columns.cpp \
|
||||
Sqlite3/Detail/columns.hpp \
|
||||
Sqlite3/Query.cpp \
|
||||
Sqlite3/Query.hpp \
|
||||
Sqlite3/Result.cpp \
|
||||
Sqlite3/Result.hpp \
|
||||
Sqlite3/Tx.cpp \
|
||||
Sqlite3/Tx.hpp \
|
||||
Stats/RunningMean.cpp \
|
||||
|
|
|
|||
9
Sqlite3.hpp
Normal file
9
Sqlite3.hpp
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef SQLITE3_HPP
|
||||
#define SQLITE3_HPP
|
||||
|
||||
#include"Sqlite3/Db.hpp"
|
||||
#include"Sqlite3/Query.hpp"
|
||||
#include"Sqlite3/Result.hpp"
|
||||
#include"Sqlite3/Tx.hpp"
|
||||
|
||||
#endif /* !defined(SQLITE3_HPP) */
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
#include<string>
|
||||
|
||||
namespace Ev { template<typename a> class Io; }
|
||||
namespace Sqlite3 { class Result; }
|
||||
namespace Sqlite3 { class Tx; }
|
||||
|
||||
namespace Sqlite3 {
|
||||
|
|
@ -22,6 +23,7 @@ private:
|
|||
class Impl;
|
||||
std::shared_ptr<Impl> pimpl;
|
||||
|
||||
friend class Sqlite3::Result;
|
||||
friend class Sqlite3::Tx;
|
||||
|
||||
void* get_connection() const;
|
||||
|
|
@ -40,6 +42,8 @@ public:
|
|||
Db() =default;
|
||||
Db(Db const&) =default;
|
||||
Db(Db&&) =default;
|
||||
Db& operator=(Db const&) =default;
|
||||
Db& operator=(Db&&) =default;
|
||||
~Db() =default;
|
||||
|
||||
/* If the Db is false (invalid), you cannot use transact(). */
|
||||
|
|
|
|||
35
Sqlite3/Detail/binds.cpp
Normal file
35
Sqlite3/Detail/binds.cpp
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#include"Sqlite3/Detail/binds.hpp"
|
||||
#include<sqlite3.h>
|
||||
#include<stdexcept>
|
||||
|
||||
namespace Sqlite3 { namespace Detail {
|
||||
|
||||
void bind_d(void* stmt, int l, double v) {
|
||||
auto res = sqlite3_bind_double( (sqlite3_stmt*) stmt
|
||||
, l, v
|
||||
);
|
||||
if (res != SQLITE_OK)
|
||||
throw std::runtime_error("Sqlite3: bind error.");
|
||||
}
|
||||
void bind_i(void* stmt, int l, std::int64_t v) {
|
||||
auto res = sqlite3_bind_int64( (sqlite3_stmt*) stmt
|
||||
, l, v
|
||||
);
|
||||
if (res != SQLITE_OK)
|
||||
throw std::runtime_error("Sqlite3: bind error.");
|
||||
}
|
||||
void bind_s(void* stmt, int l, std::string v) {
|
||||
auto res = sqlite3_bind_text( (sqlite3_stmt*) stmt
|
||||
, l, v.c_str(), v.size()
|
||||
, SQLITE_TRANSIENT
|
||||
);
|
||||
if (res != SQLITE_OK)
|
||||
throw std::runtime_error("Sqlite3: bind error.");
|
||||
}
|
||||
void bind_null(void* stmt, int l) {
|
||||
auto res = sqlite3_bind_null( (sqlite3_stmt*) stmt, l);
|
||||
if (res != SQLITE_OK)
|
||||
throw std::runtime_error("Sqlite3: bind error.");
|
||||
}
|
||||
|
||||
}}
|
||||
102
Sqlite3/Detail/binds.hpp
Normal file
102
Sqlite3/Detail/binds.hpp
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
#ifndef SQLITE3_DETAILS_BINDS_HPP
|
||||
#define SQLITE3_DETAILS_BINDS_HPP
|
||||
|
||||
#include<cstddef>
|
||||
#include<cstdint>
|
||||
#include<string>
|
||||
|
||||
namespace Sqlite3 { namespace Detail {
|
||||
|
||||
void bind_d(void* stmt, int l, double);
|
||||
void bind_i(void* stmt, int l, std::int64_t);
|
||||
void bind_s(void* stmt, int l, std::string);
|
||||
void bind_null(void *stmt, int l);
|
||||
|
||||
template<typename a>
|
||||
struct Bind;
|
||||
|
||||
template<>
|
||||
struct Bind<float> {
|
||||
static void bind(void *stmt, int l, float v) {
|
||||
bind_d(stmt, l, v);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct Bind<double> {
|
||||
static void bind(void *stmt, int l, double v) {
|
||||
bind_d(stmt, l, v);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Bind<std::int8_t> {
|
||||
static void bind(void *stmt, int l, std::int8_t v) {
|
||||
bind_i(stmt, l, v);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct Bind<std::uint8_t> {
|
||||
static void bind(void *stmt, int l, std::uint8_t v) {
|
||||
bind_i(stmt, l, v);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct Bind<std::int16_t> {
|
||||
static void bind(void *stmt, int l, std::int16_t v) {
|
||||
bind_i(stmt, l, v);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct Bind<std::uint16_t> {
|
||||
static void bind(void *stmt, int l, std::uint16_t v) {
|
||||
bind_i(stmt, l, v);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct Bind<std::int32_t> {
|
||||
static void bind(void *stmt, int l, std::int32_t v) {
|
||||
bind_i(stmt, l, v);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct Bind<std::uint32_t> {
|
||||
static void bind(void *stmt, int l, std::uint32_t v) {
|
||||
bind_i(stmt, l, v);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct Bind<std::int64_t> {
|
||||
static void bind(void *stmt, int l, std::int64_t v) {
|
||||
bind_i(stmt, l, v);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct Bind<std::uint64_t> {
|
||||
static void bind(void *stmt, int l, std::uint64_t v) {
|
||||
bind_i(stmt, l, v);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Bind<char const*> {
|
||||
static void bind(void *stmt, int l, char const* v) {
|
||||
bind_s(stmt, l, v);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct Bind<std::string> {
|
||||
static void bind(void *stmt, int l, std::string v) {
|
||||
bind_s(stmt, l, std::move(v));
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Bind<std::nullptr_t> {
|
||||
static void bind(void *stmt, int l, std::nullptr_t) {
|
||||
bind_null(stmt, l);
|
||||
}
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* !defined(SQLITE3_DETAILS_BINDS_HPP) */
|
||||
25
Sqlite3/Detail/columns.cpp
Normal file
25
Sqlite3/Detail/columns.cpp
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#include"Sqlite3/Detail/columns.hpp"
|
||||
#include<algorithm>
|
||||
#include<sqlite3.h>
|
||||
|
||||
namespace Sqlite3 { namespace Detail {
|
||||
|
||||
double column_d(void* stmt, int c) {
|
||||
return sqlite3_column_double((sqlite3_stmt*) stmt, c);
|
||||
}
|
||||
std::int64_t column_i(void* stmt, int c) {
|
||||
return sqlite3_column_int64((sqlite3_stmt*) stmt, c);
|
||||
}
|
||||
std::string column_s(void* vstmt, int c) {
|
||||
auto stmt = (sqlite3_stmt*) vstmt;
|
||||
auto len = sqlite3_column_bytes(stmt, c);
|
||||
auto dat = sqlite3_column_text(stmt, c);
|
||||
|
||||
auto s = std::string();
|
||||
s.resize(len);
|
||||
std::copy(dat, dat + len, s.begin());
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
}}
|
||||
98
Sqlite3/Detail/columns.hpp
Normal file
98
Sqlite3/Detail/columns.hpp
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
#ifndef SQLITE3_DETAIL_COLUMNS_HPP
|
||||
#define SQLITE3_DETAIL_COLUMNS_HPP
|
||||
|
||||
#include<cstdint>
|
||||
#include<string>
|
||||
|
||||
namespace Sqlite3 { namespace Detail {
|
||||
|
||||
double column_d(void* stmt, int c);
|
||||
std::int64_t column_i(void* stmt, int c);
|
||||
std::string column_s(void* stmt, int c);
|
||||
|
||||
template<typename a>
|
||||
struct Column;
|
||||
|
||||
template<>
|
||||
struct Column<float> {
|
||||
static
|
||||
float column(void* stmt, int c) {
|
||||
return column_d(stmt, c);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct Column<double> {
|
||||
static
|
||||
double column(void* stmt, int c) {
|
||||
return column_d(stmt, c);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Column<std::int8_t> {
|
||||
static
|
||||
std::int8_t column(void* stmt, int c) {
|
||||
return column_i(stmt, c);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct Column<std::uint8_t> {
|
||||
static
|
||||
std::uint8_t column(void* stmt, int c) {
|
||||
return column_i(stmt, c);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct Column<std::int16_t> {
|
||||
static
|
||||
std::int16_t column(void* stmt, int c) {
|
||||
return column_i(stmt, c);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct Column<std::uint16_t> {
|
||||
static
|
||||
std::uint16_t column(void* stmt, int c) {
|
||||
return column_i(stmt, c);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct Column<std::int32_t> {
|
||||
static
|
||||
std::int32_t column(void* stmt, int c) {
|
||||
return column_i(stmt, c);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct Column<std::uint32_t> {
|
||||
static
|
||||
std::uint32_t column(void* stmt, int c) {
|
||||
return column_i(stmt, c);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct Column<std::int64_t> {
|
||||
static
|
||||
std::int64_t column(void* stmt, int c) {
|
||||
return column_i(stmt, c);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct Column<std::uint64_t> {
|
||||
static
|
||||
std::uint64_t column(void* stmt, int c) {
|
||||
return column_i(stmt, c);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Column<std::string> {
|
||||
static
|
||||
std::string column(void* stmt, int c) {
|
||||
return column_s(stmt, c);
|
||||
}
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* !defined(SQLITE3_DETAIL_COLUMNS_HPP) */
|
||||
63
Sqlite3/Query.cpp
Normal file
63
Sqlite3/Query.cpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#include"Sqlite3/Db.hpp"
|
||||
#include"Sqlite3/Query.hpp"
|
||||
#include"Sqlite3/Result.hpp"
|
||||
#include"Util/make_unique.hpp"
|
||||
#include<sqlite3.h>
|
||||
#include<stdexcept>
|
||||
|
||||
namespace Sqlite3 {
|
||||
|
||||
class Query::Impl {
|
||||
private:
|
||||
Db db;
|
||||
sqlite3_stmt* stmt;
|
||||
|
||||
public:
|
||||
Impl( Db const& db_
|
||||
, void* stmt_
|
||||
) : db(db_)
|
||||
, stmt((sqlite3_stmt*) stmt_)
|
||||
{ }
|
||||
~Impl() {
|
||||
if (stmt)
|
||||
(void) sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
void* get_stmt() const { return stmt; }
|
||||
int get_location(char const* field) const {
|
||||
auto res = sqlite3_bind_parameter_index(stmt, field);
|
||||
if (res == 0)
|
||||
throw std::runtime_error(
|
||||
std::string("Sqlite3::Query::bind: "
|
||||
"no field: "
|
||||
) + field
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
Result execute() {
|
||||
/* Move responsibility off ourself. */
|
||||
auto my_stmt = stmt;
|
||||
stmt = nullptr;
|
||||
/* Result constructor executes first sqlite3_step. */
|
||||
return Result(db, my_stmt);
|
||||
}
|
||||
};
|
||||
|
||||
Query::Query(Sqlite3::Db const& db, void* stmt)
|
||||
: pimpl(Util::make_unique<Impl>(db, stmt)) { }
|
||||
Query::Query(Query&& o) : pimpl(std::move(o.pimpl)) { }
|
||||
Query::~Query() { }
|
||||
|
||||
void* Query::get_stmt() const { return pimpl->get_stmt(); }
|
||||
int Query::get_location(const char* field) const {
|
||||
return pimpl->get_location(field);
|
||||
}
|
||||
|
||||
Result Query::execute() {
|
||||
auto r = pimpl->execute();
|
||||
pimpl = nullptr;
|
||||
return r;
|
||||
}
|
||||
|
||||
}
|
||||
66
Sqlite3/Query.hpp
Normal file
66
Sqlite3/Query.hpp
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#ifndef SQLITE3_QUERY_HPP
|
||||
#define SQLITE3_QUERY_HPP
|
||||
|
||||
#include"Sqlite3/Detail/binds.hpp"
|
||||
#include<memory>
|
||||
#include<string>
|
||||
|
||||
namespace Sqlite3 { class Db; }
|
||||
namespace Sqlite3 { class Result; }
|
||||
namespace Sqlite3 { class Tx; }
|
||||
|
||||
namespace Sqlite3 {
|
||||
|
||||
/** class Sqlite3::Query
|
||||
*
|
||||
* @brief Represents a query that will have parameters bound,
|
||||
* then later executed to produce a result.
|
||||
*/
|
||||
class Query {
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> pimpl;
|
||||
|
||||
friend class Sqlite3::Tx;
|
||||
Query(Sqlite3::Db const&, void*);
|
||||
|
||||
void* get_stmt() const;
|
||||
int get_location(char const*) const;
|
||||
|
||||
public:
|
||||
Query() =delete;
|
||||
Query(Query&&);
|
||||
~Query();
|
||||
|
||||
/** Sqlite3::Query::bind
|
||||
*
|
||||
* @brief binds a named parameter of the query.
|
||||
* Named parameters are of the form :VVV, @VVV,
|
||||
* or $VVV, where VVV is alphanumeric.
|
||||
*/
|
||||
template<typename a>
|
||||
Query& bind(char const* field, a value) {
|
||||
Detail::Bind<a>::bind( get_stmt(), get_location(field)
|
||||
, std::move(value)
|
||||
);
|
||||
return *this;
|
||||
}
|
||||
template<typename a>
|
||||
Query& bind(std::string const& field, a value) {
|
||||
return bind<a>(field.c_str(), value);
|
||||
}
|
||||
|
||||
/** Sqlite3::Query::execute
|
||||
*
|
||||
* @brief executes the query.
|
||||
* Pre-condition: you should have bound all the
|
||||
* parameters (or else they would be NULL).
|
||||
* Post-condition: the query cannot be reused
|
||||
* and is in an "invalid" state.
|
||||
*/
|
||||
Result execute();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* !defined(SQLITE3_QUERY_HPP) */
|
||||
|
|
@ -6,14 +6,18 @@ Here is our desired interface:
|
|||
/* This could block if a transaction is currently in-flight. */
|
||||
return db.transact();
|
||||
}).then([](Sqlite3::Tx tx) {
|
||||
auto it = tx.query("SELECT (a, b) FROM tablename WHERE id=:ID")
|
||||
.bind(":ID", 42)
|
||||
.execute()
|
||||
;
|
||||
/* `it` is a single-pass input iterator. */
|
||||
for (; it != tx.end(); ++it) {
|
||||
auto a = it->column<int>(0);
|
||||
auto b = it->column<std::string>(1);
|
||||
auto res = tx.query("SELECT (a, b) FROM tablename WHERE id=:ID")
|
||||
.bind(":ID", 42)
|
||||
.execute()
|
||||
;
|
||||
/* `res` is a single-pass sequence!
|
||||
* It has an iterator type that is an input iterator
|
||||
* (can only dereference and advance, but copying
|
||||
* does not create a distinct state).
|
||||
*/
|
||||
for (auto r : res) {
|
||||
auto a = r.column<int>(0);
|
||||
auto b = r.column<std::string>(1);
|
||||
process(a, b);
|
||||
}
|
||||
/* Sqlite3::Tx cannot be copied, but can be moved.
|
||||
|
|
|
|||
39
Sqlite3/Result.cpp
Normal file
39
Sqlite3/Result.cpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#include"Sqlite3/Db.hpp"
|
||||
#include"Sqlite3/Result.hpp"
|
||||
#include<sqlite3.h>
|
||||
|
||||
namespace Sqlite3 {
|
||||
|
||||
Result::Result(Sqlite3::Db const& db_, void* stmt_
|
||||
) : db(db_), stmt(stmt_) {
|
||||
advance();
|
||||
}
|
||||
Result::Result(Result&& o) {
|
||||
db = std::move(o.db);
|
||||
stmt = o.stmt;
|
||||
o.stmt = nullptr;
|
||||
}
|
||||
Result::~Result() {
|
||||
if (stmt)
|
||||
sqlite3_finalize((sqlite3_stmt*) stmt);
|
||||
}
|
||||
|
||||
bool Result::advance() {
|
||||
auto ss = (sqlite3_stmt*) stmt;
|
||||
auto res = sqlite3_step(ss);
|
||||
if (res == SQLITE_DONE) {
|
||||
sqlite3_finalize((sqlite3_stmt*) stmt);
|
||||
stmt = nullptr;
|
||||
return false;
|
||||
} else if (res == SQLITE_ROW)
|
||||
return true;
|
||||
else {
|
||||
auto connection = (sqlite3*) db.get_connection();
|
||||
auto err = std::string(sqlite3_errmsg(connection));
|
||||
throw std::runtime_error(
|
||||
std::string("Sqlite3::Result: ") + err
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
130
Sqlite3/Result.hpp
Normal file
130
Sqlite3/Result.hpp
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
#ifndef SQLITE3_RESULT_HPP
|
||||
#define SQLITE3_RESULT_HPP
|
||||
|
||||
#include"Sqlite3/Db.hpp"
|
||||
#include"Sqlite3/Detail/columns.hpp"
|
||||
#include<iterator>
|
||||
|
||||
namespace Sqlite3 { class Query; }
|
||||
namespace Sqlite3 { class Result; }
|
||||
namespace Sqlite3 { class Row; }
|
||||
|
||||
namespace Sqlite3 {
|
||||
|
||||
/** class Sqlite3::Row
|
||||
*
|
||||
* @brief repersents a row in the results.
|
||||
*/
|
||||
class Row {
|
||||
private:
|
||||
Result* r;
|
||||
|
||||
friend class Sqlite3::Result;
|
||||
explicit
|
||||
Row(Result& r_) : r(&r_) { }
|
||||
|
||||
Row() : r(nullptr) { }
|
||||
|
||||
public:
|
||||
Row(Row const&) =delete;
|
||||
Row(Row&) =delete;
|
||||
|
||||
~Row() =default;
|
||||
|
||||
template<typename a>
|
||||
a get(int c);
|
||||
};
|
||||
|
||||
/** Sqlite3::Result
|
||||
*
|
||||
* @brief sequence of rows emitted by a query.
|
||||
*
|
||||
* @desc
|
||||
* IMPORTANT a `Sqlite3::Result` can only be traversed
|
||||
* exactly once!!!
|
||||
*/
|
||||
class Result {
|
||||
private:
|
||||
Sqlite3::Db db;
|
||||
void* stmt;
|
||||
|
||||
friend class Sqlite3::Query;
|
||||
friend class Sqlite3::Row;
|
||||
|
||||
Result(Sqlite3::Db const& db_, void* stmt_);
|
||||
|
||||
/* Return false at end of result. */
|
||||
bool advance();
|
||||
|
||||
public:
|
||||
Result() =delete;
|
||||
Result(Result const&) =delete;
|
||||
Result(Result&&);
|
||||
~Result();
|
||||
|
||||
Result& operator=(Result&& o) {
|
||||
auto tmp = std::move(o);
|
||||
std::swap(db, tmp.db);
|
||||
std::swap(stmt, tmp.stmt);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Sqlite3::Result::iterator
|
||||
*
|
||||
* @brief An input iterator that lets you traverse
|
||||
* the results once.
|
||||
*/
|
||||
class iterator : Row {
|
||||
private:
|
||||
friend class Sqlite3::Result;
|
||||
explicit
|
||||
iterator(Result& r_) : Row(r_) {
|
||||
if (!r->stmt)
|
||||
r = nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
iterator() : Row() { }
|
||||
iterator(iterator const& o) : Row() {
|
||||
r = o.r;
|
||||
}
|
||||
|
||||
bool operator==(iterator const& o) const {
|
||||
return o.r == r;
|
||||
}
|
||||
bool operator!=(iterator const& o) const {
|
||||
return o.r != r;
|
||||
}
|
||||
|
||||
iterator& operator++() {
|
||||
if (r)
|
||||
if (!r->advance())
|
||||
r = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
typedef std::input_iterator_tag iterator_category;
|
||||
typedef Row* pointer;
|
||||
typedef Row reference;
|
||||
typedef Row value_type;
|
||||
|
||||
Row& operator*() {
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
iterator begin() {
|
||||
return iterator(*this);
|
||||
}
|
||||
iterator end() {
|
||||
return iterator();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename a>
|
||||
a Row::get(int c) {
|
||||
return Detail::Column<a>::column(r->stmt, c);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* !defined(SQLITE3_RESULT_HPP) */
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
#include"Sqlite3/Db.hpp"
|
||||
#include"Sqlite3/Query.hpp"
|
||||
#include"Sqlite3/Tx.hpp"
|
||||
#include"Util/make_unique.hpp"
|
||||
#include<sqlite3.h>
|
||||
|
|
@ -47,6 +48,18 @@ public:
|
|||
if (res != SQLITE_OK)
|
||||
throw_sqlite3(q);
|
||||
}
|
||||
|
||||
Query query(char const* sql) {
|
||||
auto connection = (sqlite3*) db.get_connection();
|
||||
auto stmt = (sqlite3_stmt*) nullptr;
|
||||
auto res = sqlite3_prepare_v2( connection, sql, -1
|
||||
, &stmt, nullptr
|
||||
);
|
||||
if (res != SQLITE_OK)
|
||||
throw_sqlite3(sql);
|
||||
|
||||
return Query(db, stmt);
|
||||
}
|
||||
};
|
||||
|
||||
Tx::Tx(Sqlite3::Db const& db)
|
||||
|
|
@ -69,6 +82,10 @@ void Tx::rollback() {
|
|||
pimpl = nullptr;
|
||||
}
|
||||
|
||||
Query Tx::query(char const* sql) {
|
||||
return pimpl->query(sql);
|
||||
}
|
||||
|
||||
void Tx::query_execute(char const* q) {
|
||||
return pimpl->query_execute(q);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,13 @@ public:
|
|||
operator bool() const { return !!pimpl; }
|
||||
bool operator!() const { return !pimpl; }
|
||||
|
||||
Sqlite3::Query query(std::string const&);
|
||||
/** Sqlite3::Tx::query
|
||||
*
|
||||
* @brief Create a query; provide the query template
|
||||
* and bind any parameters to it, then execute to
|
||||
* retrieve results.
|
||||
*/
|
||||
Sqlite3::Query query(char const*);
|
||||
|
||||
/** Sqlite3::Tx::query_execute
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
#undef NDEBUG
|
||||
#include"Sqlite3/Db.hpp"
|
||||
#include"Sqlite3/Tx.hpp"
|
||||
#include"Sqlite3.hpp"
|
||||
#include"Ev/Io.hpp"
|
||||
#include"Ev/concurrent.hpp"
|
||||
#include"Ev/start.hpp"
|
||||
|
|
@ -47,6 +46,41 @@ int main() {
|
|||
return db.transact();
|
||||
}).then([&](Sqlite3::Tx tx) {
|
||||
tx.query_execute("CREATE TABLE \"foo\" (c1 INTEGER, c2 TEXT);");
|
||||
tx.commit();
|
||||
|
||||
/* Test full query interface. */
|
||||
return db.transact();
|
||||
}).then([&](Sqlite3::Tx tx) {
|
||||
auto res = tx.query("INSERT INTO \"foo\" VALUES(:c1, :c2)")
|
||||
.bind(":c1", 42)
|
||||
.bind(":c2", "some text")
|
||||
.execute()
|
||||
;
|
||||
for (auto& r : res) {
|
||||
(void) r;
|
||||
/* Should have empty result! */
|
||||
assert(false);
|
||||
}
|
||||
tx.commit();
|
||||
|
||||
/* Test full query interface again. */
|
||||
return db.transact();
|
||||
}).then([&](Sqlite3::Tx tx) {
|
||||
auto res = tx.query("SELECT c1, c2 FROM \"foo\"")
|
||||
.execute()
|
||||
;
|
||||
auto flag = false;
|
||||
for (auto& r : res) {
|
||||
/* Should have single result! */
|
||||
assert(!flag);
|
||||
flag = true;
|
||||
/* Should be what we inserted. */
|
||||
assert(r.get<int>(0) == 42);
|
||||
assert(r.get<std::string>(1) == "some text");
|
||||
}
|
||||
/* Should have result! */
|
||||
assert(flag);
|
||||
tx.commit();
|
||||
|
||||
return Ev::lift(0);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue