Skip to content

Commit 3cc90bb

Browse files
authored
Merge pull request #88 from zauguin/sql_stmt
Include SQL statement in exception
2 parents c74c5df + 6c99c9b commit 3cc90bb

File tree

3 files changed

+77
-49
lines changed

3 files changed

+77
-49
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,8 @@ 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.
287+
sqlite::sqlite_exception has a `get_code()` member function to get the SQLITE3 error code.
288+
Additionally you can use `get_sql()` to see the SQL statement leading to the error.
288289
289290
```c++
290291
database db(":memory:");
@@ -298,7 +299,8 @@ sqlite::sqlite_exception has a get_code() member function to get the SQLITE3 err
298299
/* if you are trying to catch all sqlite related exceptions
299300
* make sure to catch them by reference */
300301
catch (sqlite_exception& e) {
301-
cerr << e.get_code() << ": " << e.what() << endl;
302+
cerr << e.get_code() << ": " << e.what() << " during "
303+
<< e.get_sql() << endl;
302304
}
303305
/* you can catch specific exceptions as well,
304306
catch(sqlite::exceptions::constraint e) { } */

hdr/sqlite_modern_cpp.h

Lines changed: 62 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@ namespace sqlite {
3131

3232
class sqlite_exception: public std::runtime_error {
3333
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) {}
34+
sqlite_exception(const char* msg, std::string sql, int code = -1): runtime_error(msg), code(code), sql(sql) {}
35+
sqlite_exception(int code, std::string sql): runtime_error(sqlite3_errstr(code)), code(code), sql(sql) {}
3636
int get_code() {return code;}
37+
std::string get_sql() {return sql;}
3738
private:
3839
int code;
40+
std::string sql;
3941
};
4042

4143
namespace exceptions {
@@ -75,34 +77,34 @@ namespace sqlite {
7577
class more_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; };
7678
class no_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; };
7779

78-
static void throw_sqlite_error(const int& 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);
80+
static void throw_sqlite_error(const int& error_code, const std::string &sql = "") {
81+
if(error_code == SQLITE_ERROR) throw exceptions::error(error_code, sql);
82+
else if(error_code == SQLITE_INTERNAL) throw exceptions::internal (error_code, sql);
83+
else if(error_code == SQLITE_PERM) throw exceptions::perm(error_code, sql);
84+
else if(error_code == SQLITE_ABORT) throw exceptions::abort(error_code, sql);
85+
else if(error_code == SQLITE_BUSY) throw exceptions::busy(error_code, sql);
86+
else if(error_code == SQLITE_LOCKED) throw exceptions::locked(error_code, sql);
87+
else if(error_code == SQLITE_NOMEM) throw exceptions::nomem(error_code, sql);
88+
else if(error_code == SQLITE_READONLY) throw exceptions::readonly(error_code, sql);
89+
else if(error_code == SQLITE_INTERRUPT) throw exceptions::interrupt(error_code, sql);
90+
else if(error_code == SQLITE_IOERR) throw exceptions::ioerr(error_code, sql);
91+
else if(error_code == SQLITE_CORRUPT) throw exceptions::corrupt(error_code, sql);
92+
else if(error_code == SQLITE_NOTFOUND) throw exceptions::notfound(error_code, sql);
93+
else if(error_code == SQLITE_FULL) throw exceptions::full(error_code, sql);
94+
else if(error_code == SQLITE_CANTOPEN) throw exceptions::cantopen(error_code, sql);
95+
else if(error_code == SQLITE_PROTOCOL) throw exceptions::protocol(error_code, sql);
96+
else if(error_code == SQLITE_EMPTY) throw exceptions::empty(error_code, sql);
97+
else if(error_code == SQLITE_SCHEMA) throw exceptions::schema(error_code, sql);
98+
else if(error_code == SQLITE_TOOBIG) throw exceptions::toobig(error_code, sql);
99+
else if(error_code == SQLITE_CONSTRAINT) throw exceptions::constraint(error_code, sql);
100+
else if(error_code == SQLITE_MISMATCH) throw exceptions::mismatch(error_code, sql);
101+
else if(error_code == SQLITE_MISUSE) throw exceptions::misuse(error_code, sql);
102+
else if(error_code == SQLITE_NOLFS) throw exceptions::nolfs(error_code, sql);
103+
else if(error_code == SQLITE_AUTH) throw exceptions::auth(error_code, sql);
104+
else if(error_code == SQLITE_FORMAT) throw exceptions::format(error_code, sql);
105+
else if(error_code == SQLITE_RANGE) throw exceptions::range(error_code, sql);
106+
else if(error_code == SQLITE_NOTADB) throw exceptions::notadb(error_code, sql);
107+
else throw sqlite_exception(error_code, sql);
106108
}
107109
}
108110

@@ -150,10 +152,24 @@ namespace sqlite {
150152
while((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) {}
151153

152154
if(hresult != SQLITE_DONE) {
153-
exceptions::throw_sqlite_error(hresult);
155+
exceptions::throw_sqlite_error(hresult, sql());
154156
}
155157
used(true); /* prevent from executing again when goes out of scope */
156158
}
159+
160+
std::string sql() {
161+
#if SQLITE_VERSION_NUMBER >= 3014000
162+
auto sqlite_deleter = [](void *ptr) {sqlite3_free(ptr);};
163+
std::unique_ptr<char, decltype(sqlite_deleter)> str(sqlite3_expanded_sql(_stmt.get()), sqlite_deleter);
164+
return str ? str.get() : original_sql();
165+
#else
166+
return original_sql();
167+
#endif
168+
}
169+
170+
std::string original_sql() {
171+
return sqlite3_sql(_stmt.get());
172+
}
157173

158174
void used(bool state) { execution_started = state; }
159175
bool used() const { return execution_started; }
@@ -176,7 +192,7 @@ namespace sqlite {
176192
}
177193

178194
if(hresult != SQLITE_DONE) {
179-
exceptions::throw_sqlite_error(hresult);
195+
exceptions::throw_sqlite_error(hresult, sql());
180196
}
181197
reset();
182198
}
@@ -188,15 +204,15 @@ namespace sqlite {
188204
if((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) {
189205
call_back();
190206
} else if(hresult == SQLITE_DONE) {
191-
throw exceptions::no_rows("no rows to extract: exactly 1 row expected", SQLITE_DONE);
207+
throw exceptions::no_rows("no rows to extract: exactly 1 row expected", sql(), SQLITE_DONE);
192208
}
193209

194210
if((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) {
195-
throw exceptions::more_rows("not all rows extracted", SQLITE_ROW);
211+
throw exceptions::more_rows("not all rows extracted", sql(), SQLITE_ROW);
196212
}
197213

198214
if(hresult != SQLITE_DONE) {
199-
exceptions::throw_sqlite_error(hresult);
215+
exceptions::throw_sqlite_error(hresult, sql());
200216
}
201217
reset();
202218
}
@@ -213,7 +229,7 @@ namespace sqlite {
213229
int hresult;
214230
sqlite3_stmt* tmp = nullptr;
215231
hresult = sqlite3_prepare_v2(_db.get(), sql.data(), -1, &tmp, nullptr);
216-
if((hresult) != SQLITE_OK) exceptions::throw_sqlite_error(hresult);
232+
if((hresult) != SQLITE_OK) exceptions::throw_sqlite_error(hresult, sql);
217233
return tmp;
218234
}
219235

@@ -526,7 +542,7 @@ namespace sqlite {
526542
inline database_binder& operator<<(database_binder& db, const int& val) {
527543
int hresult;
528544
if((hresult = sqlite3_bind_int(db._stmt.get(), db._inx, val)) != SQLITE_OK) {
529-
exceptions::throw_sqlite_error(hresult);
545+
exceptions::throw_sqlite_error(hresult, db.sql());
530546
}
531547
++db._inx;
532548
return db;
@@ -553,7 +569,7 @@ namespace sqlite {
553569
inline database_binder& operator <<(database_binder& db, const sqlite_int64& val) {
554570
int hresult;
555571
if((hresult = sqlite3_bind_int64(db._stmt.get(), db._inx, val)) != SQLITE_OK) {
556-
exceptions::throw_sqlite_error(hresult);
572+
exceptions::throw_sqlite_error(hresult, db.sql());
557573
}
558574

559575
++db._inx;
@@ -581,7 +597,7 @@ namespace sqlite {
581597
inline database_binder& operator <<(database_binder& db, const float& val) {
582598
int hresult;
583599
if((hresult = sqlite3_bind_double(db._stmt.get(), db._inx, double(val))) != SQLITE_OK) {
584-
exceptions::throw_sqlite_error(hresult);
600+
exceptions::throw_sqlite_error(hresult, db.sql());
585601
}
586602

587603
++db._inx;
@@ -609,7 +625,7 @@ namespace sqlite {
609625
inline database_binder& operator <<(database_binder& db, const double& val) {
610626
int hresult;
611627
if((hresult = sqlite3_bind_double(db._stmt.get(), db._inx, val)) != SQLITE_OK) {
612-
exceptions::throw_sqlite_error(hresult);
628+
exceptions::throw_sqlite_error(hresult, db.sql());
613629
}
614630

615631
++db._inx;
@@ -639,7 +655,7 @@ namespace sqlite {
639655
int bytes = vec.size() * sizeof(T);
640656
int hresult;
641657
if((hresult = sqlite3_bind_blob(db._stmt.get(), db._inx, buf, bytes, SQLITE_TRANSIENT)) != SQLITE_OK) {
642-
exceptions::throw_sqlite_error(hresult);
658+
exceptions::throw_sqlite_error(hresult, db.sql());
643659
}
644660
++db._inx;
645661
return db;
@@ -672,7 +688,7 @@ namespace sqlite {
672688
inline database_binder& operator <<(database_binder& db, std::nullptr_t) {
673689
int hresult;
674690
if((hresult = sqlite3_bind_null(db._stmt.get(), db._inx)) != SQLITE_OK) {
675-
exceptions::throw_sqlite_error(hresult);
691+
exceptions::throw_sqlite_error(hresult, db.sql());
676692
}
677693
++db._inx;
678694
return db;
@@ -734,7 +750,7 @@ namespace sqlite {
734750
inline database_binder& operator <<(database_binder& db, const std::string& txt) {
735751
int hresult;
736752
if((hresult = sqlite3_bind_text(db._stmt.get(), db._inx, txt.data(), -1, SQLITE_TRANSIENT)) != SQLITE_OK) {
737-
exceptions::throw_sqlite_error(hresult);
753+
exceptions::throw_sqlite_error(hresult, db.sql());
738754
}
739755

740756
++db._inx;
@@ -765,7 +781,7 @@ namespace sqlite {
765781
inline database_binder& operator <<(database_binder& db, const std::u16string& txt) {
766782
int hresult;
767783
if((hresult = sqlite3_bind_text16(db._stmt.get(), db._inx, txt.data(), -1, SQLITE_TRANSIENT)) != SQLITE_OK) {
768-
exceptions::throw_sqlite_error(hresult);
784+
exceptions::throw_sqlite_error(hresult, db.sql());
769785
}
770786

771787
++db._inx;
@@ -782,7 +798,7 @@ namespace sqlite {
782798
}
783799
int hresult;
784800
if((hresult = sqlite3_bind_null(db._stmt.get(), db._inx)) != SQLITE_OK) {
785-
exceptions::throw_sqlite_error(hresult);
801+
exceptions::throw_sqlite_error(hresult, db.sql());
786802
}
787803

788804
++db._inx;
@@ -823,7 +839,7 @@ namespace sqlite {
823839
}
824840
int hresult;
825841
if((hresult = sqlite3_bind_null(db._stmt.get(), db._inx)) != SQLITE_OK) {
826-
exceptions::throw_sqlite_error(hresult);
842+
exceptions::throw_sqlite_error(hresult, db.sql());
827843
}
828844

829845
++db._inx;

tests/exceptions.cc

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include <iostream>
2+
#include <iomanip>
23
#include <string>
34
#include <memory>
45
#include <stdexcept>
@@ -17,8 +18,17 @@ int main() {
1718
// inserting again to produce error
1819
db << "INSERT INTO person (id,name) VALUES (?,?)" << 1 << "jack";
1920
} catch (sqlite_exception& e) {
20-
cerr << e.get_code() << ": " << e.what() << endl;
21+
cerr << e.get_code() << ": " << e.what() << " during "
22+
<< quoted(e.get_sql()) << endl;
2123
expception_thrown = true;
24+
#if SQLITE_VERSION_NUMBER >= 3014000
25+
if(e.get_sql() != "INSERT INTO person (id,name) VALUES (1,'jack')") {
26+
#else
27+
if(e.get_sql() != "INSERT INTO person (id,name) VALUES (?,?)") {
28+
#endif
29+
cerr << "Wrong statement failed\n";
30+
exit(EXIT_FAILURE);
31+
}
2232
} catch (...) {
2333
cerr << "Ok, we have our excpetion thrown" << endl;
2434
expception_thrown = true;

0 commit comments

Comments
 (0)