From 5d6b00a5337259f7418ffe1fed5265b59eca3ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20Kr=C3=BCger?= Date: Sun, 14 May 2017 19:16:02 +0200 Subject: [PATCH 1/4] Auto reset after statement execution --- README.md | 9 +++------ hdr/sqlite_modern_cpp.h | 29 +++++++++++++++++------------ hdr/sqlite_modern_cpp/errors.h | 2 +- tests/prepared_statment.cc | 17 +---------------- 4 files changed, 22 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index f01f59f5..ea156859 100644 --- a/README.md +++ b/README.md @@ -117,13 +117,10 @@ ps << tmp; // But beware that it will execute on destruction if it wasn't executed! ps >> [&](int a,int b){ ... }; -// after a successfull execution the statment needs to be reset to be execute again. This will reset the bound values too! -ps.reset(); - +// after a successfull execution the statment an be executed again, but the bound values are resetted. // If you dont need the returned values you can execute it like this -ps.execute(); // the statment will not be reset! - -// there is a convinience operator to execute and reset in one go +ps.execute(); +// or like this ps++; // To disable the execution of a statment when it goes out of scope and wasn't used diff --git a/hdr/sqlite_modern_cpp.h b/hdr/sqlite_modern_cpp.h index b493aaa2..79ce0e4d 100644 --- a/hdr/sqlite_modern_cpp.h +++ b/hdr/sqlite_modern_cpp.h @@ -73,23 +73,19 @@ namespace sqlite { _stmt(std::move(other._stmt)), _inx(other._inx), execution_started(other.execution_started) { } - void reset() { - sqlite3_reset(_stmt.get()); - sqlite3_clear_bindings(_stmt.get()); - _inx = 1; + void reset[[deprecated]]() { used(false); } void execute() { int hresult; - used(true); /* prevent from executing again when goes out of scope */ + auto_reset helper(this); while((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) {} if(hresult != SQLITE_DONE) { errors::throw_sqlite_error(hresult, sql()); } - } std::string sql() { @@ -107,9 +103,6 @@ namespace sqlite { } void used(bool state) { - if(execution_started == true && state == true) { - throw errors::reexecution("Already used statement executed again! Please reset() first!",sql()); - } execution_started = state; } bool used() const { return execution_started; } @@ -123,9 +116,21 @@ namespace sqlite { bool execution_started = false; + struct auto_reset { + database_binder *binder; + auto_reset(database_binder *binder): binder(binder) { + binder->used(true); + } + ~auto_reset() { + sqlite3_reset(binder->_stmt.get()); + sqlite3_clear_bindings(binder->_stmt.get()); + binder->_inx = 1; + } + }; + void _extract(std::function call_back) { int hresult; - used(true); + auto_reset helper(this); while((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) { call_back(); @@ -138,7 +143,7 @@ namespace sqlite { void _extract_single_value(std::function call_back) { int hresult; - used(true); + auto_reset helper(this); if((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) { call_back(); @@ -892,7 +897,7 @@ namespace sqlite { #endif // Some ppl are lazy so we have a operator for proper prep. statemant handling. - void inline operator++(database_binder& db, int) { db.execute(); db.reset(); } + void inline operator++(database_binder& db, int) { db.execute(); } // Convert the rValue binder to a reference and call first op<<, its needed for the call that creates the binder (be carefull of recursion here!) template database_binder& operator << (database_binder&& db, const T& val) { return db << val; } diff --git a/hdr/sqlite_modern_cpp/errors.h b/hdr/sqlite_modern_cpp/errors.h index 60faaecc..f9a9ed4b 100644 --- a/hdr/sqlite_modern_cpp/errors.h +++ b/hdr/sqlite_modern_cpp/errors.h @@ -37,7 +37,7 @@ namespace sqlite { //Some additional errors are here for the C++ interface class more_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; }; class no_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; }; - class reexecution: public sqlite_exception { using sqlite_exception::sqlite_exception; }; // Prepared statements need to be reset before calling them again + class [[deprecated]] reexecution: public sqlite_exception { using sqlite_exception::sqlite_exception; }; // Prepared statements needed to be reset before calling them again in older versions class more_statements: public sqlite_exception { using sqlite_exception::sqlite_exception; }; // Prepared statements can only contain one statement static void throw_sqlite_error(const int& error_code, const std::string &sql = "") { diff --git a/tests/prepared_statment.cc b/tests/prepared_statment.cc index b50d356d..8080f0ca 100644 --- a/tests/prepared_statment.cc +++ b/tests/prepared_statment.cc @@ -16,10 +16,8 @@ int main() { pps >> test; // execute statement - pps.reset(); - pps << 4; // bind a rvalue - pps++; // and execute and reset + pps++; // and execute pps << 8 >> test; @@ -81,22 +79,9 @@ int main() { auto prep = db << "select ?"; prep << 5; - prep.execute(); - try { - prep.execute(); - exit(EXIT_FAILURE); - } catch(errors::reexecution& ex) { - // Thats ok here - } catch(...) { - exit(EXIT_FAILURE); - } - - prep.reset(); - prep << 6; prep.execute(); - } From 2b3746034b82b4f5a9ab18c983d97f252938f06c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20Kr=C3=BCger?= Date: Sun, 14 May 2017 21:46:01 +0200 Subject: [PATCH 2/4] Avoid unnecessary resets --- hdr/sqlite_modern_cpp.h | 68 ++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/hdr/sqlite_modern_cpp.h b/hdr/sqlite_modern_cpp.h index 79ce0e4d..16b4cd0b 100644 --- a/hdr/sqlite_modern_cpp.h +++ b/hdr/sqlite_modern_cpp.h @@ -74,12 +74,14 @@ namespace sqlite { _inx(other._inx), execution_started(other.execution_started) { } void reset[[deprecated]]() { + used(true); + _inx = 0; used(false); } void execute() { + _start_execute(); int hresult; - auto_reset helper(this); while((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) {} @@ -103,6 +105,11 @@ namespace sqlite { } void used(bool state) { + if(!state) { + // We may have to reset first if we haven't done so already: + _next_index(); + --_inx; + } execution_started = state; } bool used() const { return execution_started; } @@ -116,21 +123,22 @@ namespace sqlite { bool execution_started = false; - struct auto_reset { - database_binder *binder; - auto_reset(database_binder *binder): binder(binder) { - binder->used(true); - } - ~auto_reset() { - sqlite3_reset(binder->_stmt.get()); - sqlite3_clear_bindings(binder->_stmt.get()); - binder->_inx = 1; + int _next_index() { + if(execution_started && !_inx) { + sqlite3_reset(_stmt.get()); + sqlite3_clear_bindings(_stmt.get()); } - }; + return ++_inx; + } + void _start_execute() { + _next_index(); + _inx = 0; + used(true); + } void _extract(std::function call_back) { int hresult; - auto_reset helper(this); + _start_execute(); while((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) { call_back(); @@ -143,7 +151,7 @@ namespace sqlite { void _extract_single_value(std::function call_back) { int hresult; - auto_reset helper(this); + _start_execute(); if((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) { call_back(); @@ -248,13 +256,13 @@ namespace sqlite { database_binder(std::shared_ptr db, std::u16string const & sql): _db(db), _stmt(_prepare(sql), sqlite3_finalize), - _inx(1) { + _inx(0) { } database_binder(std::shared_ptr db, std::string const & sql): _db(db), _stmt(_prepare(sql), sqlite3_finalize), - _inx(1) { + _inx(0) { } ~database_binder() noexcept(false) { @@ -520,10 +528,9 @@ namespace sqlite { // int inline database_binder& operator<<(database_binder& db, const int& val) { int hresult; - if((hresult = sqlite3_bind_int(db._stmt.get(), db._inx, val)) != SQLITE_OK) { + if((hresult = sqlite3_bind_int(db._stmt.get(), db._next_index(), val)) != SQLITE_OK) { errors::throw_sqlite_error(hresult, db.sql()); } - ++db._inx; return db; } inline void store_result_in_db(sqlite3_context* db, const int& val) { @@ -547,11 +554,10 @@ namespace sqlite { // sqlite_int64 inline database_binder& operator <<(database_binder& db, const sqlite_int64& val) { int hresult; - if((hresult = sqlite3_bind_int64(db._stmt.get(), db._inx, val)) != SQLITE_OK) { + if((hresult = sqlite3_bind_int64(db._stmt.get(), db._next_index(), val)) != SQLITE_OK) { errors::throw_sqlite_error(hresult, db.sql()); } - ++db._inx; return db; } inline void store_result_in_db(sqlite3_context* db, const sqlite_int64& val) { @@ -575,11 +581,10 @@ namespace sqlite { // float inline database_binder& operator <<(database_binder& db, const float& val) { int hresult; - if((hresult = sqlite3_bind_double(db._stmt.get(), db._inx, double(val))) != SQLITE_OK) { + if((hresult = sqlite3_bind_double(db._stmt.get(), db._next_index(), double(val))) != SQLITE_OK) { errors::throw_sqlite_error(hresult, db.sql()); } - ++db._inx; return db; } inline void store_result_in_db(sqlite3_context* db, const float& val) { @@ -603,11 +608,10 @@ namespace sqlite { // double inline database_binder& operator <<(database_binder& db, const double& val) { int hresult; - if((hresult = sqlite3_bind_double(db._stmt.get(), db._inx, val)) != SQLITE_OK) { + if((hresult = sqlite3_bind_double(db._stmt.get(), db._next_index(), val)) != SQLITE_OK) { errors::throw_sqlite_error(hresult, db.sql()); } - ++db._inx; return db; } inline void store_result_in_db(sqlite3_context* db, const double& val) { @@ -633,10 +637,9 @@ namespace sqlite { void const* buf = reinterpret_cast(vec.data()); int bytes = vec.size() * sizeof(T); int hresult; - if((hresult = sqlite3_bind_blob(db._stmt.get(), db._inx, buf, bytes, SQLITE_TRANSIENT)) != SQLITE_OK) { + if((hresult = sqlite3_bind_blob(db._stmt.get(), db._next_index(), buf, bytes, SQLITE_TRANSIENT)) != SQLITE_OK) { errors::throw_sqlite_error(hresult, db.sql()); } - ++db._inx; return db; } template inline void store_result_in_db(sqlite3_context* db, const std::vector& vec) { @@ -666,10 +669,9 @@ namespace sqlite { /* for nullptr support */ inline database_binder& operator <<(database_binder& db, std::nullptr_t) { int hresult; - if((hresult = sqlite3_bind_null(db._stmt.get(), db._inx)) != SQLITE_OK) { + if((hresult = sqlite3_bind_null(db._stmt.get(), db._next_index())) != SQLITE_OK) { errors::throw_sqlite_error(hresult, db.sql()); } - ++db._inx; return db; } inline void store_result_in_db(sqlite3_context* db, std::nullptr_t) { @@ -728,11 +730,10 @@ namespace sqlite { inline database_binder& operator <<(database_binder& db, const std::string& txt) { int hresult; - if((hresult = sqlite3_bind_text(db._stmt.get(), db._inx, txt.data(), -1, SQLITE_TRANSIENT)) != SQLITE_OK) { + if((hresult = sqlite3_bind_text(db._stmt.get(), db._next_index(), txt.data(), -1, SQLITE_TRANSIENT)) != SQLITE_OK) { errors::throw_sqlite_error(hresult, db.sql()); } - ++db._inx; return db; } inline void store_result_in_db(sqlite3_context* db, const std::string& val) { @@ -759,11 +760,10 @@ namespace sqlite { inline database_binder& operator <<(database_binder& db, const std::u16string& txt) { int hresult; - if((hresult = sqlite3_bind_text16(db._stmt.get(), db._inx, txt.data(), -1, SQLITE_TRANSIENT)) != SQLITE_OK) { + if((hresult = sqlite3_bind_text16(db._stmt.get(), db._next_index(), txt.data(), -1, SQLITE_TRANSIENT)) != SQLITE_OK) { errors::throw_sqlite_error(hresult, db.sql()); } - ++db._inx; return db; } inline void store_result_in_db(sqlite3_context* db, const std::u16string& val) { @@ -799,11 +799,10 @@ namespace sqlite { return operator << (std::move(db), std::move(*val)); } int hresult; - if((hresult = sqlite3_bind_null(db._stmt.get(), db._inx)) != SQLITE_OK) { + if((hresult = sqlite3_bind_null(db._stmt.get(), db._next_index())) != SQLITE_OK) { errors::throw_sqlite_error(hresult, db.sql()); } - ++db._inx; return db; } template inline void store_result_in_db(sqlite3_context* db, const std::optional& val) { @@ -840,11 +839,10 @@ namespace sqlite { return operator << (std::move(db), std::move(*val)); } int hresult; - if((hresult = sqlite3_bind_null(db._stmt.get(), db._inx)) != SQLITE_OK) { + if((hresult = sqlite3_bind_null(db._stmt.get(), db._next_index())) != SQLITE_OK) { errors::throw_sqlite_error(hresult, db.sql()); } - ++db._inx; return db; } template inline void store_result_in_db(sqlite3_context* db, const boost::optional& val) { From 79d159dfa67a9b438f4b068852441731433641c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20Kr=C3=BCger?= Date: Sun, 14 May 2017 21:50:25 +0200 Subject: [PATCH 3/4] Fix typo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ea156859..39920769 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ ps << tmp; // But beware that it will execute on destruction if it wasn't executed! ps >> [&](int a,int b){ ... }; -// after a successfull execution the statment an be executed again, but the bound values are resetted. +// after a successfull execution the statment can be executed again, but the bound values are resetted. // If you dont need the returned values you can execute it like this ps.execute(); // or like this From 05ea5a8ee9cc68b68d73533781f77fca033e9f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20Kr=C3=BCger?= Date: Sun, 14 May 2017 21:56:18 +0200 Subject: [PATCH 4/4] Remoe `reset` and `reexecution` --- hdr/sqlite_modern_cpp.h | 6 ------ hdr/sqlite_modern_cpp/errors.h | 1 - 2 files changed, 7 deletions(-) diff --git a/hdr/sqlite_modern_cpp.h b/hdr/sqlite_modern_cpp.h index 16b4cd0b..c553d278 100644 --- a/hdr/sqlite_modern_cpp.h +++ b/hdr/sqlite_modern_cpp.h @@ -73,12 +73,6 @@ namespace sqlite { _stmt(std::move(other._stmt)), _inx(other._inx), execution_started(other.execution_started) { } - void reset[[deprecated]]() { - used(true); - _inx = 0; - used(false); - } - void execute() { _start_execute(); int hresult; diff --git a/hdr/sqlite_modern_cpp/errors.h b/hdr/sqlite_modern_cpp/errors.h index f9a9ed4b..6c75b7ae 100644 --- a/hdr/sqlite_modern_cpp/errors.h +++ b/hdr/sqlite_modern_cpp/errors.h @@ -37,7 +37,6 @@ namespace sqlite { //Some additional errors are here for the C++ interface class more_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; }; class no_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; }; - class [[deprecated]] reexecution: public sqlite_exception { using sqlite_exception::sqlite_exception; }; // Prepared statements needed to be reset before calling them again in older versions class more_statements: public sqlite_exception { using sqlite_exception::sqlite_exception; }; // Prepared statements can only contain one statement static void throw_sqlite_error(const int& error_code, const std::string &sql = "") {