From 5d5ce076f28b5f58198b325026d63fb79d6f51d6 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Mon, 18 Jun 2018 12:14:13 -0600 Subject: [PATCH] Add binding mode option to database and database_binder --- hdr/sqlite_modern_cpp.h | 32 +++++++++----- hdr/sqlite_modern_cpp/type_wrapper.h | 66 ++++++++++++++++++---------- 2 files changed, 63 insertions(+), 35 deletions(-) diff --git a/hdr/sqlite_modern_cpp.h b/hdr/sqlite_modern_cpp.h index a19c9851..f2172b8c 100644 --- a/hdr/sqlite_modern_cpp.h +++ b/hdr/sqlite_modern_cpp.h @@ -27,6 +27,7 @@ namespace sqlite { typedef std::shared_ptr connection_type; class row_iterator; + class database_binder { public: @@ -38,7 +39,8 @@ namespace sqlite { database_binder(database_binder&& other) : _db(std::move(other._db)), _stmt(std::move(other._stmt)), - _inx(other._inx), execution_started(other.execution_started) { } + _inx(other._inx), execution_started(other.execution_started), + bind_mode(std::move(other.bind_mode)){ } void execute(); @@ -69,6 +71,7 @@ namespace sqlite { row_iterator end(); private: + binding_mode bind_mode; std::shared_ptr _db; std::unique_ptr _stmt; utility::UncaughtExceptionDetector _has_uncaught_exception; @@ -105,17 +108,17 @@ namespace sqlite { public: - database_binder(std::shared_ptr db, u16str_ref sql): + database_binder(std::shared_ptr db, u16str_ref sql, binding_mode bmode): _db(db), _stmt(_prepare(sql), sqlite3_finalize), - _inx(0) { - } + _inx(0), + bind_mode(bmode){} - database_binder(std::shared_ptr db, str_ref sql): + database_binder(std::shared_ptr db, str_ref sql, binding_mode bmode): _db(db), _stmt(_prepare(sql), sqlite3_finalize), - _inx(0) { - } + _inx(0), + bind_mode(bmode){} ~database_binder() noexcept(false) { /* Will be executed if no >>op is found, but not if an exception @@ -124,7 +127,10 @@ namespace sqlite { execute(); } } - + database_binder & operator << (binding_mode bmode) + { + bind_mode = bmode; + } friend class row_iterator; }; @@ -355,6 +361,7 @@ namespace sqlite { OpenFlags flags = OpenFlags::READWRITE | OpenFlags::CREATE; const char *zVfs = nullptr; Encoding encoding = Encoding::ANY; + binding_mode bind_mode = binding_mode::make_copies; }; class database { @@ -362,7 +369,8 @@ namespace sqlite { std::shared_ptr _db; public: - database(const std::string &db_name, const sqlite_config &config = {}): _db(nullptr) { + binding_mode Default_Bind_Mode; + database(const std::string &db_name, const sqlite_config &config = {}): _db(nullptr), Default_Bind_Mode(config.bind_mode){ sqlite3* tmp = nullptr; auto ret = sqlite3_open_v2(db_name.data(), &tmp, static_cast(config.flags), config.zVfs); _db = std::shared_ptr(tmp, [=](sqlite3* ptr) { sqlite3_close_v2(ptr); }); // this will close the connection eventually when no longer needed. @@ -381,11 +389,11 @@ namespace sqlite { _db(db) {} database_binder operator<<(str_ref sql) { - return database_binder(_db, sql); + return database_binder(_db, sql, Default_Bind_Mode); } database_binder operator<<(u16str_ref sql) { - return database_binder(_db, sql); + return database_binder(_db, sql, Default_Bind_Mode); } connection_type connection() const { return _db; } @@ -481,7 +489,7 @@ namespace sqlite { void inline operator++(database_binder& db, int) { db.execute(); } template database_binder &operator<<(database_binder& db, T&& val) { - int result = bind_col_in_db(db._stmt.get(), db._next_index(), std::forward(val)); + int result = bind_col_in_db(db._stmt.get(), db._next_index(), std::forward(val), db.bind_mode); if(result != SQLITE_OK) exceptions::throw_sqlite_error(result, db.sql()); return db; diff --git a/hdr/sqlite_modern_cpp/type_wrapper.h b/hdr/sqlite_modern_cpp/type_wrapper.h index e17932fe..bd70f98d 100644 --- a/hdr/sqlite_modern_cpp/type_wrapper.h +++ b/hdr/sqlite_modern_cpp/type_wrapper.h @@ -53,6 +53,11 @@ namespace sqlite #include "errors.h" namespace sqlite { + // Whether or not SQlite should make a copy of the string or blob when binding to statements. Only use dont_make_copies when you are sure the reference passed will be valid at least until after the query gets executed + enum class binding_mode : bool + { + make_copies, dont_make_copies + }; template struct has_sqlite_type : std::false_type {}; @@ -84,7 +89,7 @@ namespace sqlite { template<> struct has_sqlite_type : std::true_type {}; - inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const int& val) { + inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const int& val, binding_mode) { return sqlite3_bind_int(stmt, inx, val); } inline void store_result_in_db(sqlite3_context* db, const int& val) { @@ -103,7 +108,7 @@ namespace sqlite { template<> struct has_sqlite_type : std::true_type {}; - inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const sqlite_int64& val) { + inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const sqlite_int64& val, binding_mode) { return sqlite3_bind_int64(stmt, inx, val); } inline void store_result_in_db(sqlite3_context* db, const sqlite_int64& val) { @@ -122,7 +127,7 @@ namespace sqlite { template<> struct has_sqlite_type : std::true_type {}; - inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const float& val) { + inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const float& val, binding_mode) { return sqlite3_bind_double(stmt, inx, double(val)); } inline void store_result_in_db(sqlite3_context* db, const float& val) { @@ -141,7 +146,7 @@ namespace sqlite { template<> struct has_sqlite_type : std::true_type {}; - inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const double& val) { + inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const double& val, binding_mode) { return sqlite3_bind_double(stmt, inx, val); } inline void store_result_in_db(sqlite3_context* db, const double& val) { @@ -160,7 +165,7 @@ namespace sqlite { template<> struct has_sqlite_type : std::true_type {}; - inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, std::nullptr_t) { + inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, std::nullptr_t, binding_mode) { return sqlite3_bind_null(stmt, inx); } inline void store_result_in_db(sqlite3_context* db, std::nullptr_t) { @@ -170,13 +175,19 @@ namespace sqlite { // str_ref template<> struct has_sqlite_type : std::true_type {}; - inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, str_ref val) { - return sqlite3_bind_text(stmt, inx, val.data(), val.length(), SQLITE_TRANSIENT); + inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, str_ref val, binding_mode bmode) { + if(bmode == binding_mode::make_copies) + return sqlite3_bind_text(stmt, inx, val.data(), val.length(), SQLITE_TRANSIENT); + else + return sqlite3_bind_text(stmt, inx, val.data(), val.length(), SQLITE_STATIC); } // Convert char* to string_view to trigger op<<(..., const str_ref ) - template inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const char(&STR)[N]) { - return sqlite3_bind_text(stmt, inx, &STR[0], N-1, SQLITE_TRANSIENT); + template inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const char(&STR)[N], binding_mode bmode) { + if (bmode == binding_mode::make_copies) + return sqlite3_bind_text(stmt, inx, &STR[0], N-1, SQLITE_TRANSIENT); + else + return sqlite3_bind_text(stmt, inx, &STR[0], N - 1, SQLITE_STATIC); } inline std::string get_col_from_db(sqlite3_stmt* stmt, int inx, result_type) { @@ -194,13 +205,19 @@ namespace sqlite { // u16str_ref template<> struct has_sqlite_type : std::true_type {}; - inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, u16str_ref val) { - return sqlite3_bind_text16(stmt, inx, val.data(), sizeof(char16_t) * val.length(), SQLITE_TRANSIENT); + inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, u16str_ref val, binding_mode bmode) { + if (bmode == binding_mode::make_copies) + return sqlite3_bind_text16(stmt, inx, val.data(), sizeof(char16_t) * val.length(), SQLITE_TRANSIENT); + else + return sqlite3_bind_text16(stmt, inx, val.data(), sizeof(char16_t) * val.length(), SQLITE_STATIC); } // Convert char* to string_view to trigger op<<(..., const str_ref ) - template inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const char16_t(&STR)[N]) { - return sqlite3_bind_text16(stmt, inx, &STR[0], sizeof(char16_t) * (N-1), SQLITE_TRANSIENT); + template inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const char16_t(&STR)[N], binding_mode bmode) { + if(bmode == binding_mode::make_copies) + return sqlite3_bind_text16(stmt, inx, &STR[0],sizeof(char16_t) * (N-1), SQLITE_TRANSIENT); + else + return sqlite3_bind_text16(stmt, inx, &STR[0], sizeof(char16_t) * (N-1), SQLITE_STATIC); } inline std::u16string get_col_from_db(sqlite3_stmt* stmt, int inx, result_type) { @@ -221,8 +238,8 @@ namespace sqlite { struct has_sqlite_type::value>::type> : std::true_type {}; template::value>::type> - inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const Integral& val) { - return bind_col_in_db(stmt, inx, static_cast(val)); + inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const Integral& val, binding_mode bmode) { + return bind_col_in_db(stmt, inx, static_cast(val), bmode); } template::type>> inline void store_result_in_db(sqlite3_context* db, const Integral& val) { @@ -241,10 +258,13 @@ namespace sqlite { template struct has_sqlite_type, SQLITE_BLOB, void> : std::true_type {}; - template inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::vector& vec) { + template inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::vector& vec, binding_mode bmode) { void const* buf = reinterpret_cast(vec.data()); int bytes = vec.size() * sizeof(T); - return sqlite3_bind_blob(stmt, inx, buf, bytes, SQLITE_TRANSIENT); + if(bmode == binding_mode::make_copies) + return sqlite3_bind_blob(stmt, inx, buf, bytes, SQLITE_TRANSIENT); + else + return sqlite3_bind_blob(stmt, inx, buf, bytes, SQLITE_STATIC); } template inline void store_result_in_db(sqlite3_context* db, const std::vector& vec) { void const* buf = reinterpret_cast(vec.data()); @@ -274,8 +294,8 @@ namespace sqlite { template struct has_sqlite_type, SQLITE_NULL, void> : std::true_type {}; - template inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::unique_ptr& val) { - return val ? bind_col_in_db(stmt, inx, *val) : bind_col_in_db(stmt, inx, nullptr); + template inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::unique_ptr& val, binding_mode bmode) { + return val ? bind_col_in_db(stmt, inx, *val, bmode) : bind_col_in_db(stmt, inx, nullptr, bmode); } template inline std::unique_ptr get_col_from_db(sqlite3_stmt* stmt, int inx, result_type>) { if(sqlite3_column_type(stmt, inx) == SQLITE_NULL) { @@ -305,8 +325,8 @@ namespace sqlite { template struct has_sqlite_type, SQLITE_NULL, void> : std::true_type {}; - template inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const optional& val) { - return val ? bind_col_in_db(stmt, inx, *val) : bind_col_in_db(stmt, inx, nullptr); + template inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const optional& val, binding_mode bmode) { + return val ? bind_col_in_db(stmt, inx, *val, bmode) : bind_col_in_db(stmt, inx, nullptr, bmode); } template inline void store_result_in_db(sqlite3_context* db, const optional& val) { if(val) @@ -380,8 +400,8 @@ namespace sqlite { #endif } } - template inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::variant& val) { - return std::visit([&](auto &&opt) {return bind_col_in_db(stmt, inx, std::forward(opt));}, val); + template inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::variant& val, binding_mode bmode) { + return std::visit([&](auto &&opt) {return bind_col_in_db(stmt, inx, std::forward(opt), bmode);}, val); } template inline void store_result_in_db(sqlite3_context* db, const std::variant& val) { std::visit([&](auto &&opt) {store_result_in_db(db, std::forward(opt));}, val);