summaryrefslogtreecommitdiffstats
path: root/ext/sqlite_modern_cpp/sqlite_modern_cpp.h
diff options
context:
space:
mode:
Diffstat (limited to 'ext/sqlite_modern_cpp/sqlite_modern_cpp.h')
-rw-r--r--ext/sqlite_modern_cpp/sqlite_modern_cpp.h1040
1 files changed, 1040 insertions, 0 deletions
diff --git a/ext/sqlite_modern_cpp/sqlite_modern_cpp.h b/ext/sqlite_modern_cpp/sqlite_modern_cpp.h
new file mode 100644
index 0000000..b493aaa
--- /dev/null
+++ b/ext/sqlite_modern_cpp/sqlite_modern_cpp.h
@@ -0,0 +1,1040 @@
+#pragma once
+
+#include <algorithm>
+#include <cctype>
+#include <string>
+#include <functional>
+#include <ctime>
+#include <tuple>
+#include <memory>
+#include <vector>
+#include <locale>
+#include <codecvt>
+
+#ifdef __has_include
+#if __cplusplus > 201402 && __has_include(<optional>)
+#define MODERN_SQLITE_STD_OPTIONAL_SUPPORT
+#endif
+#endif
+
+#ifdef __has_include
+#if __cplusplus > 201402 && __has_include(<variant>)
+#define MODERN_SQLITE_STD_VARIANT_SUPPORT
+#endif
+#endif
+
+#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
+#include <optional>
+#endif
+
+#ifdef _MODERN_SQLITE_BOOST_OPTIONAL_SUPPORT
+#include <boost/optional.hpp>
+#endif
+
+#include <sqlite3.h>
+
+#include "sqlite_modern_cpp/errors.h"
+#include "sqlite_modern_cpp/utility/function_traits.h"
+#include "sqlite_modern_cpp/utility/uncaught_exceptions.h"
+
+#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
+#include "sqlite_modern_cpp/utility/variant.h"
+#endif
+
+namespace sqlite {
+ class database;
+ class database_binder;
+
+ template<std::size_t> class binder;
+
+ typedef std::shared_ptr<sqlite3> connection_type;
+
+ template<typename Tuple, int Element = 0, bool Last = (std::tuple_size<Tuple>::value == Element)> struct tuple_iterate {
+ static void iterate(Tuple& t, database_binder& db) {
+ get_col_from_db(db, Element, std::get<Element>(t));
+ tuple_iterate<Tuple, Element + 1>::iterate(t, db);
+ }
+ };
+
+ template<typename Tuple, int Element> struct tuple_iterate<Tuple, Element, true> {
+ static void iterate(Tuple&, database_binder&) {}
+ };
+
+ class database_binder {
+
+ public:
+ // database_binder is not copyable
+ database_binder() = delete;
+ database_binder(const database_binder& other) = delete;
+ database_binder& operator=(const database_binder&) = delete;
+
+ database_binder(database_binder&& other) :
+ _db(std::move(other._db)),
+ _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() {
+ 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() {
+#if SQLITE_VERSION_NUMBER >= 3014000
+ auto sqlite_deleter = [](void *ptr) {sqlite3_free(ptr);};
+ std::unique_ptr<char, decltype(sqlite_deleter)> str(sqlite3_expanded_sql(_stmt.get()), sqlite_deleter);
+ return str ? str.get() : original_sql();
+#else
+ return original_sql();
+#endif
+ }
+
+ std::string original_sql() {
+ return sqlite3_sql(_stmt.get());
+ }
+
+ 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; }
+
+ private:
+ std::shared_ptr<sqlite3> _db;
+ std::unique_ptr<sqlite3_stmt, decltype(&sqlite3_finalize)> _stmt;
+ utility::UncaughtExceptionDetector _has_uncaught_exception;
+
+ int _inx;
+
+ bool execution_started = false;
+
+ void _extract(std::function<void(void)> call_back) {
+ int hresult;
+ used(true);
+
+ while((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) {
+ call_back();
+ }
+
+ if(hresult != SQLITE_DONE) {
+ errors::throw_sqlite_error(hresult, sql());
+ }
+ }
+
+ void _extract_single_value(std::function<void(void)> call_back) {
+ int hresult;
+ used(true);
+
+ if((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) {
+ call_back();
+ } else if(hresult == SQLITE_DONE) {
+ throw errors::no_rows("no rows to extract: exactly 1 row expected", sql(), SQLITE_DONE);
+ }
+
+ if((hresult = sqlite3_step(_stmt.get())) == SQLITE_ROW) {
+ throw errors::more_rows("not all rows extracted", sql(), SQLITE_ROW);
+ }
+
+ if(hresult != SQLITE_DONE) {
+ errors::throw_sqlite_error(hresult, sql());
+ }
+ }
+
+#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())));
+ }
+#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;
+ sqlite3_stmt* tmp = nullptr;
+ 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);}))
+ throw errors::more_statements("Multiple semicolon separated statements are unsupported", sql);
+ return tmp;
+ }
+
+ template <typename Type>
+ struct is_sqlite_value : public std::integral_constant<
+ bool,
+ std::is_floating_point<Type>::value
+ || std::is_integral<Type>::value
+ || std::is_same<std::string, Type>::value
+ || std::is_same<std::u16string, Type>::value
+ || std::is_same<sqlite_int64, Type>::value
+ > { };
+ template <typename Type, typename Allocator>
+ struct is_sqlite_value< std::vector<Type, Allocator> > : public std::integral_constant<
+ bool,
+ std::is_floating_point<Type>::value
+ || std::is_integral<Type>::value
+ || std::is_same<sqlite_int64, Type>::value
+ > { };
+#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
+ template <typename ...Args>
+ struct is_sqlite_value< std::variant<Args...> > : public std::integral_constant<
+ bool,
+ true
+ > { };
+#endif
+
+
+ /* for vector<T, A> support */
+ template<typename T, typename A> friend database_binder& operator <<(database_binder& db, const std::vector<T, A>& val);
+ template<typename T, typename A> friend void get_col_from_db(database_binder& db, int inx, std::vector<T, A>& val);
+ /* for nullptr & unique_ptr support */
+ friend database_binder& operator <<(database_binder& db, std::nullptr_t);
+ template<typename T> friend database_binder& operator <<(database_binder& db, const std::unique_ptr<T>& val);
+ template<typename T> friend void get_col_from_db(database_binder& db, int inx, std::unique_ptr<T>& val);
+#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
+ template<typename ...Args> friend database_binder& operator <<(database_binder& db, const std::variant<Args...>& val);
+ template<typename ...Args> friend void get_col_from_db(database_binder& db, int inx, std::variant<Args...>& val);
+#endif
+ template<typename T> friend T operator++(database_binder& db, int);
+ // Overload instead of specializing function templates (http://www.gotw.ca/publications/mill17.htm)
+ friend database_binder& operator<<(database_binder& db, const int& val);
+ friend void get_col_from_db(database_binder& db, int inx, int& val);
+ friend database_binder& operator <<(database_binder& db, const sqlite_int64& val);
+ friend void get_col_from_db(database_binder& db, int inx, sqlite3_int64& i);
+ friend database_binder& operator <<(database_binder& db, const float& val);
+ friend void get_col_from_db(database_binder& db, int inx, float& f);
+ friend database_binder& operator <<(database_binder& db, const double& val);
+ friend void get_col_from_db(database_binder& db, int inx, double& d);
+ friend void get_col_from_db(database_binder& db, int inx, std::string & s);
+ friend database_binder& operator <<(database_binder& db, const std::string& txt);
+ friend void get_col_from_db(database_binder& db, int inx, std::u16string & w);
+ friend database_binder& operator <<(database_binder& db, const std::u16string& txt);
+
+
+#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);
+#endif
+
+#ifdef _MODERN_SQLITE_BOOST_OPTIONAL_SUPPORT
+ template <typename BoostOptionalT> friend database_binder& operator <<(database_binder& db, const boost::optional<BoostOptionalT>& val);
+ template <typename BoostOptionalT> friend void get_col_from_db(database_binder& db, int inx, boost::optional<BoostOptionalT>& o);
+#endif
+
+ public:
+
+ database_binder(std::shared_ptr<sqlite3> db, std::u16string const & sql):
+ _db(db),
+ _stmt(_prepare(sql), sqlite3_finalize),
+ _inx(1) {
+ }
+
+ database_binder(std::shared_ptr<sqlite3> db, std::string const & sql):
+ _db(db),
+ _stmt(_prepare(sql), sqlite3_finalize),
+ _inx(1) {
+ }
+
+ ~database_binder() noexcept(false) {
+ /* Will be executed if no >>op is found, but not if an exception
+ is in mid flight */
+ if(!used() && !_has_uncaught_exception && _stmt) {
+ execute();
+ }
+ }
+
+ template <typename Result>
+ typename std::enable_if<is_sqlite_value<Result>::value, void>::type operator>>(
+ Result& value) {
+ this->_extract_single_value([&value, this] {
+ get_col_from_db(*this, 0, value);
+ });
+ }
+
+ template<typename... Types>
+ void operator>>(std::tuple<Types...>&& values) {
+ this->_extract_single_value([&values, this] {
+ tuple_iterate<std::tuple<Types...>>::iterate(values, *this);
+ });
+ }
+
+ template <typename Function>
+ typename std::enable_if<!is_sqlite_value<Function>::value, void>::type operator>>(
+ Function&& func) {
+ typedef utility::function_traits<Function> traits;
+
+ this->_extract([&func, this]() {
+ binder<traits::arity>::run(*this, func);
+ });
+ }
+ };
+
+ namespace sql_function_binder {
+ template<
+ typename ContextType,
+ std::size_t Count,
+ typename Functions
+ >
+ inline void step(
+ sqlite3_context* db,
+ int count,
+ sqlite3_value** vals
+ );
+
+ template<
+ std::size_t Count,
+ typename Functions,
+ typename... Values
+ >
+ inline typename std::enable_if<(sizeof...(Values) && sizeof...(Values) < Count), void>::type step(
+ sqlite3_context* db,
+ int count,
+ sqlite3_value** vals,
+ Values&&... values
+ );
+
+ template<
+ std::size_t Count,
+ typename Functions,
+ typename... Values
+ >
+ inline typename std::enable_if<(sizeof...(Values) == Count), void>::type step(
+ sqlite3_context* db,
+ int,
+ sqlite3_value**,
+ Values&&... values
+ );
+
+ template<
+ typename ContextType,
+ typename Functions
+ >
+ inline void final(sqlite3_context* db);
+
+ template<
+ std::size_t Count,
+ typename Function,
+ typename... Values
+ >
+ inline typename std::enable_if<(sizeof...(Values) < Count), void>::type scalar(
+ sqlite3_context* db,
+ int count,
+ sqlite3_value** vals,
+ Values&&... values
+ );
+
+ template<
+ std::size_t Count,
+ typename Function,
+ typename... Values
+ >
+ inline typename std::enable_if<(sizeof...(Values) == Count), void>::type scalar(
+ sqlite3_context* db,
+ int,
+ sqlite3_value**,
+ Values&&... values
+ );
+ }
+
+ enum class OpenFlags {
+ READONLY = SQLITE_OPEN_READONLY,
+ READWRITE = SQLITE_OPEN_READWRITE,
+ CREATE = SQLITE_OPEN_CREATE,
+ NOMUTEX = SQLITE_OPEN_NOMUTEX,
+ FULLMUTEX = SQLITE_OPEN_FULLMUTEX,
+ SHAREDCACHE = SQLITE_OPEN_SHAREDCACHE,
+ PRIVATECACH = SQLITE_OPEN_PRIVATECACHE,
+ URI = SQLITE_OPEN_URI
+ };
+ 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,
+ UTF16 = SQLITE_UTF16
+ };
+ struct sqlite_config {
+ OpenFlags flags = OpenFlags::READWRITE | OpenFlags::CREATE;
+ const char *zVfs = nullptr;
+ Encoding encoding = Encoding::ANY;
+ };
+
+ class database {
+ protected:
+ std::shared_ptr<sqlite3> _db;
+
+ public:
+ database(const std::string &db_name, const sqlite_config &config = {}): _db(nullptr) {
+ sqlite3* tmp = nullptr;
+ auto ret = sqlite3_open_v2(db_name.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.
+ if(ret != SQLITE_OK) errors::throw_sqlite_error(_db ? sqlite3_extended_errcode(_db.get()) : ret);
+ sqlite3_extended_result_codes(_db.get(), true);
+ if(config.encoding == Encoding::UTF16)
+ *this << R"(PRAGMA encoding = "UTF-16";)";
+ }
+
+ 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
+ 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.
+ if(ret != SQLITE_OK) errors::throw_sqlite_error(_db ? sqlite3_extended_errcode(_db.get()) : ret);
+ sqlite3_extended_result_codes(_db.get(), true);
+ if(config.encoding != Encoding::UTF8)
+ *this << R"(PRAGMA encoding = "UTF-16";)";
+ }
+
+ database(std::shared_ptr<sqlite3> db):
+ _db(db) {}
+
+ database_binder operator<<(const std::string& sql) {
+ return database_binder(_db, sql);
+ }
+
+ database_binder operator<<(const char* sql) {
+ return *this << std::string(sql);
+ }
+
+ database_binder operator<<(const std::u16string& sql) {
+ return database_binder(_db, sql);
+ }
+
+ database_binder operator<<(const char16_t* sql) {
+ return *this << std::u16string(sql);
+ }
+
+ connection_type connection() const { return _db; }
+
+ sqlite3_int64 last_insert_rowid() const {
+ return sqlite3_last_insert_rowid(_db.get());
+ }
+
+ template <typename Function>
+ void define(const std::string &name, Function&& func) {
+ typedef utility::function_traits<Function> traits;
+
+ auto funcPtr = new auto(std::forward<Function>(func));
+ if(int result = sqlite3_create_function_v2(
+ _db.get(), name.c_str(), traits::arity, SQLITE_UTF8, funcPtr,
+ sql_function_binder::scalar<traits::arity, typename std::remove_reference<Function>::type>,
+ nullptr, nullptr, [](void* ptr){
+ delete static_cast<decltype(funcPtr)>(ptr);
+ }))
+ errors::throw_sqlite_error(result);
+ }
+
+ template <typename StepFunction, typename FinalFunction>
+ void define(const std::string &name, StepFunction&& step, FinalFunction&& final) {
+ typedef utility::function_traits<StepFunction> traits;
+ using ContextType = typename std::remove_reference<typename traits::template argument<0>>::type;
+
+ auto funcPtr = new auto(std::make_pair(std::forward<StepFunction>(step), std::forward<FinalFunction>(final)));
+ if(int result = sqlite3_create_function_v2(
+ _db.get(), name.c_str(), traits::arity - 1, SQLITE_UTF8, funcPtr, nullptr,
+ sql_function_binder::step<ContextType, traits::arity, typename std::remove_reference<decltype(*funcPtr)>::type>,
+ sql_function_binder::final<ContextType, typename std::remove_reference<decltype(*funcPtr)>::type>,
+ [](void* ptr){
+ delete static_cast<decltype(funcPtr)>(ptr);
+ }))
+ errors::throw_sqlite_error(result);
+ }
+
+ };
+
+ template<std::size_t Count>
+ class binder {
+ private:
+ template <
+ typename Function,
+ std::size_t Index
+ >
+ using nth_argument_type = typename utility::function_traits<
+ Function
+ >::template argument<Index>;
+
+ public:
+ // `Boundary` needs to be defaulted to `Count` so that the `run` function
+ // template is not implicitly instantiated on class template instantiation.
+ // Look up section 14.7.1 _Implicit instantiation_ of the ISO C++14 Standard
+ // and the [dicussion](https://github.com/aminroosta/sqlite_modern_cpp/issues/8)
+ // on Github.
+
+ template<
+ typename Function,
+ typename... Values,
+ std::size_t Boundary = Count
+ >
+ static typename std::enable_if<(sizeof...(Values) < Boundary), void>::type run(
+ database_binder& db,
+ Function&& function,
+ Values&&... values
+ ) {
+ typename std::remove_cv<typename std::remove_reference<nth_argument_type<Function, sizeof...(Values)>>::type>::type value{};
+ get_col_from_db(db, sizeof...(Values), value);
+
+ run<Function>(db, function, std::forward<Values>(values)..., std::move(value));
+ }
+
+ template<
+ typename Function,
+ typename... Values,
+ std::size_t Boundary = Count
+ >
+ static typename std::enable_if<(sizeof...(Values) == Boundary), void>::type run(
+ database_binder&,
+ Function&& function,
+ Values&&... values
+ ) {
+ function(std::move(values)...);
+ }
+ };
+
+ // 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) {
+ errors::throw_sqlite_error(hresult, db.sql());
+ }
+ ++db._inx;
+ return db;
+ }
+ inline void store_result_in_db(sqlite3_context* db, const int& val) {
+ sqlite3_result_int(db, val);
+ }
+ inline void get_col_from_db(database_binder& db, int inx, int& val) {
+ if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) {
+ val = 0;
+ } else {
+ val = sqlite3_column_int(db._stmt.get(), inx);
+ }
+ }
+ inline void get_val_from_db(sqlite3_value *value, int& val) {
+ if(sqlite3_value_type(value) == SQLITE_NULL) {
+ val = 0;
+ } else {
+ val = sqlite3_value_int(value);
+ }
+ }
+
+ // 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) {
+ errors::throw_sqlite_error(hresult, db.sql());
+ }
+
+ ++db._inx;
+ return db;
+ }
+ inline void store_result_in_db(sqlite3_context* db, const sqlite_int64& val) {
+ sqlite3_result_int64(db, val);
+ }
+ inline void get_col_from_db(database_binder& db, int inx, sqlite3_int64& i) {
+ if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) {
+ i = 0;
+ } else {
+ i = sqlite3_column_int64(db._stmt.get(), inx);
+ }
+ }
+ inline void get_val_from_db(sqlite3_value *value, sqlite3_int64& i) {
+ if(sqlite3_value_type(value) == SQLITE_NULL) {
+ i = 0;
+ } else {
+ i = sqlite3_value_int64(value);
+ }
+ }
+
+ // 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) {
+ errors::throw_sqlite_error(hresult, db.sql());
+ }
+
+ ++db._inx;
+ return db;
+ }
+ inline void store_result_in_db(sqlite3_context* db, const float& val) {
+ sqlite3_result_double(db, val);
+ }
+ inline void get_col_from_db(database_binder& db, int inx, float& f) {
+ if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) {
+ f = 0;
+ } else {
+ f = float(sqlite3_column_double(db._stmt.get(), inx));
+ }
+ }
+ inline void get_val_from_db(sqlite3_value *value, float& f) {
+ if(sqlite3_value_type(value) == SQLITE_NULL) {
+ f = 0;
+ } else {
+ f = float(sqlite3_value_double(value));
+ }
+ }
+
+ // 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) {
+ errors::throw_sqlite_error(hresult, db.sql());
+ }
+
+ ++db._inx;
+ return db;
+ }
+ inline void store_result_in_db(sqlite3_context* db, const double& val) {
+ sqlite3_result_double(db, val);
+ }
+ inline void get_col_from_db(database_binder& db, int inx, double& d) {
+ if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) {
+ d = 0;
+ } else {
+ d = sqlite3_column_double(db._stmt.get(), inx);
+ }
+ }
+ inline void get_val_from_db(sqlite3_value *value, double& d) {
+ if(sqlite3_value_type(value) == SQLITE_NULL) {
+ d = 0;
+ } else {
+ d = sqlite3_value_double(value);
+ }
+ }
+
+ // vector<T, A>
+ template<typename T, typename A> inline database_binder& operator<<(database_binder& db, const std::vector<T, A>& vec) {
+ 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) {
+ 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) {
+ void const* buf = reinterpret_cast<void const *>(vec.data());
+ int bytes = vec.size() * sizeof(T);
+ sqlite3_result_blob(db, buf, bytes, SQLITE_TRANSIENT);
+ }
+ template<typename T, typename A> inline void get_col_from_db(database_binder& db, int inx, std::vector<T, A>& vec) {
+ if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) {
+ vec.clear();
+ } else {
+ int bytes = sqlite3_column_bytes(db._stmt.get(), inx);
+ T const* buf = reinterpret_cast<T const *>(sqlite3_column_blob(db._stmt.get(), inx));
+ vec = std::vector<T, A>(buf, buf + bytes/sizeof(T));
+ }
+ }
+ template<typename T, typename A> inline void get_val_from_db(sqlite3_value *value, std::vector<T, A>& vec) {
+ if(sqlite3_value_type(value) == SQLITE_NULL) {
+ vec.clear();
+ } else {
+ int bytes = sqlite3_value_bytes(value);
+ T const* buf = reinterpret_cast<T const *>(sqlite3_value_blob(value));
+ vec = std::vector<T, A>(buf, buf + bytes/sizeof(T));
+ }
+ }
+
+ /* 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) {
+ errors::throw_sqlite_error(hresult, db.sql());
+ }
+ ++db._inx;
+ return db;
+ }
+ inline void store_result_in_db(sqlite3_context* db, std::nullptr_t) {
+ sqlite3_result_null(db);
+ }
+ /* for nullptr support */
+ template<typename T> inline database_binder& operator <<(database_binder& db, const std::unique_ptr<T>& val) {
+ if(val)
+ db << *val;
+ else
+ db << nullptr;
+ return db;
+ }
+
+ /* for unique_ptr<T> support */
+ template<typename T> inline void get_col_from_db(database_binder& db, int inx, std::unique_ptr<T>& _ptr_) {
+ if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) {
+ _ptr_ = nullptr;
+ } else {
+ auto underling_ptr = new T();
+ get_col_from_db(db, inx, *underling_ptr);
+ _ptr_.reset(underling_ptr);
+ }
+ }
+ template<typename T> inline void get_val_from_db(sqlite3_value *value, std::unique_ptr<T>& _ptr_) {
+ if(sqlite3_value_type(value) == SQLITE_NULL) {
+ _ptr_ = nullptr;
+ } else {
+ auto underling_ptr = new T();
+ get_val_from_db(value, *underling_ptr);
+ _ptr_.reset(underling_ptr);
+ }
+ }
+
+ // std::string
+ inline void get_col_from_db(database_binder& db, int inx, std::string & s) {
+ if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) {
+ s = std::string();
+ } else {
+ sqlite3_column_bytes(db._stmt.get(), inx);
+ s = std::string(reinterpret_cast<char const *>(sqlite3_column_text(db._stmt.get(), inx)));
+ }
+ }
+ inline void get_val_from_db(sqlite3_value *value, std::string & s) {
+ if(sqlite3_value_type(value) == SQLITE_NULL) {
+ s = std::string();
+ } else {
+ sqlite3_value_bytes(value);
+ s = std::string(reinterpret_cast<char const *>(sqlite3_value_text(value)));
+ }
+ }
+
+ // Convert char* to string to trigger op<<(..., const std::string )
+ template<std::size_t N> inline database_binder& operator <<(database_binder& db, const char(&STR)[N]) { return db << std::string(STR); }
+ template<std::size_t N> inline database_binder& operator <<(database_binder& db, const char16_t(&STR)[N]) { return db << std::u16string(STR); }
+
+ 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) {
+ errors::throw_sqlite_error(hresult, db.sql());
+ }
+
+ ++db._inx;
+ return db;
+ }
+ inline void store_result_in_db(sqlite3_context* db, const std::string& val) {
+ sqlite3_result_text(db, val.data(), -1, SQLITE_TRANSIENT);
+ }
+ // std::u16string
+ inline void get_col_from_db(database_binder& db, int inx, std::u16string & w) {
+ if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) {
+ w = std::u16string();
+ } else {
+ sqlite3_column_bytes16(db._stmt.get(), inx);
+ w = std::u16string(reinterpret_cast<char16_t const *>(sqlite3_column_text16(db._stmt.get(), inx)));
+ }
+ }
+ inline void get_val_from_db(sqlite3_value *value, std::u16string & w) {
+ if(sqlite3_value_type(value) == SQLITE_NULL) {
+ w = std::u16string();
+ } else {
+ sqlite3_value_bytes16(value);
+ w = std::u16string(reinterpret_cast<char16_t const *>(sqlite3_value_text16(value)));
+ }
+ }
+
+
+ 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) {
+ errors::throw_sqlite_error(hresult, db.sql());
+ }
+
+ ++db._inx;
+ return db;
+ }
+ inline void store_result_in_db(sqlite3_context* db, const std::u16string& val) {
+ sqlite3_result_text16(db, val.data(), -1, SQLITE_TRANSIENT);
+ }
+
+ // Other integer types
+ template<class Integral, class = typename std::enable_if<std::is_integral<Integral>::value>::type>
+ inline database_binder& operator <<(database_binder& db, const Integral& val) {
+ return db << static_cast<sqlite3_int64>(val);
+ }
+ template<class Integral, class = std::enable_if<std::is_integral<Integral>::type>>
+ inline void store_result_in_db(sqlite3_context* db, const Integral& val) {
+ store_result_in_db(db, static_cast<sqlite3_int64>(val));
+ }
+ template<class Integral, class = typename std::enable_if<std::is_integral<Integral>::value>::type>
+ inline void get_col_from_db(database_binder& db, int inx, Integral& val) {
+ sqlite3_int64 i;
+ get_col_from_db(db, inx, i);
+ val = i;
+ }
+ template<class Integral, class = typename std::enable_if<std::is_integral<Integral>::value>::type>
+ inline void get_val_from_db(sqlite3_value *value, Integral& val) {
+ sqlite3_int64 i;
+ get_val_from_db(value, i);
+ val = i;
+ }
+
+ // 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) {
+ 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());
+ }
+
+ ++db._inx;
+ return db;
+ }
+ template <typename OptionalT> inline void store_result_in_db(sqlite3_context* db, const std::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) {
+ if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) {
+ o.reset();
+ } 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) {
+ if(sqlite3_value_type(value) == SQLITE_NULL) {
+ o.reset();
+ } else {
+ OptionalT v;
+ get_val_from_db(value, v);
+ o = std::move(v);
+ }
+ }
+#endif
+
+ // boost::optional support for NULL values
+#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());
+ }
+
+ ++db._inx;
+ return db;
+ }
+ template <typename BoostOptionalT> inline void store_result_in_db(sqlite3_context* db, const boost::optional<BoostOptionalT>& val) {
+ if(val) {
+ store_result_in_db(db, *val);
+ }
+ sqlite3_result_null(db);
+ }
+
+ template <typename BoostOptionalT> inline void get_col_from_db(database_binder& db, int inx, boost::optional<BoostOptionalT>& o) {
+ if(sqlite3_column_type(db._stmt.get(), inx) == SQLITE_NULL) {
+ o.reset();
+ } else {
+ BoostOptionalT v;
+ get_col_from_db(db, inx, v);
+ o = std::move(v);
+ }
+ }
+ template <typename BoostOptionalT> inline void get_val_from_db(sqlite3_value *value, boost::optional<BoostOptionalT>& o) {
+ if(sqlite3_value_type(value) == SQLITE_NULL) {
+ o.reset();
+ } else {
+ BoostOptionalT v;
+ get_val_from_db(value, v);
+ o = std::move(v);
+ }
+ }
+#endif
+
+#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
+ template <typename ...Args> inline database_binder& operator <<(database_binder& db, const std::variant<Args...>& val) {
+ std::visit([&](auto &&opt) {db << std::forward<decltype(opt)>(opt);}, val);
+ return db;
+ }
+ template <typename ...Args> inline void store_result_in_db(sqlite3_context* db, const std::variant<Args...>& val) {
+ std::visit([&](auto &&opt) {store_result_in_db(db, std::forward<decltype(opt)>(opt));}, val);
+ }
+ template <typename ...Args> inline void get_col_from_db(database_binder& db, int inx, std::variant<Args...>& val) {
+ utility::variant_select<Args...>(sqlite3_column_type(db._stmt.get(), inx))([&](auto v) {
+ get_col_from_db(db, inx, v);
+ val = std::move(v);
+ });
+ }
+ template <typename ...Args> inline void get_val_from_db(sqlite3_value *value, std::variant<Args...>& val) {
+ utility::variant_select<Args...>(sqlite3_value_type(value))([&](auto v) {
+ get_val_from_db(value, v);
+ val = std::move(v);
+ });
+ }
+#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(); }
+
+ // 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; }
+
+ namespace sql_function_binder {
+ template<class T>
+ struct AggregateCtxt {
+ T obj;
+ bool constructed = true;
+ };
+
+ template<
+ typename ContextType,
+ std::size_t Count,
+ typename Functions
+ >
+ inline void step(
+ sqlite3_context* db,
+ int count,
+ sqlite3_value** vals
+ ) {
+ auto ctxt = static_cast<AggregateCtxt<ContextType>*>(sqlite3_aggregate_context(db, sizeof(AggregateCtxt<ContextType>)));
+ if(!ctxt) return;
+ try {
+ if(!ctxt->constructed) new(ctxt) AggregateCtxt<ContextType>();
+ step<Count, Functions>(db, count, vals, ctxt->obj);
+ return;
+ } catch(sqlite_exception &e) {
+ sqlite3_result_error_code(db, e.get_code());
+ sqlite3_result_error(db, e.what(), -1);
+ } catch(std::exception &e) {
+ sqlite3_result_error(db, e.what(), -1);
+ } catch(...) {
+ sqlite3_result_error(db, "Unknown error", -1);
+ }
+ if(ctxt && ctxt->constructed)
+ ctxt->~AggregateCtxt();
+ }
+
+ template<
+ std::size_t Count,
+ typename Functions,
+ typename... Values
+ >
+ inline typename std::enable_if<(sizeof...(Values) && sizeof...(Values) < Count), void>::type step(
+ sqlite3_context* db,
+ int count,
+ sqlite3_value** vals,
+ Values&&... values
+ ) {
+ typename std::remove_cv<
+ typename std::remove_reference<
+ typename utility::function_traits<
+ typename Functions::first_type
+ >::template argument<sizeof...(Values)>
+ >::type
+ >::type value{};
+ get_val_from_db(vals[sizeof...(Values) - 1], value);
+
+ step<Count, Functions>(db, count, vals, std::forward<Values>(values)..., std::move(value));
+ }
+
+ template<
+ std::size_t Count,
+ typename Functions,
+ typename... Values
+ >
+ inline typename std::enable_if<(sizeof...(Values) == Count), void>::type step(
+ sqlite3_context* db,
+ int,
+ sqlite3_value**,
+ Values&&... values
+ ) {
+ static_cast<Functions*>(sqlite3_user_data(db))->first(std::forward<Values>(values)...);
+ };
+
+ template<
+ typename ContextType,
+ typename Functions
+ >
+ inline void final(sqlite3_context* db) {
+ auto ctxt = static_cast<AggregateCtxt<ContextType>*>(sqlite3_aggregate_context(db, sizeof(AggregateCtxt<ContextType>)));
+ try {
+ if(!ctxt) return;
+ if(!ctxt->constructed) new(ctxt) AggregateCtxt<ContextType>();
+ store_result_in_db(db,
+ static_cast<Functions*>(sqlite3_user_data(db))->second(ctxt->obj));
+ } catch(sqlite_exception &e) {
+ sqlite3_result_error_code(db, e.get_code());
+ sqlite3_result_error(db, e.what(), -1);
+ } catch(std::exception &e) {
+ sqlite3_result_error(db, e.what(), -1);
+ } catch(...) {
+ sqlite3_result_error(db, "Unknown error", -1);
+ }
+ if(ctxt && ctxt->constructed)
+ ctxt->~AggregateCtxt();
+ }
+
+ template<
+ std::size_t Count,
+ typename Function,
+ typename... Values
+ >
+ inline typename std::enable_if<(sizeof...(Values) < Count), void>::type scalar(
+ sqlite3_context* db,
+ int count,
+ sqlite3_value** vals,
+ Values&&... values
+ ) {
+ typename std::remove_cv<
+ typename std::remove_reference<
+ typename utility::function_traits<Function>::template argument<sizeof...(Values)>
+ >::type
+ >::type value{};
+ get_val_from_db(vals[sizeof...(Values)], value);
+
+ scalar<Count, Function>(db, count, vals, std::forward<Values>(values)..., std::move(value));
+ }
+