Branch data Line data Source code
1 : : /*
2 : : * Copyright 2006-2009 Savarese Software Research Corporation
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License");
5 : : * you may not use this file except in compliance with the License.
6 : : * You may obtain a copy of the License at
7 : : *
8 : : * https://www.savarese.com/software/ApacheLicense-2.0
9 : : *
10 : : * Unless required by applicable law or agreed to in writing, software
11 : : * distributed under the License is distributed on an "AS IS" BASIS,
12 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : : * See the License for the specific language governing permissions and
14 : : * limitations under the License.
15 : : */
16 : :
17 : : /**
18 : : * @file
19 : : * This header defines the Database class.
20 : : */
21 : :
22 : : #ifndef __SSRC_WSPR_DATABASE_DATABASE_H
23 : : #define __SSRC_WSPR_DATABASE_DATABASE_H
24 : :
25 : : #include <ssrc/wispers/database/types.h>
26 : : #include <sstream>
27 : : #include <cstring>
28 : :
29 : : // TODO: Add support for handling SQLITE_BUSY. Currently we throw!
30 : : __BEGIN_NS_SSRC_WSPR_DATABASE
31 : :
32 : 266 : struct QueryResult {
33 : : unsigned int changes;
34 : : result_set_ptr result_set;
35 : :
36 : 157 : explicit QueryResult(const unsigned int changes = 0) :
37 : 157 : changes(changes), result_set()
38 : 157 : { }
39 : :
40 : 103 : explicit QueryResult(const result_set_ptr & result_set) :
41 : 103 : changes(0), result_set(result_set)
42 : 103 : { }
43 : : };
44 : :
45 : : // Forward declaration;
46 : :
47 : 868 : class PreparedStatement {
48 : : friend class Database;
49 : :
50 : : sqlite3_stmt_ptr _statement;
51 : :
52 : 868 : PreparedStatement(sqlite3_ptr & db, const string & statement) :
53 : : _statement(detail::sqlite3_stmt_initializer(db, statement),
54 : 868 : detail::sqlite3_stmt_init())
55 : : {
56 [ - + ]: 868 : if(_statement.init_error())
57 [ # # ]: 0 : throw DatabaseException(_statement);
58 : 868 : }
59 : :
60 : : public:
61 : :
62 : : enum BindPolicy { BindTransient, BindStatic };
63 : :
64 : 5 : bool expired() {
65 : 5 : return SQLite3::sqlite3_expired(_statement.get());
66 : : }
67 : :
68 : 5 : unsigned int count_parameters() {
69 : 5 : return SQLite3::sqlite3_bind_parameter_count(_statement.get());
70 : : }
71 : :
72 : : PreparedStatement & bind(const unsigned int index, const double value)
73 : : SSRC_DECL_THROW(DatabaseException)
74 : : {
75 : : if(SQLite3::sqlite3_bind_double(_statement.get(), index, value)
76 : : != SQLITE_OK)
77 : : throw DatabaseException(_statement);
78 : : return *this;
79 : : }
80 : :
81 : 263 : PreparedStatement & bind(const unsigned int index, const int value)
82 : : SSRC_DECL_THROW(DatabaseException)
83 : : {
84 [ - + ]: 263 : if(SQLite3::sqlite3_bind_int(_statement.get(), index, value) != SQLITE_OK)
85 [ # # ]: 0 : throw DatabaseException(_statement);
86 : 263 : return *this;
87 : : }
88 : :
89 : 101 : PreparedStatement & bind(const unsigned int index, const unsigned int value)
90 : : SSRC_DECL_THROW(DatabaseException)
91 : : {
92 : 101 : return bind(index, static_cast<int>(value));
93 : : }
94 : :
95 : : PreparedStatement & bind(const unsigned int index, const std::int16_t value)
96 : : SSRC_DECL_THROW(DatabaseException)
97 : : {
98 : : return bind(index, static_cast<int>(value));
99 : : }
100 : :
101 : 66 : PreparedStatement & bind(const unsigned int index, const std::uint16_t value)
102 : : SSRC_DECL_THROW(DatabaseException)
103 : : {
104 : 66 : return bind(index, static_cast<int>(value));
105 : : }
106 : :
107 : : PreparedStatement & bind(const unsigned int index, const bool value)
108 : : SSRC_DECL_THROW(DatabaseException)
109 : : {
110 : : if(SQLite3::sqlite3_bind_int(_statement.get(), index, value) != SQLITE_OK)
111 : : throw DatabaseException(_statement);
112 : : return *this;
113 : : }
114 : :
115 : 277 : PreparedStatement & bind(const unsigned int index, const std::int64_t value)
116 : : SSRC_DECL_THROW(DatabaseException)
117 : : {
118 [ - + ]: 277 : if(SQLite3::sqlite3_bind_int64(_statement.get(), index, value)
119 : : != SQLITE_OK)
120 [ # # ]: 0 : throw DatabaseException(_statement);
121 : 277 : return *this;
122 : : }
123 : :
124 : 118 : PreparedStatement & bind(const unsigned int index, const std::uint64_t value)
125 : : SSRC_DECL_THROW(DatabaseException)
126 : : {
127 : 118 : return bind(index, static_cast<std::int64_t>(value));
128 : : }
129 : :
130 : : #if (LONG_MAX == INT_MAX)
131 : : PreparedStatement & bind(const unsigned int index, const long value)
132 : : SSRC_DECL_THROW(DatabaseException)
133 : : {
134 : : if(SQLite3::sqlite3_bind_int64(_statement.get(), index, value)
135 : : != SQLITE_OK)
136 : : throw DatabaseException(_statement);
137 : : return *this;
138 : : }
139 : :
140 : : PreparedStatement & bind(const unsigned int index, const unsigned long value)
141 : : SSRC_DECL_THROW(DatabaseException)
142 : : {
143 : : return bind(index, static_cast<long>(value));
144 : : }
145 : : #endif
146 : :
147 : 110 : PreparedStatement & bind(const unsigned int index, const char * const value,
148 : : const unsigned int bytes,
149 : : const BindPolicy policy = BindTransient)
150 : : SSRC_DECL_THROW(DatabaseException)
151 : : {
152 [ - + ]: 110 : if(SQLite3::sqlite3_bind_text(_statement.get(), index, value,
153 : : bytes,
154 : : (policy == BindTransient ?
155 [ + - ]: 110 : SQLITE_TRANSIENT : SQLITE_STATIC)))
156 [ # # ]: 0 : throw DatabaseException(_statement);
157 : 110 : return *this;
158 : : }
159 : :
160 : 1 : PreparedStatement & bind(const unsigned int index, const char * const value,
161 : : const BindPolicy policy = BindTransient)
162 : : SSRC_DECL_THROW(DatabaseException)
163 : : {
164 : 1 : return bind(index, value, std::strlen(value), policy);
165 : : }
166 : :
167 : 109 : PreparedStatement & bind(const unsigned int index, const string & value,
168 : : const BindPolicy policy = BindTransient)
169 : : SSRC_DECL_THROW(DatabaseException)
170 : : {
171 : 109 : return bind(index, value.c_str(), value.size(), policy);
172 : : }
173 : :
174 : 2 : PreparedStatement & bind(const unsigned int index, const void *value,
175 : : const unsigned int bytes,
176 : : const BindPolicy policy = BindTransient)
177 : : SSRC_DECL_THROW(DatabaseException)
178 : : {
179 [ - + ]: 2 : if(SQLite3::sqlite3_bind_blob(_statement.get(), index, value, bytes,
180 : : (policy == BindTransient ?
181 [ + - ]: 2 : SQLITE_TRANSIENT : SQLITE_STATIC)))
182 [ # # ]: 0 : throw DatabaseException(_statement);
183 : 2 : return *this;
184 : : }
185 : :
186 : 1 : PreparedStatement & bind(const unsigned int index, const blob_type & blob,
187 : : const BindPolicy policy = BindTransient)
188 : : SSRC_DECL_THROW(DatabaseException)
189 : : {
190 : 1 : return bind(index, blob.first, blob.second, policy);
191 : : }
192 : :
193 : : /*
194 : : * We allow client code to implement custom bind functions
195 : : * by defining a function called bind_column with the following
196 : : * signature:
197 : : * PreparedStatement & bind_column(PreparedStatement & statement,
198 : : * const unsigned int index,
199 : : * const T & t);
200 : : */
201 : : template<typename T>
202 : 1 : PreparedStatement & bind(const unsigned int index, const T & t)
203 : : SSRC_DECL_THROW(DatabaseException)
204 : : {
205 : 1 : return bind_column(*this, index, t);
206 : : }
207 : :
208 : : PreparedStatement & unbind(const unsigned int index)
209 : : SSRC_DECL_THROW(DatabaseException)
210 : : {
211 : : if(SQLite3::sqlite3_bind_null(_statement.get(), index) != SQLITE_OK)
212 : : throw DatabaseException(_statement);
213 : : return *this;
214 : : }
215 : :
216 : : PreparedStatement & unbind() SSRC_DECL_THROW(DatabaseException) {
217 : : if(SQLite3::sqlite3_clear_bindings(_statement.get()) != SQLITE_OK)
218 : : throw DatabaseException(_statement);
219 : : return *this;
220 : : }
221 : :
222 : 70 : void reset() SSRC_DECL_THROW(DatabaseException) {
223 [ - + ]: 70 : if(SQLite3::sqlite3_reset(_statement.get()) != SQLITE_OK)
224 [ # # ]: 0 : throw DatabaseException(_statement);
225 : 70 : }
226 : :
227 : 260 : QueryResult execute() SSRC_DECL_THROW(DatabaseException) {
228 : : int result;
229 : :
230 : 260 : result = detail::step(_statement);
231 : :
232 [ + + ]: 260 : if(result == SQLITE_DONE) {
233 : : return
234 : 157 : QueryResult(SQLite3::sqlite3_changes(SQLite3::sqlite3_db_handle(_statement.get())));
235 : : }
236 : :
237 [ - + ]: 103 : if(result != SQLITE_ROW)
238 [ # # ]: 0 : throw DatabaseException(_statement);
239 : :
240 [ + - + - ]: 103 : return QueryResult(result_set_ptr(new ResultSet(_statement)));
241 : : }
242 : :
243 : : template<typename functor>
244 : 35 : unsigned int for_each(const functor & apply) SSRC_DECL_THROW(DatabaseException) {
245 : 70 : QueryResult && result = execute();
246 : :
247 [ + + + + : 35 : if(result.result_set)
+ + ]
248 [ + - + - : 30 : return result.result_set->for_each(apply);
+ - ]
249 : :
250 : 5 : return 0;
251 : : }
252 : :
253 : : // TODO: Move to ResultSetMetaData? Add other metadata functions.
254 : 11 : unsigned int count_columns() {
255 : 11 : return SQLite3::sqlite3_column_count(_statement.get());
256 : : }
257 : :
258 : 2 : string column_name(const unsigned int index) {
259 : 2 : const char *name = SQLite3::sqlite3_column_name(_statement.get(), index);
260 : :
261 [ + - + - : 2 : return (name ? name : string());
# # + - #
# ]
262 : : }
263 : :
264 : : private:
265 : :
266 : : /* _bindp is a support function for compile-time expansion of bind
267 : : sequence required by bindp. It has the effect of producing
268 : : bind(1,p1).bind(2,p2).bind(3,p3) ...
269 : : */
270 : :
271 : : template<const unsigned int index, typename T>
272 : 226 : PreparedStatement & _bindp(const T & t)
273 : : SSRC_DECL_THROW(DatabaseException)
274 : : {
275 : 226 : return bind(index, t);
276 : : }
277 : :
278 : : template<const unsigned int index, typename T, typename... P>
279 : : PreparedStatement &
280 : 403 : _bindp(const T & t, const P & ...p)
281 : : SSRC_DECL_THROW(DatabaseException)
282 : : {
283 : 403 : return bind(index, t)._bindp<index + 1>(p...);
284 : : }
285 : :
286 : : public:
287 : :
288 : : template<typename... P>
289 : 226 : PreparedStatement & bindp(const P & ...p) SSRC_DECL_THROW(DatabaseException) {
290 : 226 : return _bindp<1>(p...);
291 : : }
292 : :
293 : : template<typename... P>
294 : 32 : QueryResult execute(const P & ...p) SSRC_DECL_THROW(DatabaseException) {
295 : 32 : return bindp(p...).execute();
296 : : }
297 : :
298 : : template<typename... P, typename functor>
299 : 31 : unsigned int for_each(const functor & apply, const P & ...p)
300 : : SSRC_DECL_THROW(DatabaseException)
301 : : {
302 : 31 : return bindp(p...).for_each(apply);
303 : : }
304 : : };
305 : :
306 : : typedef SSRC_UNIQUE_PTR<PreparedStatement> prepared_statement_ptr;
307 : :
308 [ + - + - : 23 : class Database {
+ - ]
309 : :
310 : : sqlite3_ptr _database;
311 : : prepared_statement_ptr _begin_transaction;
312 : : prepared_statement_ptr _rollback_transaction;
313 : : prepared_statement_ptr _end_transaction;
314 : :
315 : : public:
316 : :
317 : : explicit
318 : 23 : Database(const string & db_name = ":memory:") SSRC_DECL_THROW(DatabaseException) :
319 : 23 : _database(db_name)
320 : : {
321 [ - + ]: 23 : if(_database.init_error())
322 [ # # # # ]: 0 : throw DatabaseException("database init error");
323 : :
324 [ + - + - : 23 : _begin_transaction = prepare("BEGIN");
+ - + - +
- ]
325 [ + - + - : 23 : _rollback_transaction = prepare("ROLLBACK");
+ - + - +
- ]
326 [ + - + - : 23 : _end_transaction = prepare("END");
+ - + - +
- ]
327 : 23 : }
328 : :
329 : 868 : prepared_statement_ptr prepare(const string & statement)
330 : : SSRC_DECL_THROW(DatabaseException)
331 : : {
332 [ + - ]: 868 : return prepared_statement_ptr(new PreparedStatement(_database, statement));
333 : : }
334 : :
335 : 4 : void execute(const string & statement)
336 : : SSRC_DECL_THROW(DatabaseException)
337 : : {
338 : 4 : char *err = 0;
339 : :
340 [ - + # # : 4 : if(SQLite3::sqlite3_exec(_database.get(), statement.c_str(), 0, 0, &err)
- + ]
341 : : != SQLITE_OK && err != 0)
342 : : {
343 [ # # ]: 0 : string error(err);
344 [ # # ]: 0 : SQLite3::sqlite3_free(err);
345 [ # # ]: 0 : throw DatabaseException(error);
346 : : }
347 : 4 : }
348 : :
349 : : bool auto_commit() {
350 : : return SQLite3::sqlite3_get_autocommit(_database.get());
351 : : }
352 : :
353 : : void begin_transaction() SSRC_DECL_THROW(DatabaseException) {
354 : : _begin_transaction->execute();
355 : : }
356 : :
357 : : void rollback_transaction() SSRC_DECL_THROW(DatabaseException) {
358 : : _rollback_transaction->execute();
359 : : }
360 : :
361 : : void end_transaction() SSRC_DECL_THROW(DatabaseException) {
362 : : _end_transaction->execute();
363 : : }
364 : : };
365 : :
366 : : __END_NS_SSRC_WSPR_DATABASE
367 : :
368 : : #endif
|