1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
|
export module routemon:sqlite3;
import std;
import :util;
extern "C"
{
using sqlite3 = struct sqlite3;
using sqlite3_mutex = struct sqlite3_mutex;
using sqlite3_stmt = struct sqlite3_stmt;
}
namespace routemon::sqlite3 {
template <class T, template <class U> concept C>
concept optional_of = requires {
typename T::value_type;
requires std::same_as<T, std::optional<typename T::value_type>>;
requires C<typename T::value_type>;
};
template <class T>
concept scannable_prim = std::same_as<T, std::string> || std::same_as<T, double>
|| std::same_as<T, std::int64_t>;
template <class T>
concept scannable = scannable_prim<T> || optional_of<T, scannable_prim>;
class mutex_guard
{
explicit mutex_guard(::sqlite3_mutex* mut) noexcept;
friend auto
do_guarded(::sqlite3_mutex* mut, std::invocable<mutex_guard const&> auto f)
-> decltype(f(std::declval<mutex_guard const&>()));
public:
mutex_guard(mutex_guard const&) = delete;
~mutex_guard();
private:
::sqlite3_mutex* mut_;
};
class error : public std::exception
{
int code_;
std::string message_;
public:
explicit error(mutex_guard const&, int code, ::sqlite3* dbc);
explicit error(int code);
[[nodiscard]] auto what() const noexcept -> char const* override;
[[nodiscard]] auto code() const noexcept -> int;
};
class statement
{
::sqlite3_stmt* stmt_;
public:
explicit statement(::sqlite3_stmt* stmt);
statement(statement const&) = delete;
statement(statement&& s) noexcept;
~statement();
auto get() -> ::sqlite3_stmt*;
};
class row_reader
{
statement stmt_;
explicit row_reader(statement stmt);
friend class connection;
auto is_null(int col) -> bool;
auto ncols() -> std::size_t;
auto scan(int col, std::string& s) -> void;
auto scan(int col, double& v) -> void;
auto scan(int col, std::int64_t& v) -> void;
auto scan(int col, optional_of<scannable> auto& v) -> void
{
if (is_null(col))
{
v.reset();
}
else
{
typename std::remove_cvref_t<decltype(v)>::value_type tmp;
scan(col, tmp);
v = std::move(tmp);
}
}
public:
auto next() -> bool;
auto scan(scannable auto&... args) -> void
{
if (sizeof...(args) > ncols())
throw std::invalid_argument{
"more scanning arguments provided than columns in result set"
};
auto col = 0;
(..., scan(col++, args));
}
auto scan_single(scannable auto&... args) -> void
{
if (!next())
throw std::logic_error{"no row in result set"};
scan(args...);
if (next())
{
throw std::logic_error{"more than one row in result set"};
}
}
};
class binder
{
statement& stmt_;
explicit binder(statement& stmt);
friend class connection;
public:
auto text(std::string const& param_name, std::string_view str) -> void;
static auto noop(binder&) -> void;
};
export class connection
{
::sqlite3* dbc_;
::sqlite3_mutex* mut_;
explicit connection(::sqlite3* dbc);
friend auto open(std::string const& filename) -> connection;
public:
connection(connection const&) = delete;
connection(connection&& c) noexcept;
[[nodiscard]] auto query(
std::string const& sql,
std::function<void(binder&)> const& bf = binder::noop) -> row_reader;
auto exec(
std::string const& sql,
std::function<void(binder&)> const& bf = binder::noop) -> void;
~connection();
};
export auto open(std::string const& filename) -> connection;
} // namespace routemon::sqlite3
|