Skip to content

Commit 96b1230

Browse files
committed
Include SQL statement in exception
1 parent f9cf0fd commit 96b1230

File tree

3 files changed

+69
-49
lines changed

3 files changed

+69
-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: 58 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,13 @@ namespace sqlite {
3030

3131
class sqlite_exception: public std::runtime_error {
3232
public:
33-
sqlite_exception(const char* msg, int code = -1): runtime_error(msg), code(code) {}
34-
sqlite_exception(int code): runtime_error(sqlite3_errstr(code)), code(code) {}
33+
sqlite_exception(const char* msg, std::string sql, int code = -1): runtime_error(msg), code(code), sql(sql) {}
34+
sqlite_exception(int code, std::string sql): runtime_error(sqlite3_errstr(code)), code(code), sql(sql) {}
3535
int get_code() {return code;}
36+
std::string get_sql() {return sql;}
3637
private:
3738
int code;
39+
std::string sql;
3840
};
3941

4042
namespace exceptions {
@@ -74,34 +76,34 @@ namespace sqlite {
7476
class more_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; };
7577
class no_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; };
7678

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

@@ -149,10 +151,20 @@ namespace sqlite {
149151
while((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) {}
150152

151153
if(hresult != SQLITE_DONE) {
152-
exceptions::throw_sqlite_error(hresult);
154+
exceptions::throw_sqlite_error(hresult, sql());
153155
}
154156
used(true); /* prevent from executing again when goes out of scope */
155157
}
158+
159+
std::string sql() {
160+
auto sqlite_deleter = [](void *ptr) {sqlite3_free(ptr);};
161+
std::unique_ptr<char, decltype(sqlite_deleter)> str(sqlite3_expanded_sql(_stmt.get()), sqlite_deleter);
162+
return str ? str.get() : original_sql();
163+
}
164+
165+
std::string original_sql() {
166+
return sqlite3_sql(_stmt.get());
167+
}
156168

157169
void used(bool state) { execution_started = state; }
158170
bool used() const { return execution_started; }
@@ -174,7 +186,7 @@ namespace sqlite {
174186
}
175187

176188
if(hresult != SQLITE_DONE) {
177-
exceptions::throw_sqlite_error(hresult);
189+
exceptions::throw_sqlite_error(hresult, sql());
178190
}
179191
reset();
180192
}
@@ -186,15 +198,15 @@ namespace sqlite {
186198
if((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) {
187199
call_back();
188200
} else if(hresult == SQLITE_DONE) {
189-
throw exceptions::no_rows("no rows to extract: exactly 1 row expected", SQLITE_DONE);
201+
throw exceptions::no_rows("no rows to extract: exactly 1 row expected", sql(), SQLITE_DONE);
190202
}
191203

192204
if((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) {
193-
throw exceptions::more_rows("not all rows extracted", SQLITE_ROW);
205+
throw exceptions::more_rows("not all rows extracted", sql(), SQLITE_ROW);
194206
}
195207

196208
if(hresult != SQLITE_DONE) {
197-
exceptions::throw_sqlite_error(hresult);
209+
exceptions::throw_sqlite_error(hresult, sql());
198210
}
199211
reset();
200212
}
@@ -211,7 +223,7 @@ namespace sqlite {
211223
int hresult;
212224
sqlite3_stmt* tmp = nullptr;
213225
hresult = sqlite3_prepare_v2(_db.get(), sql.data(), -1, &tmp, nullptr);
214-
if((hresult) != SQLITE_OK) exceptions::throw_sqlite_error(hresult);
226+
if((hresult) != SQLITE_OK) exceptions::throw_sqlite_error(hresult, sql);
215227
return tmp;
216228
}
217229

@@ -416,7 +428,7 @@ namespace sqlite {
416428
inline database_binder& operator<<(database_binder& db, const int& val) {
417429
int hresult;
418430
if((hresult = sqlite3_bind_int(db._stmt.get(), db._inx, val)) != SQLITE_OK) {
419-
exceptions::throw_sqlite_error(hresult);
431+
exceptions::throw_sqlite_error(hresult, db.sql());
420432
}
421433
++db._inx;
422434
return db;
@@ -433,7 +445,7 @@ namespace sqlite {
433445
inline database_binder& operator <<(database_binder& db, const sqlite_int64& val) {
434446
int hresult;
435447
if((hresult = sqlite3_bind_int64(db._stmt.get(), db._inx, val)) != SQLITE_OK) {
436-
exceptions::throw_sqlite_error(hresult);
448+
exceptions::throw_sqlite_error(hresult, db.sql());
437449
}
438450

439451
++db._inx;
@@ -451,7 +463,7 @@ namespace sqlite {
451463
inline database_binder& operator <<(database_binder& db, const float& val) {
452464
int hresult;
453465
if((hresult = sqlite3_bind_double(db._stmt.get(), db._inx, double(val))) != SQLITE_OK) {
454-
exceptions::throw_sqlite_error(hresult);
466+
exceptions::throw_sqlite_error(hresult, db.sql());
455467
}
456468

457469
++db._inx;
@@ -469,7 +481,7 @@ namespace sqlite {
469481
inline database_binder& operator <<(database_binder& db, const double& val) {
470482
int hresult;
471483
if((hresult = sqlite3_bind_double(db._stmt.get(), db._inx, val)) != SQLITE_OK) {
472-
exceptions::throw_sqlite_error(hresult);
484+
exceptions::throw_sqlite_error(hresult, db.sql());
473485
}
474486

475487
++db._inx;
@@ -489,7 +501,7 @@ namespace sqlite {
489501
int bytes = vec.size() * sizeof(T);
490502
int hresult;
491503
if((hresult = sqlite3_bind_blob(db._stmt.get(), db._inx, buf, bytes, SQLITE_TRANSIENT)) != SQLITE_OK) {
492-
exceptions::throw_sqlite_error(hresult);
504+
exceptions::throw_sqlite_error(hresult, db.sql());
493505
}
494506
++db._inx;
495507
return db;
@@ -508,7 +520,7 @@ namespace sqlite {
508520
inline database_binder& operator <<(database_binder& db, std::nullptr_t) {
509521
int hresult;
510522
if((hresult = sqlite3_bind_null(db._stmt.get(), db._inx)) != SQLITE_OK) {
511-
exceptions::throw_sqlite_error(hresult);
523+
exceptions::throw_sqlite_error(hresult, db.sql());
512524
}
513525
++db._inx;
514526
return db;
@@ -550,7 +562,7 @@ namespace sqlite {
550562
inline database_binder& operator <<(database_binder& db, const std::string& txt) {
551563
int hresult;
552564
if((hresult = sqlite3_bind_text(db._stmt.get(), db._inx, txt.data(), -1, SQLITE_TRANSIENT)) != SQLITE_OK) {
553-
exceptions::throw_sqlite_error(hresult);
565+
exceptions::throw_sqlite_error(hresult, db.sql());
554566
}
555567

556568
++db._inx;
@@ -570,7 +582,7 @@ namespace sqlite {
570582
inline database_binder& operator <<(database_binder& db, const std::u16string& txt) {
571583
int hresult;
572584
if((hresult = sqlite3_bind_text16(db._stmt.get(), db._inx, txt.data(), -1, SQLITE_TRANSIENT)) != SQLITE_OK) {
573-
exceptions::throw_sqlite_error(hresult);
585+
exceptions::throw_sqlite_error(hresult, db.sql());
574586
}
575587

576588
++db._inx;
@@ -584,7 +596,7 @@ namespace sqlite {
584596
}
585597
int hresult;
586598
if((hresult = sqlite3_bind_null(db._stmt.get(), db._inx)) != SQLITE_OK) {
587-
exceptions::throw_sqlite_error(hresult);
599+
exceptions::throw_sqlite_error(hresult, db.sql());
588600
}
589601

590602
++db._inx;
@@ -610,7 +622,7 @@ namespace sqlite {
610622
}
611623
int hresult;
612624
if((hresult = sqlite3_bind_null(db._stmt.get(), db._inx)) != SQLITE_OK) {
613-
exceptions::throw_sqlite_error(hresult);
625+
exceptions::throw_sqlite_error(hresult, db.sql());
614626
}
615627

616628
++db._inx;

tests/exceptions.cc

Lines changed: 7 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,13 @@ 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(e.get_sql() != "INSERT INTO person (id,name) VALUES (1,'jack')") {
25+
cerr << "Wrong statement failed\n";
26+
exit(EXIT_FAILURE);
27+
}
2228
} catch (...) {
2329
cerr << "Ok, we have our excpetion thrown" << endl;
2430
expception_thrown = true;

0 commit comments

Comments
 (0)