diff options
author | Adam Szmigin <smidge@xsco.net> | 2020-08-09 23:36:31 +0100 |
---|---|---|
committer | Adam Szmigin <smidge@xsco.net> | 2020-08-09 23:36:31 +0100 |
commit | 37c10fed1775b9e88217be368b719847f43bd265 (patch) | |
tree | e00cc0d0a3707c60e85e599f095ffad451bef3a3 | |
parent | 1814f491cfc68cc00215fecbb990ace3dc0390e7 (diff) |
Upgrade sqlite_modern_cpp to latest version
4 files changed, 127 insertions, 74 deletions
diff --git a/ext/sqlite_modern_cpp/sqlite_modern_cpp.h b/ext/sqlite_modern_cpp/sqlite_modern_cpp.h index b493aaa..0d8065b 100644 --- a/ext/sqlite_modern_cpp/sqlite_modern_cpp.h +++ b/ext/sqlite_modern_cpp/sqlite_modern_cpp.h @@ -8,12 +8,14 @@ #include <tuple> #include <memory> #include <vector> -#include <locale> -#include <codecvt> + +#define MODERN_SQLITE_VERSION 3002008 #ifdef __has_include #if __cplusplus > 201402 && __has_include(<optional>) #define MODERN_SQLITE_STD_OPTIONAL_SUPPORT +#elif __has_include(<experimental/optional>) +#define MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT #endif #endif @@ -27,6 +29,11 @@ #include <optional> #endif +#ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT +#include <experimental/optional> +#define MODERN_SQLITE_STD_OPTIONAL_SUPPORT +#endif + #ifdef _MODERN_SQLITE_BOOST_OPTIONAL_SUPPORT #include <boost/optional.hpp> #endif @@ -36,12 +43,25 @@ #include "sqlite_modern_cpp/errors.h" #include "sqlite_modern_cpp/utility/function_traits.h" #include "sqlite_modern_cpp/utility/uncaught_exceptions.h" +#include "sqlite_modern_cpp/utility/utf16_utf8.h" #ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT #include "sqlite_modern_cpp/utility/variant.h" #endif namespace sqlite { + + // std::optional support for NULL values + #ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT + #ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT + template<class T> + using optional = std::experimental::optional<T>; + #else + template<class T> + using optional = std::optional<T>; + #endif + #endif + class database; class database_binder; @@ -73,23 +93,15 @@ 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; - used(false); - } - void execute() { + _start_execute(); int hresult; - used(true); /* prevent from executing again when goes out of scope */ while((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) {} if(hresult != SQLITE_DONE) { errors::throw_sqlite_error(hresult, sql()); } - } std::string sql() { @@ -107,8 +119,10 @@ namespace sqlite { } void used(bool state) { - if(execution_started == true && state == true) { - throw errors::reexecution("Already used statement executed again! Please reset() first!",sql()); + if(!state) { + // We may have to reset first if we haven't done so already: + _next_index(); + --_inx; } execution_started = state; } @@ -123,9 +137,22 @@ namespace sqlite { bool execution_started = false; + 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<void(void)> call_back) { int hresult; - used(true); + _start_execute(); while((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) { call_back(); @@ -138,7 +165,7 @@ namespace sqlite { void _extract_single_value(std::function<void(void)> call_back) { int hresult; - used(true); + _start_execute(); if((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) { call_back(); @@ -155,15 +182,9 @@ namespace sqlite { } } -#ifdef _MSC_VER sqlite3_stmt* _prepare(const std::u16string& sql) { - return _prepare(std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t>().to_bytes(reinterpret_cast<const wchar_t*>(sql.c_str()))); + return _prepare(utility::utf16_to_utf8(sql)); } -#else - sqlite3_stmt* _prepare(const std::u16string& sql) { - return _prepare(std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>().to_bytes(sql)); - } -#endif sqlite3_stmt* _prepare(const std::string& sql) { int hresult; @@ -171,7 +192,7 @@ namespace sqlite { const char *remaining; hresult = sqlite3_prepare_v2(_db.get(), sql.data(), -1, &tmp, &remaining); if(hresult != SQLITE_OK) errors::throw_sqlite_error(hresult, sql); - if(!std::all_of(remaining, sql.data() + sql.size(), [](char ch) {return std::isblank(ch);})) + if(!std::all_of(remaining, sql.data() + sql.size(), [](char ch) {return std::isspace(ch);})) throw errors::more_statements("Multiple semicolon separated statements are unsupported", sql); return tmp; } @@ -229,8 +250,8 @@ namespace sqlite { #ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT - template <typename OptionalT> friend database_binder& operator <<(database_binder& db, const std::optional<OptionalT>& val); - template <typename OptionalT> friend void get_col_from_db(database_binder& db, int inx, std::optional<OptionalT>& o); + template <typename OptionalT> friend database_binder& operator <<(database_binder& db, const optional<OptionalT>& val); + template <typename OptionalT> friend void get_col_from_db(database_binder& db, int inx, optional<OptionalT>& o); #endif #ifdef _MODERN_SQLITE_BOOST_OPTIONAL_SUPPORT @@ -243,13 +264,13 @@ namespace sqlite { database_binder(std::shared_ptr<sqlite3> db, std::u16string const & sql): _db(db), _stmt(_prepare(sql), sqlite3_finalize), - _inx(1) { + _inx(0) { } database_binder(std::shared_ptr<sqlite3> db, std::string const & sql): _db(db), _stmt(_prepare(sql), sqlite3_finalize), - _inx(1) { + _inx(0) { } ~database_binder() noexcept(false) { @@ -365,7 +386,7 @@ namespace sqlite { }; inline OpenFlags operator|(const OpenFlags& a, const OpenFlags& b) { return static_cast<OpenFlags>(static_cast<int>(a) | static_cast<int>(b)); - }; + } enum class Encoding { ANY = SQLITE_ANY, UTF8 = SQLITE_UTF8, @@ -393,11 +414,7 @@ namespace sqlite { } database(const std::u16string &db_name, const sqlite_config &config = {}): _db(nullptr) { -#ifdef _MSC_VER - auto db_name_utf8 = std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t>().to_bytes(reinterpret_cast<const wchar_t*>(db_name.c_str())); -#else - auto db_name_utf8 = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>().to_bytes(db_name); -#endif + auto db_name_utf8 = utility::utf16_to_utf8(db_name); sqlite3* tmp = nullptr; auto ret = sqlite3_open_v2(db_name_utf8.data(), &tmp, static_cast<int>(config.flags), config.zVfs); _db = std::shared_ptr<sqlite3>(tmp, [=](sqlite3* ptr) { sqlite3_close_v2(ptr); }); // this will close the connection eventually when no longer needed. @@ -515,10 +532,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) { @@ -542,11 +558,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) { @@ -570,11 +585,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) { @@ -598,11 +612,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) { @@ -628,10 +641,9 @@ namespace sqlite { void const* buf = reinterpret_cast<void const *>(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<typename T, typename A> inline void store_result_in_db(sqlite3_context* db, const std::vector<T, A>& vec) { @@ -661,10 +673,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) { @@ -723,11 +734,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) { @@ -754,11 +764,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) { @@ -789,37 +798,40 @@ namespace sqlite { // std::optional support for NULL values #ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT - template <typename OptionalT> inline database_binder& operator <<(database_binder& db, const std::optional<OptionalT>& val) { + template <typename OptionalT> inline database_binder& operator <<(database_binder& db, const optional<OptionalT>& val) { if(val) { - return operator << (std::move(db), std::move(*val)); - } - int hresult; - if((hresult = sqlite3_bind_null(db._stmt.get(), db._inx)) != SQLITE_OK) { - errors::throw_sqlite_error(hresult, db.sql()); + return db << std::move(*val); + } else { + return db << nullptr; } - - ++db._inx; - return db; } - template <typename OptionalT> inline void store_result_in_db(sqlite3_context* db, const std::optional<OptionalT>& val) { + template <typename OptionalT> inline void store_result_in_db(sqlite3_context* db, const optional<OptionalT>& val) { if(val) { store_result_in_db(db, *val); } sqlite3_result_null(db); } - template <typename OptionalT> inline void get_col_from_db(database_binder& db, int inx, std::optional<OptionalT>& o) { + template <typename OptionalT> inline void get_col_from_db(database_binder& db, int inx, optional<OptionalT>& o) { if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) { + #ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT + o = std::experimental::nullopt; + #else o.reset(); + #endif } else { OptionalT v; get_col_from_db(db, inx, v); o = std::move(v); } } - template <typename OptionalT> inline void get_val_from_db(sqlite3_value *value, std::optional<OptionalT>& o) { + template <typename OptionalT> inline void get_val_from_db(sqlite3_value *value, optional<OptionalT>& o) { if(sqlite3_value_type(value) == SQLITE_NULL) { + #ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT + o = std::experimental::nullopt; + #else o.reset(); + #endif } else { OptionalT v; get_val_from_db(value, v); @@ -832,15 +844,10 @@ namespace sqlite { #ifdef _MODERN_SQLITE_BOOST_OPTIONAL_SUPPORT template <typename BoostOptionalT> inline database_binder& operator <<(database_binder& db, const boost::optional<BoostOptionalT>& val) { if(val) { - return operator << (std::move(db), std::move(*val)); - } - int hresult; - if((hresult = sqlite3_bind_null(db._stmt.get(), db._inx)) != SQLITE_OK) { - errors::throw_sqlite_error(hresult, db.sql()); + return db << std::move(*val); + } else { + return db << nullptr; } - - ++db._inx; - return db; } template <typename BoostOptionalT> inline void store_result_in_db(sqlite3_context* db, const boost::optional<BoostOptionalT>& val) { if(val) { @@ -892,10 +899,10 @@ 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<typename T> database_binder& operator << (database_binder&& db, const T& val) { return db << val; } + template<typename T> database_binder&& operator << (database_binder&& db, const T& val) { db << val; return std::move(db); } namespace sql_function_binder { template<class T> @@ -967,7 +974,7 @@ namespace sqlite { Values&&... values ) { static_cast<Functions*>(sqlite3_user_data(db))->first(std::forward<Values>(values)...); - }; + } template< typename ContextType, diff --git a/ext/sqlite_modern_cpp/sqlite_modern_cpp/errors.h b/ext/sqlite_modern_cpp/sqlite_modern_cpp/errors.h index 60faaec..2b9ab75 100644 --- a/ext/sqlite_modern_cpp/sqlite_modern_cpp/errors.h +++ b/ext/sqlite_modern_cpp/sqlite_modern_cpp/errors.h @@ -37,8 +37,8 @@ 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 more_statements: public sqlite_exception { using sqlite_exception::sqlite_exception; }; // Prepared statements can only contain one statement + class invalid_utf16: public sqlite_exception { using sqlite_exception::sqlite_exception; }; static void throw_sqlite_error(const int& error_code, const std::string &sql = "") { switch(error_code & 0xFF) { diff --git a/ext/sqlite_modern_cpp/sqlite_modern_cpp/lists/error_codes.h b/ext/sqlite_modern_cpp/sqlite_modern_cpp/lists/error_codes.h index 5b3d271..5dfa0d3 100644 --- a/ext/sqlite_modern_cpp/sqlite_modern_cpp/lists/error_codes.h +++ b/ext/sqlite_modern_cpp/sqlite_modern_cpp/lists/error_codes.h @@ -1,3 +1,8 @@ +#if SQLITE_VERSION_NUMBER < 3010000 +#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8)) +#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) +#define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) +#endif SQLITE_MODERN_CPP_ERROR_CODE(ERROR,error,) SQLITE_MODERN_CPP_ERROR_CODE(INTERNAL,internal,) SQLITE_MODERN_CPP_ERROR_CODE(PERM,perm,) @@ -75,7 +80,6 @@ SQLITE_MODERN_CPP_ERROR_CODE(MISMATCH,mismatch,) SQLITE_MODERN_CPP_ERROR_CODE(MISUSE,misuse,) SQLITE_MODERN_CPP_ERROR_CODE(NOLFS,nolfs,) SQLITE_MODERN_CPP_ERROR_CODE(AUTH,auth, - SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(AUTH,USER,auth,user) ) SQLITE_MODERN_CPP_ERROR_CODE(FORMAT,format,) SQLITE_MODERN_CPP_ERROR_CODE(RANGE,range,) diff --git a/ext/sqlite_modern_cpp/sqlite_modern_cpp/utility/utf16_utf8.h b/ext/sqlite_modern_cpp/sqlite_modern_cpp/utility/utf16_utf8.h new file mode 100644 index 0000000..ea21723 --- /dev/null +++ b/ext/sqlite_modern_cpp/sqlite_modern_cpp/utility/utf16_utf8.h @@ -0,0 +1,42 @@ +#pragma once + +#include <locale> +#include <string> +#include <algorithm> + +#include "../errors.h" + +namespace sqlite { + namespace utility { + inline std::string utf16_to_utf8(const std::u16string &input) { + struct : std::codecvt<char16_t, char, std::mbstate_t> { + } codecvt; + std::mbstate_t state{}; + std::string result((std::max)(input.size() * 3 / 2, std::size_t(4)), '\0'); + const char16_t *remaining_input = input.data(); + std::size_t produced_output = 0; + while(true) { + char *used_output; + switch(codecvt.out(state, remaining_input, &input[input.size()], + remaining_input, &result[produced_output], + &result[result.size() - 1] + 1, used_output)) { + case std::codecvt_base::ok: + result.resize(used_output - result.data()); + return result; + case std::codecvt_base::noconv: + // This should be unreachable + case std::codecvt_base::error: + throw errors::invalid_utf16("Invalid UTF-16 input", ""); + case std::codecvt_base::partial: + if(used_output == result.data() + produced_output) + throw errors::invalid_utf16("Unexpected end of input", ""); + produced_output = used_output - result.data(); + result.resize( + result.size() + + (std::max)((&input[input.size()] - remaining_input) * 3 / 2, + std::ptrdiff_t(4))); + } + } + } + } // namespace utility +} // namespace sqlite |