Skip to content

Commit e05aa4b

Browse files
authored
Merge pull request #110 from zauguin/extended_errors
Extended errors
2 parents ef55b8e + 0a78e1d commit e05aa4b

File tree

11 files changed

+238
-126
lines changed

11 files changed

+238
-126
lines changed

README.md

+5-3
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,8 @@ To support all possible values, you can use `variant<nullptr_t, sqlite_int64, do
377377
Errors
378378
----
379379

380-
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.
381-
sqlite::sqlite_exception has a `get_code()` member function to get the SQLITE3 error code.
380+
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::errors::constraint. SQLite3 extended error names are supported too. So there is e.g. a class sqlite::errors::constraint_primarykey derived from sqlite::errors::constraint. Note that all errors are derived from sqlite::sqlite_exception and that itself is derived from std::runtime_exception.
381+
sqlite::sqlite_exception has a `get_code()` member function to get the SQLITE3 error code or `get_extended_code()` to get the extended error code.
382382
Additionally you can use `get_sql()` to see the SQL statement leading to the error.
383383

384384
```c++
@@ -397,7 +397,9 @@ Additionally you can use `get_sql()` to see the SQL statement leading to the err
397397
<< e.get_sql() << endl;
398398
}
399399
/* you can catch specific exceptions as well,
400-
catch(sqlite::exceptions::constraint e) { } */
400+
catch(sqlite::errors::constraint e) { } */
401+
/* and even more specific exceptions
402+
catch(sqlite::errors::constraint_primarykey e) { } */
401403
```
402404
403405
Custom SQL functions

hdr/sqlite_modern_cpp.h

+25-107
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
#include <cctype>
55
#include <string>
66
#include <functional>
7-
#include <stdexcept>
87
#include <ctime>
98
#include <tuple>
109
#include <memory>
@@ -34,93 +33,10 @@
3433

3534
#include <sqlite3.h>
3635

36+
#include "sqlite_modern_cpp/errors.h"
3737
#include "sqlite_modern_cpp/utility/function_traits.h"
3838
#include "sqlite_modern_cpp/utility/uncaught_exceptions.h"
3939

40-
namespace sqlite {
41-
42-
class sqlite_exception: public std::runtime_error {
43-
public:
44-
sqlite_exception(const char* msg, std::string sql, int code = -1): runtime_error(msg), code(code), sql(sql) {}
45-
sqlite_exception(int code, std::string sql): runtime_error(sqlite3_errstr(code)), code(code), sql(sql) {}
46-
int get_code() const {return code;}
47-
std::string get_sql() const {return sql;}
48-
private:
49-
int code;
50-
std::string sql;
51-
};
52-
53-
namespace exceptions {
54-
//One more or less trivial derived error class for each SQLITE error.
55-
//Note the following are not errors so have no classes:
56-
//SQLITE_OK, SQLITE_NOTICE, SQLITE_WARNING, SQLITE_ROW, SQLITE_DONE
57-
//
58-
//Note these names are exact matches to the names of the SQLITE error codes.
59-
class error: public sqlite_exception { using sqlite_exception::sqlite_exception; };
60-
class internal: public sqlite_exception{ using sqlite_exception::sqlite_exception; };
61-
class perm: public sqlite_exception { using sqlite_exception::sqlite_exception; };
62-
class abort: public sqlite_exception { using sqlite_exception::sqlite_exception; };
63-
class busy: public sqlite_exception { using sqlite_exception::sqlite_exception; };
64-
class locked: public sqlite_exception { using sqlite_exception::sqlite_exception; };
65-
class nomem: public sqlite_exception { using sqlite_exception::sqlite_exception; };
66-
class readonly: public sqlite_exception { using sqlite_exception::sqlite_exception; };
67-
class interrupt: public sqlite_exception { using sqlite_exception::sqlite_exception; };
68-
class ioerr: public sqlite_exception { using sqlite_exception::sqlite_exception; };
69-
class corrupt: public sqlite_exception { using sqlite_exception::sqlite_exception; };
70-
class notfound: public sqlite_exception { using sqlite_exception::sqlite_exception; };
71-
class full: public sqlite_exception { using sqlite_exception::sqlite_exception; };
72-
class cantopen: public sqlite_exception { using sqlite_exception::sqlite_exception; };
73-
class protocol: public sqlite_exception { using sqlite_exception::sqlite_exception; };
74-
class empty: public sqlite_exception { using sqlite_exception::sqlite_exception; };
75-
class schema: public sqlite_exception { using sqlite_exception::sqlite_exception; };
76-
class toobig: public sqlite_exception { using sqlite_exception::sqlite_exception; };
77-
class constraint: public sqlite_exception { using sqlite_exception::sqlite_exception; };
78-
class mismatch: public sqlite_exception { using sqlite_exception::sqlite_exception; };
79-
class misuse: public sqlite_exception { using sqlite_exception::sqlite_exception; };
80-
class nolfs: public sqlite_exception { using sqlite_exception::sqlite_exception; };
81-
class auth: public sqlite_exception { using sqlite_exception::sqlite_exception; };
82-
class format: public sqlite_exception { using sqlite_exception::sqlite_exception; };
83-
class range: public sqlite_exception { using sqlite_exception::sqlite_exception; };
84-
class notadb: public sqlite_exception { using sqlite_exception::sqlite_exception; };
85-
86-
//Some additional errors are here for the C++ interface
87-
class more_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; };
88-
class no_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; };
89-
class reexecution: public sqlite_exception { using sqlite_exception::sqlite_exception; }; // Prepared statements need to be reset before calling them again
90-
class more_statements: public sqlite_exception { using sqlite_exception::sqlite_exception; }; // Prepared statements can only contain one statement
91-
92-
static void throw_sqlite_error(const int& error_code, const std::string &sql = "") {
93-
if(error_code == SQLITE_ERROR) throw exceptions::error(error_code, sql);
94-
else if(error_code == SQLITE_INTERNAL) throw exceptions::internal(error_code, sql);
95-
else if(error_code == SQLITE_PERM) throw exceptions::perm(error_code, sql);
96-
else if(error_code == SQLITE_ABORT) throw exceptions::abort(error_code, sql);
97-
else if(error_code == SQLITE_BUSY) throw exceptions::busy(error_code, sql);
98-
else if(error_code == SQLITE_LOCKED) throw exceptions::locked(error_code, sql);
99-
else if(error_code == SQLITE_NOMEM) throw exceptions::nomem(error_code, sql);
100-
else if(error_code == SQLITE_READONLY) throw exceptions::readonly(error_code, sql);
101-
else if(error_code == SQLITE_INTERRUPT) throw exceptions::interrupt(error_code, sql);
102-
else if(error_code == SQLITE_IOERR) throw exceptions::ioerr(error_code, sql);
103-
else if(error_code == SQLITE_CORRUPT) throw exceptions::corrupt(error_code, sql);
104-
else if(error_code == SQLITE_NOTFOUND) throw exceptions::notfound(error_code, sql);
105-
else if(error_code == SQLITE_FULL) throw exceptions::full(error_code, sql);
106-
else if(error_code == SQLITE_CANTOPEN) throw exceptions::cantopen(error_code, sql);
107-
else if(error_code == SQLITE_PROTOCOL) throw exceptions::protocol(error_code, sql);
108-
else if(error_code == SQLITE_EMPTY) throw exceptions::empty(error_code, sql);
109-
else if(error_code == SQLITE_SCHEMA) throw exceptions::schema(error_code, sql);
110-
else if(error_code == SQLITE_TOOBIG) throw exceptions::toobig(error_code, sql);
111-
else if(error_code == SQLITE_CONSTRAINT) throw exceptions::constraint(error_code, sql);
112-
else if(error_code == SQLITE_MISMATCH) throw exceptions::mismatch(error_code, sql);
113-
else if(error_code == SQLITE_MISUSE) throw exceptions::misuse(error_code, sql);
114-
else if(error_code == SQLITE_NOLFS) throw exceptions::nolfs(error_code, sql);
115-
else if(error_code == SQLITE_AUTH) throw exceptions::auth(error_code, sql);
116-
else if(error_code == SQLITE_FORMAT) throw exceptions::format(error_code, sql);
117-
else if(error_code == SQLITE_RANGE) throw exceptions::range(error_code, sql);
118-
else if(error_code == SQLITE_NOTADB) throw exceptions::notadb(error_code, sql);
119-
else throw sqlite_exception(error_code, sql);
120-
}
121-
}
122-
}
123-
12440
#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
12541
#include "sqlite_modern_cpp/utility/variant.h"
12642
#endif
@@ -171,7 +87,7 @@ namespace sqlite {
17187
while((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) {}
17288

17389
if(hresult != SQLITE_DONE) {
174-
exceptions::throw_sqlite_error(hresult, sql());
90+
errors::throw_sqlite_error(hresult, sql());
17591
}
17692

17793
}
@@ -192,7 +108,7 @@ namespace sqlite {
192108

193109
void used(bool state) {
194110
if(execution_started == true && state == true) {
195-
throw exceptions::reexecution("Already used statement executed again! Please reset() first!",sql());
111+
throw errors::reexecution("Already used statement executed again! Please reset() first!",sql());
196112
}
197113
execution_started = state;
198114
}
@@ -216,7 +132,7 @@ namespace sqlite {
216132
}
217133

218134
if(hresult != SQLITE_DONE) {
219-
exceptions::throw_sqlite_error(hresult, sql());
135+
errors::throw_sqlite_error(hresult, sql());
220136
}
221137
}
222138

@@ -227,15 +143,15 @@ namespace sqlite {
227143
if((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) {
228144
call_back();
229145
} else if(hresult == SQLITE_DONE) {
230-
throw exceptions::no_rows("no rows to extract: exactly 1 row expected", sql(), SQLITE_DONE);
146+
throw errors::no_rows("no rows to extract: exactly 1 row expected", sql(), SQLITE_DONE);
231147
}
232148

233149
if((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) {
234-
throw exceptions::more_rows("not all rows extracted", sql(), SQLITE_ROW);
150+
throw errors::more_rows("not all rows extracted", sql(), SQLITE_ROW);
235151
}
236152

237153
if(hresult != SQLITE_DONE) {
238-
exceptions::throw_sqlite_error(hresult, sql());
154+
errors::throw_sqlite_error(hresult, sql());
239155
}
240156
}
241157

@@ -254,9 +170,9 @@ namespace sqlite {
254170
sqlite3_stmt* tmp = nullptr;
255171
const char *remaining;
256172
hresult = sqlite3_prepare_v2(_db.get(), sql.data(), -1, &tmp, &remaining);
257-
if(hresult != SQLITE_OK) exceptions::throw_sqlite_error(hresult, sql);
173+
if(hresult != SQLITE_OK) errors::throw_sqlite_error(hresult, sql);
258174
if(!std::all_of(remaining, sql.data() + sql.size(), [](char ch) {return std::isblank(ch);}))
259-
throw exceptions::more_statements("Multiple semicolon separated statements are unsupported", sql);
175+
throw errors::more_statements("Multiple semicolon separated statements are unsupported", sql);
260176
return tmp;
261177
}
262178

@@ -470,7 +386,8 @@ namespace sqlite {
470386
sqlite3* tmp = nullptr;
471387
auto ret = sqlite3_open_v2(db_name.data(), &tmp, static_cast<int>(config.flags), config.zVfs);
472388
_db = std::shared_ptr<sqlite3>(tmp, [=](sqlite3* ptr) { sqlite3_close_v2(ptr); }); // this will close the connection eventually when no longer needed.
473-
if(ret != SQLITE_OK) exceptions::throw_sqlite_error(ret);
389+
if(ret != SQLITE_OK) errors::throw_sqlite_error(_db ? sqlite3_extended_errcode(_db.get()) : ret);
390+
sqlite3_extended_result_codes(_db.get(), true);
474391
if(config.encoding == Encoding::UTF16)
475392
*this << R"(PRAGMA encoding = "UTF-16";)";
476393
}
@@ -484,7 +401,8 @@ namespace sqlite {
484401
sqlite3* tmp = nullptr;
485402
auto ret = sqlite3_open_v2(db_name_utf8.data(), &tmp, static_cast<int>(config.flags), config.zVfs);
486403
_db = std::shared_ptr<sqlite3>(tmp, [=](sqlite3* ptr) { sqlite3_close_v2(ptr); }); // this will close the connection eventually when no longer needed.
487-
if(ret != SQLITE_OK) exceptions::throw_sqlite_error(ret);
404+
if(ret != SQLITE_OK) errors::throw_sqlite_error(_db ? sqlite3_extended_errcode(_db.get()) : ret);
405+
sqlite3_extended_result_codes(_db.get(), true);
488406
if(config.encoding != Encoding::UTF8)
489407
*this << R"(PRAGMA encoding = "UTF-16";)";
490408
}
@@ -525,7 +443,7 @@ namespace sqlite {
525443
nullptr, nullptr, [](void* ptr){
526444
delete static_cast<decltype(funcPtr)>(ptr);
527445
}))
528-
exceptions::throw_sqlite_error(result);
446+
errors::throw_sqlite_error(result);
529447
}
530448

531449
template <typename StepFunction, typename FinalFunction>
@@ -541,7 +459,7 @@ namespace sqlite {
541459
[](void* ptr){
542460
delete static_cast<decltype(funcPtr)>(ptr);
543461
}))
544-
exceptions::throw_sqlite_error(result);
462+
errors::throw_sqlite_error(result);
545463
}
546464

547465
};
@@ -598,7 +516,7 @@ namespace sqlite {
598516
inline database_binder& operator<<(database_binder& db, const int& val) {
599517
int hresult;
600518
if((hresult = sqlite3_bind_int(db._stmt.get(), db._inx, val)) != SQLITE_OK) {
601-
exceptions::throw_sqlite_error(hresult, db.sql());
519+
errors::throw_sqlite_error(hresult, db.sql());
602520
}
603521
++db._inx;
604522
return db;
@@ -625,7 +543,7 @@ namespace sqlite {
625543
inline database_binder& operator <<(database_binder& db, const sqlite_int64& val) {
626544
int hresult;
627545
if((hresult = sqlite3_bind_int64(db._stmt.get(), db._inx, val)) != SQLITE_OK) {
628-
exceptions::throw_sqlite_error(hresult, db.sql());
546+
errors::throw_sqlite_error(hresult, db.sql());
629547
}
630548

631549
++db._inx;
@@ -653,7 +571,7 @@ namespace sqlite {
653571
inline database_binder& operator <<(database_binder& db, const float& val) {
654572
int hresult;
655573
if((hresult = sqlite3_bind_double(db._stmt.get(), db._inx, double(val))) != SQLITE_OK) {
656-
exceptions::throw_sqlite_error(hresult, db.sql());
574+
errors::throw_sqlite_error(hresult, db.sql());
657575
}
658576

659577
++db._inx;
@@ -681,7 +599,7 @@ namespace sqlite {
681599
inline database_binder& operator <<(database_binder& db, const double& val) {
682600
int hresult;
683601
if((hresult = sqlite3_bind_double(db._stmt.get(), db._inx, val)) != SQLITE_OK) {
684-
exceptions::throw_sqlite_error(hresult, db.sql());
602+
errors::throw_sqlite_error(hresult, db.sql());
685603
}
686604

687605
++db._inx;
@@ -711,7 +629,7 @@ namespace sqlite {
711629
int bytes = vec.size() * sizeof(T);
712630
int hresult;
713631
if((hresult = sqlite3_bind_blob(db._stmt.get(), db._inx, buf, bytes, SQLITE_TRANSIENT)) != SQLITE_OK) {
714-
exceptions::throw_sqlite_error(hresult, db.sql());
632+
errors::throw_sqlite_error(hresult, db.sql());
715633
}
716634
++db._inx;
717635
return db;
@@ -744,7 +662,7 @@ namespace sqlite {
744662
inline database_binder& operator <<(database_binder& db, std::nullptr_t) {
745663
int hresult;
746664
if((hresult = sqlite3_bind_null(db._stmt.get(), db._inx)) != SQLITE_OK) {
747-
exceptions::throw_sqlite_error(hresult, db.sql());
665+
errors::throw_sqlite_error(hresult, db.sql());
748666
}
749667
++db._inx;
750668
return db;
@@ -806,7 +724,7 @@ namespace sqlite {
806724
inline database_binder& operator <<(database_binder& db, const std::string& txt) {
807725
int hresult;
808726
if((hresult = sqlite3_bind_text(db._stmt.get(), db._inx, txt.data(), -1, SQLITE_TRANSIENT)) != SQLITE_OK) {
809-
exceptions::throw_sqlite_error(hresult, db.sql());
727+
errors::throw_sqlite_error(hresult, db.sql());
810728
}
811729

812730
++db._inx;
@@ -837,7 +755,7 @@ namespace sqlite {
837755
inline database_binder& operator <<(database_binder& db, const std::u16string& txt) {
838756
int hresult;
839757
if((hresult = sqlite3_bind_text16(db._stmt.get(), db._inx, txt.data(), -1, SQLITE_TRANSIENT)) != SQLITE_OK) {
840-
exceptions::throw_sqlite_error(hresult, db.sql());
758+
errors::throw_sqlite_error(hresult, db.sql());
841759
}
842760

843761
++db._inx;
@@ -877,7 +795,7 @@ namespace sqlite {
877795
}
878796
int hresult;
879797
if((hresult = sqlite3_bind_null(db._stmt.get(), db._inx)) != SQLITE_OK) {
880-
exceptions::throw_sqlite_error(hresult, db.sql());
798+
errors::throw_sqlite_error(hresult, db.sql());
881799
}
882800

883801
++db._inx;
@@ -918,7 +836,7 @@ namespace sqlite {
918836
}
919837
int hresult;
920838
if((hresult = sqlite3_bind_null(db._stmt.get(), db._inx)) != SQLITE_OK) {
921-
exceptions::throw_sqlite_error(hresult, db.sql());
839+
errors::throw_sqlite_error(hresult, db.sql());
922840
}
923841

924842
++db._inx;

hdr/sqlite_modern_cpp/errors.h

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#pragma once
2+
3+
#include <string>
4+
#include <stdexcept>
5+
6+
#include <sqlite3.h>
7+
8+
namespace sqlite {
9+
10+
class sqlite_exception: public std::runtime_error {
11+
public:
12+
sqlite_exception(const char* msg, std::string sql, int code = -1): runtime_error(msg), code(code), sql(sql) {}
13+
sqlite_exception(int code, std::string sql): runtime_error(sqlite3_errstr(code)), code(code), sql(sql) {}
14+
int get_code() const {return code & 0xFF;}
15+
int get_extended_code() const {return code;}
16+
std::string get_sql() const {return sql;}
17+
private:
18+
int code;
19+
std::string sql;
20+
};
21+
22+
namespace errors {
23+
//One more or less trivial derived error class for each SQLITE error.
24+
//Note the following are not errors so have no classes:
25+
//SQLITE_OK, SQLITE_NOTICE, SQLITE_WARNING, SQLITE_ROW, SQLITE_DONE
26+
//
27+
//Note these names are exact matches to the names of the SQLITE error codes.
28+
#define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived) \
29+
class name: public sqlite_exception { using sqlite_exception::sqlite_exception; };\
30+
derived
31+
#define SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BASE,SUB,base,sub) \
32+
class base ## _ ## sub: public base { using base::base; };
33+
#include "lists/error_codes.h"
34+
#undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED
35+
#undef SQLITE_MODERN_CPP_ERROR_CODE
36+
37+
//Some additional errors are here for the C++ interface
38+
class more_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; };
39+
class no_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; };
40+
class reexecution: public sqlite_exception { using sqlite_exception::sqlite_exception; }; // Prepared statements need to be reset before calling them again
41+
class more_statements: public sqlite_exception { using sqlite_exception::sqlite_exception; }; // Prepared statements can only contain one statement
42+
43+
static void throw_sqlite_error(const int& error_code, const std::string &sql = "") {
44+
switch(error_code & 0xFF) {
45+
#define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived) \
46+
case SQLITE_ ## NAME: switch(error_code) { \
47+
derived \
48+
default: throw name(error_code, sql); \
49+
}
50+
#define SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BASE,SUB,base,sub) \
51+
case SQLITE_ ## BASE ## _ ## SUB: throw base ## _ ## sub(error_code, sql);
52+
#include "lists/error_codes.h"
53+
#undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED
54+
#undef SQLITE_MODERN_CPP_ERROR_CODE
55+
default: throw sqlite_exception(error_code, sql);
56+
}
57+
}
58+
}
59+
namespace exceptions = errors;
60+
}

0 commit comments

Comments
 (0)