Skip to content

Commit 32f8f25

Browse files
committed
Merge master into sqlcipher
2 parents d203343 + f9e8c40 commit 32f8f25

File tree

10 files changed

+169
-198059
lines changed

10 files changed

+169
-198059
lines changed

Makefile.in

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ tests/results:$(TEST_RESULT)
6565
@echo -------------- Test Results ---------------
6666
@cat tests/results
6767
@echo -------------------------------------------
68-
@ ! grep -qv OK tests/results
68+
@ ! grep -qv 'OK\|Skipped' tests/results
6969

7070

7171
#Build a test executable from a test program. On compile error,
@@ -87,6 +87,9 @@ tests/%.result_: tests/%.test
8787
if [ $$a -ge 128 and ] ; \
8888
then \
8989
echo Crash!! > $@ ; \
90+
elif [ $$a -eq 42 ] ;\
91+
then \
92+
echo Skipped > $@ ; \
9093
elif [ $$a -ne 126 ] ;\
9194
then \
9295
echo Failed > $@ ; \

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ Errors
284284
----
285285
286286
On error, the library throws an error class indicating the type of error. The error classes are derived from the SQLITE3 error names, so if the error code is SQLITE_CONSTRAINT, the error class thrown is sqlite::exceptions::constraint. Note that all errors are derived from sqlite::sqlite_exception and that itself is derived from std::runtime_exception.
287+
sqlite::sqlite_exception has a get_code() member function to get the SQLITE3 error code.
287288
288289
```c++
289290
database db(":memory:");
@@ -297,7 +298,7 @@ On error, the library throws an error class indicating the type of error. The er
297298
/* if you are trying to catch all sqlite related exceptions
298299
* make sure to catch them by reference */
299300
catch (sqlite_exception& e) {
300-
cerr << e.what() << endl;
301+
cerr << e.get_code() << ": " << e.what() << endl;
301302
}
302303
/* you can catch specific exceptions as well,
303304
catch(sqlite::exceptions::constraint e) { } */
@@ -361,6 +362,12 @@ The usual way works for installing:
361362

362363
Note, there's nothing to make, so you there's no need to run configure and you can simply point your compiler at the hdr/ directory.
363364

365+
Breaking Changes
366+
----
367+
368+
- Databases with non-ASCII characters in their names created with versions up to 2.4 are not found by the current master.
369+
You have to manually rename them to their actual (UTF-8 encoded) name.
370+
364371
Package managers
365372
----
366373
Pull requests are welcome :wink:

hdr/sqlite_modern_cpp.h

Lines changed: 76 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@
88
#include <memory>
99
#include <vector>
1010

11+
#ifdef __has_include
1112
#if __has_include(<optional>)
13+
#define _MODERN_SQLITE_STD_OPTIONAL_SUPPORT
14+
#endif
15+
#endif
16+
17+
#ifdef _MODERN_SQLITE_STD_OPTIONAL_SUPPORT
1218
#include <optional>
1319
#endif
1420

@@ -18,12 +24,18 @@
1824

1925
#include <sqlite3.h>
2026

21-
#include <sqlite_modern_cpp/utility/function_traits.h>
27+
#include "sqlite_modern_cpp/utility/function_traits.h"
28+
#include "sqlite_modern_cpp/utility/uncaught_exceptions.h"
2229

2330
namespace sqlite {
2431

25-
struct sqlite_exception: public std::runtime_error {
26-
sqlite_exception(const char* msg):runtime_error(msg) {}
32+
class sqlite_exception: public std::runtime_error {
33+
public:
34+
sqlite_exception(const char* msg, int code = -1): runtime_error(msg), code(code) {}
35+
sqlite_exception(int code): runtime_error(sqlite3_errstr(code)), code(code) {}
36+
int get_code() {return code;}
37+
private:
38+
int code;
2739
};
2840

2941
namespace exceptions {
@@ -64,33 +76,33 @@ namespace sqlite {
6476
class no_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; };
6577

6678
static void throw_sqlite_error(const int& error_code) {
67-
if(error_code == SQLITE_ERROR) throw exceptions::error(sqlite3_errstr(error_code));
68-
else if(error_code == SQLITE_INTERNAL) throw exceptions::internal (sqlite3_errstr(error_code));
69-
else if(error_code == SQLITE_PERM) throw exceptions::perm(sqlite3_errstr(error_code));
70-
else if(error_code == SQLITE_ABORT) throw exceptions::abort(sqlite3_errstr(error_code));
71-
else if(error_code == SQLITE_BUSY) throw exceptions::busy(sqlite3_errstr(error_code));
72-
else if(error_code == SQLITE_LOCKED) throw exceptions::locked(sqlite3_errstr(error_code));
73-
else if(error_code == SQLITE_NOMEM) throw exceptions::nomem(sqlite3_errstr(error_code));
74-
else if(error_code == SQLITE_READONLY) throw exceptions::readonly(sqlite3_errstr(error_code));
75-
else if(error_code == SQLITE_INTERRUPT) throw exceptions::interrupt(sqlite3_errstr(error_code));
76-
else if(error_code == SQLITE_IOERR) throw exceptions::ioerr(sqlite3_errstr(error_code));
77-
else if(error_code == SQLITE_CORRUPT) throw exceptions::corrupt(sqlite3_errstr(error_code));
78-
else if(error_code == SQLITE_NOTFOUND) throw exceptions::notfound(sqlite3_errstr(error_code));
79-
else if(error_code == SQLITE_FULL) throw exceptions::full(sqlite3_errstr(error_code));
80-
else if(error_code == SQLITE_CANTOPEN) throw exceptions::cantopen(sqlite3_errstr(error_code));
81-
else if(error_code == SQLITE_PROTOCOL) throw exceptions::protocol(sqlite3_errstr(error_code));
82-
else if(error_code == SQLITE_EMPTY) throw exceptions::empty(sqlite3_errstr(error_code));
83-
else if(error_code == SQLITE_SCHEMA) throw exceptions::schema(sqlite3_errstr(error_code));
84-
else if(error_code == SQLITE_TOOBIG) throw exceptions::toobig(sqlite3_errstr(error_code));
85-
else if(error_code == SQLITE_CONSTRAINT) throw exceptions::constraint(sqlite3_errstr(error_code));
86-
else if(error_code == SQLITE_MISMATCH) throw exceptions::mismatch(sqlite3_errstr(error_code));
87-
else if(error_code == SQLITE_MISUSE) throw exceptions::misuse(sqlite3_errstr(error_code));
88-
else if(error_code == SQLITE_NOLFS) throw exceptions::nolfs(sqlite3_errstr(error_code));
89-
else if(error_code == SQLITE_AUTH) throw exceptions::auth(sqlite3_errstr(error_code));
90-
else if(error_code == SQLITE_FORMAT) throw exceptions::format(sqlite3_errstr(error_code));
91-
else if(error_code == SQLITE_RANGE) throw exceptions::range(sqlite3_errstr(error_code));
92-
else if(error_code == SQLITE_NOTADB) throw exceptions::notadb(sqlite3_errstr(error_code));
93-
else throw sqlite_exception(sqlite3_errstr(error_code));
79+
if(error_code == SQLITE_ERROR) throw exceptions::error(error_code);
80+
else if(error_code == SQLITE_INTERNAL) throw exceptions::internal (error_code);
81+
else if(error_code == SQLITE_PERM) throw exceptions::perm(error_code);
82+
else if(error_code == SQLITE_ABORT) throw exceptions::abort(error_code);
83+
else if(error_code == SQLITE_BUSY) throw exceptions::busy(error_code);
84+
else if(error_code == SQLITE_LOCKED) throw exceptions::locked(error_code);
85+
else if(error_code == SQLITE_NOMEM) throw exceptions::nomem(error_code);
86+
else if(error_code == SQLITE_READONLY) throw exceptions::readonly(error_code);
87+
else if(error_code == SQLITE_INTERRUPT) throw exceptions::interrupt(error_code);
88+
else if(error_code == SQLITE_IOERR) throw exceptions::ioerr(error_code);
89+
else if(error_code == SQLITE_CORRUPT) throw exceptions::corrupt(error_code);
90+
else if(error_code == SQLITE_NOTFOUND) throw exceptions::notfound(error_code);
91+
else if(error_code == SQLITE_FULL) throw exceptions::full(error_code);
92+
else if(error_code == SQLITE_CANTOPEN) throw exceptions::cantopen(error_code);
93+
else if(error_code == SQLITE_PROTOCOL) throw exceptions::protocol(error_code);
94+
else if(error_code == SQLITE_EMPTY) throw exceptions::empty(error_code);
95+
else if(error_code == SQLITE_SCHEMA) throw exceptions::schema(error_code);
96+
else if(error_code == SQLITE_TOOBIG) throw exceptions::toobig(error_code);
97+
else if(error_code == SQLITE_CONSTRAINT) throw exceptions::constraint(error_code);
98+
else if(error_code == SQLITE_MISMATCH) throw exceptions::mismatch(error_code);
99+
else if(error_code == SQLITE_MISUSE) throw exceptions::misuse(error_code);
100+
else if(error_code == SQLITE_NOLFS) throw exceptions::nolfs(error_code);
101+
else if(error_code == SQLITE_AUTH) throw exceptions::auth(error_code);
102+
else if(error_code == SQLITE_FORMAT) throw exceptions::format(error_code);
103+
else if(error_code == SQLITE_RANGE) throw exceptions::range(error_code);
104+
else if(error_code == SQLITE_NOTADB) throw exceptions::notadb(error_code);
105+
else throw sqlite_exception(error_code);
94106
}
95107
}
96108

@@ -122,7 +134,6 @@ namespace sqlite {
122134

123135
database_binder(database_binder&& other) :
124136
_db(std::move(other._db)),
125-
_sql(std::move(other._sql)),
126137
_stmt(std::move(other._stmt)),
127138
_inx(other._inx), execution_started(other.execution_started) { }
128139

@@ -149,8 +160,8 @@ namespace sqlite {
149160

150161
private:
151162
std::shared_ptr<sqlite3> _db;
152-
std::u16string _sql;
153163
std::unique_ptr<sqlite3_stmt, decltype(&sqlite3_finalize)> _stmt;
164+
utility::UncaughtExceptionDetector _has_uncaught_exception;
154165

155166
int _inx;
156167

@@ -177,11 +188,11 @@ namespace sqlite {
177188
if((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) {
178189
call_back();
179190
} else if(hresult == SQLITE_DONE) {
180-
throw exceptions::no_rows("no rows to extract: exactly 1 row expected");
191+
throw exceptions::no_rows("no rows to extract: exactly 1 row expected", SQLITE_DONE);
181192
}
182193

183194
if((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) {
184-
throw exceptions::more_rows("not all rows extracted");
195+
throw exceptions::more_rows("not all rows extracted", SQLITE_ROW);
185196
}
186197

187198
if(hresult != SQLITE_DONE) {
@@ -198,6 +209,14 @@ namespace sqlite {
198209
return tmp;
199210
}
200211

212+
sqlite3_stmt* _prepare(const std::string& sql) {
213+
int hresult;
214+
sqlite3_stmt* tmp = nullptr;
215+
hresult = sqlite3_prepare_v2(_db.get(), sql.data(), -1, &tmp, nullptr);
216+
if((hresult) != SQLITE_OK) exceptions::throw_sqlite_error(hresult);
217+
return tmp;
218+
}
219+
201220
template <typename Type>
202221
struct is_sqlite_value : public std::integral_constant<
203222
bool,
@@ -207,8 +226,8 @@ namespace sqlite {
207226
|| std::is_same<std::u16string, Type>::value
208227
|| std::is_same<sqlite_int64, Type>::value
209228
> { };
210-
template <typename Type>
211-
struct is_sqlite_value< std::vector<Type> > : public std::integral_constant<
229+
template <typename Type, typename Allocator>
230+
struct is_sqlite_value< std::vector<Type, Allocator> > : public std::integral_constant<
212231
bool,
213232
std::is_floating_point<Type>::value
214233
|| std::is_integral<Type>::value
@@ -218,9 +237,9 @@ namespace sqlite {
218237

219238
template<typename T> friend database_binder& operator <<(database_binder& db, const T& val);
220239
template<typename T> friend void get_col_from_db(database_binder& db, int inx, T& val);
221-
/* for vector<T> support */
222-
template<typename T> friend database_binder& operator <<(database_binder& db, const std::vector<T>& val);
223-
template<typename T> friend void get_col_from_db(database_binder& db, int inx, std::vector<T>& val);
240+
/* for vector<T, A> support */
241+
template<typename T, typename A> friend database_binder& operator <<(database_binder& db, const std::vector<T, A>& val);
242+
template<typename T, typename A> friend void get_col_from_db(database_binder& db, int inx, std::vector<T, A>& val);
224243
/* for nullptr & unique_ptr support */
225244
friend database_binder& operator <<(database_binder& db, std::nullptr_t);
226245
template<typename T> friend database_binder& operator <<(database_binder& db, const std::unique_ptr<T>& val);
@@ -241,7 +260,7 @@ namespace sqlite {
241260
friend database_binder& operator <<(database_binder& db, const std::u16string& txt);
242261

243262

244-
#if __has_include(<optional>)
263+
#ifdef _MODERN_SQLITE_STD_OPTIONAL_SUPPORT
245264
template <typename OptionalT> friend database_binder& operator <<(database_binder& db, const std::optional<OptionalT>& val);
246265
template <typename OptionalT> friend void get_col_from_db(database_binder& db, int inx, std::optional<OptionalT>& o);
247266
#endif
@@ -255,18 +274,20 @@ namespace sqlite {
255274

256275
database_binder(std::shared_ptr<sqlite3> db, std::u16string const & sql):
257276
_db(db),
258-
_sql(sql),
259277
_stmt(_prepare(sql), sqlite3_finalize),
260278
_inx(1) {
261279
}
262280

263281
database_binder(std::shared_ptr<sqlite3> db, std::string const & sql):
264-
database_binder(db, std::u16string(sql.begin(), sql.end())) {}
282+
_db(db),
283+
_stmt(_prepare(sql), sqlite3_finalize),
284+
_inx(1) {
285+
}
265286

266287
~database_binder() noexcept(false) {
267288
/* Will be executed if no >>op is found, but not if an exception
268289
is in mid flight */
269-
if(!execution_started && !std::uncaught_exception() && _stmt) {
290+
if(!execution_started && !_has_uncaught_exception && _stmt) {
270291
execute();
271292
}
272293
}
@@ -310,13 +331,16 @@ namespace sqlite {
310331
auto ret = sqlite3_open16(db_name.data(), &tmp);
311332
_db = std::shared_ptr<sqlite3>(tmp, [=](sqlite3* ptr) { sqlite3_close_v2(ptr); }); // this will close the connection eventually when no longer needed.
312333
if(ret != SQLITE_OK) exceptions::throw_sqlite_error(ret);
313-
314-
315334
//_db.reset(tmp, sqlite3_close); // alternative close. (faster?)
316335
}
317336

318-
database(std::string const & db_name):
319-
database(std::u16string(db_name.begin(), db_name.end())) {}
337+
database(std::string const & db_name): _db(nullptr) {
338+
sqlite3* tmp = nullptr;
339+
auto ret = sqlite3_open(db_name.data(), &tmp);
340+
_db = std::shared_ptr<sqlite3>(tmp, [=](sqlite3* ptr) { sqlite3_close_v2(ptr); }); // this will close the connection eventually when no longer needed.
341+
if(ret != SQLITE_OK) exceptions::throw_sqlite_error(ret);
342+
//_db.reset(tmp, sqlite3_close); // alternative close. (faster?)
343+
}
320344

321345
database(std::shared_ptr<sqlite3> db):
322346
_db(db) {}
@@ -471,8 +495,8 @@ namespace sqlite {
471495
}
472496
}
473497

474-
// vector<T>
475-
template<typename T> inline database_binder& operator<<(database_binder& db, const std::vector<T>& vec) {
498+
// vector<T, A>
499+
template<typename T, typename A> inline database_binder& operator<<(database_binder& db, const std::vector<T, A>& vec) {
476500
void const* buf = reinterpret_cast<void const *>(vec.data());
477501
int bytes = vec.size() * sizeof(T);
478502
int hresult;
@@ -482,13 +506,13 @@ namespace sqlite {
482506
++db._inx;
483507
return db;
484508
}
485-
template<typename T> inline void get_col_from_db(database_binder& db, int inx, std::vector<T>& vec) {
509+
template<typename T, typename A> inline void get_col_from_db(database_binder& db, int inx, std::vector<T, A>& vec) {
486510
if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) {
487511
vec.clear();
488512
} else {
489513
int bytes = sqlite3_column_bytes(db._stmt.get(), inx);
490514
T const* buf = reinterpret_cast<T const *>(sqlite3_column_blob(db._stmt.get(), inx));
491-
vec = std::vector<T>(buf, buf + bytes/sizeof(T));
515+
vec = std::vector<T, A>(buf, buf + bytes/sizeof(T));
492516
}
493517
}
494518

@@ -565,7 +589,7 @@ namespace sqlite {
565589
return db;
566590
}
567591
// std::optional support for NULL values
568-
#if __has_include(<optional>)
592+
#ifdef _MODERN_SQLITE_STD_OPTIONAL_SUPPORT
569593
template <typename OptionalT> inline database_binder& operator <<(database_binder& db, const std::optional<OptionalT>& val) {
570594
if(val) {
571595
return operator << (std::move(db), std::move(*val));
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#pragma once
2+
3+
#include <cassert>
4+
#include <exception>
5+
#include <iostream>
6+
7+
namespace sqlite {
8+
namespace utility {
9+
#ifdef __cpp_lib_uncaught_exceptions
10+
class UncaughtExceptionDetector {
11+
public:
12+
operator bool() {
13+
return count != std::uncaught_exceptions();
14+
}
15+
private:
16+
int count = std::uncaught_exceptions();
17+
};
18+
#else
19+
class UncaughtExceptionDetector {
20+
public:
21+
operator bool() {
22+
return std::uncaught_exception();
23+
}
24+
};
25+
#endif
26+
}
27+
}

0 commit comments

Comments
 (0)