diff options
author | John Turner <jturner.usa@gmail.com> | 2025-09-14 00:16:10 -0400 |
---|---|---|
committer | John Turner <jturner.usa@gmail.com> | 2025-09-14 00:16:10 -0400 |
commit | efcea3a80da7c4479d5fe168435ecc9fd06bdc72 (patch) | |
tree | 5cb0177e17b1b00a177f2e830e809f606334571b /include/boost/sqlite/detail | |
download | sqlite-kv-bench-efcea3a80da7c4479d5fe168435ecc9fd06bdc72.tar.gz |
Squashed 'subprojects/boost-sqlite/' content from commit 3378e35
git-subtree-dir: subprojects/boost-sqlite
git-subtree-split: 3378e353705271e569cf4ba15c467b840a39798c
Diffstat (limited to 'include/boost/sqlite/detail')
-rw-r--r-- | include/boost/sqlite/detail/aggregate_function.hpp | 166 | ||||
-rw-r--r-- | include/boost/sqlite/detail/catch.hpp | 169 | ||||
-rw-r--r-- | include/boost/sqlite/detail/config.hpp | 83 | ||||
-rw-r--r-- | include/boost/sqlite/detail/exception.hpp | 48 | ||||
-rw-r--r-- | include/boost/sqlite/detail/scalar_function.hpp | 172 | ||||
-rw-r--r-- | include/boost/sqlite/detail/vtable.hpp | 611 | ||||
-rw-r--r-- | include/boost/sqlite/detail/window_function.hpp | 238 |
7 files changed, 1487 insertions, 0 deletions
diff --git a/include/boost/sqlite/detail/aggregate_function.hpp b/include/boost/sqlite/detail/aggregate_function.hpp new file mode 100644 index 0000000..de0c447 --- /dev/null +++ b/include/boost/sqlite/detail/aggregate_function.hpp @@ -0,0 +1,166 @@ +// Copyright (c) 2023 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#ifndef BOOST_SQLITE_DETAIL_AGGREGATE_FUNCTION_HPP +#define BOOST_SQLITE_DETAIL_AGGREGATE_FUNCTION_HPP + +#include <boost/sqlite/detail/config.hpp> +#include <boost/sqlite/detail/catch.hpp> +#include <boost/sqlite/error.hpp> +#include <boost/sqlite/cstring_ref.hpp> +#include <boost/sqlite/memory.hpp> +#include <boost/sqlite/result.hpp> +#include <boost/sqlite/value.hpp> + +#include <boost/callable_traits/args.hpp> +#include <boost/callable_traits/return_type.hpp> +#include <boost/callable_traits/has_void_return.hpp> +#include <boost/core/span.hpp> + + +BOOST_SQLITE_BEGIN_NAMESPACE + +namespace detail +{ + +template<typename Func> +struct aggregate_function_maker +{ + void * mem; + + template<typename ... Args> + Func* operator()(Args && ... args) + { + return new (mem) Func(std::forward<Args>(args)...); + } +}; + +template<typename Func, typename Args> +int create_aggregate_function(sqlite3 * db, cstring_ref name, Args && args, int flags, + std::true_type /* void return */) +{ + using args_type = callable_traits::args_t<decltype(&Func::step)>; + using span_type = typename std::tuple_element<1U, args_type>::type; + using func_type = typename std::decay<Func>::type; + using func_args_type = typename std::decay<Args>::type; + + return sqlite3_create_function_v2( + db, name.c_str(), + span_type::extent == boost::dynamic_extent ? -1 : static_cast<int>(span_type::extent), + SQLITE_UTF8 | flags, + new (memory_tag{}) func_args_type(std::forward<Args>(args)), + nullptr, + +[](sqlite3_context* ctx, int len, sqlite3_value** args) + { + auto aa = reinterpret_cast<value*>(args); + auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx)); + auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0)); + + execute_context_function( + ctx, + [&]() -> result<void> + { + if (c == nullptr) + { + auto p = sqlite3_aggregate_context(ctx, sizeof(func_type)); + if (!p) + return error(SQLITE_NOMEM); + c = mp11::tuple_apply(aggregate_function_maker<func_type>{p}, *fa); + } + c->step(span_type{aa, static_cast<std::size_t>(len)}); + return {}; + }); + }, + [](sqlite3_context* ctx) + { + auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx)); + auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0)); + + execute_context_function( + ctx, + [&]() -> result<decltype(c->final())> + { + if (c == nullptr) + { + auto p = sqlite3_aggregate_context(ctx, sizeof(func_type)); + if (!p) + return error(SQLITE_NOMEM); + c = mp11::tuple_apply(aggregate_function_maker<func_type>{p}, *fa); + } + struct reaper {void operator()(func_type * c) { c->~func_type();}}; + std::unique_ptr<func_type, reaper> cl{c}; + return c->final(); + }); + }, + [](void * ptr) noexcept { delete_(static_cast<func_type*>(ptr));} + ); +} + + +template<typename Func, typename Args> +int create_aggregate_function(sqlite3 * db, cstring_ref name, Args && args, int flags, + std::false_type /* void return */) +{ + using args_type = callable_traits::args_t<decltype(&Func::step)>; + using span_type = typename std::tuple_element<1U, args_type>::type; + using func_type = typename std::decay<Func>::type; + using func_args_type = typename std::decay<Args>::type; + + return sqlite3_create_function_v2( + db, name.c_str(), + span_type::extent == boost::dynamic_extent ? -1 : static_cast<int>(span_type::extent), + SQLITE_UTF8 | flags, + new (memory_tag{}) func_args_type(std::forward<Args>(args)), + nullptr, + +[](sqlite3_context* ctx, int len, sqlite3_value** args) + { + auto aa = reinterpret_cast<value*>(args); + auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx)); + auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0)); + + execute_context_function( + ctx, + [&]() -> result<void> + { + if (c == nullptr) + { + auto p = sqlite3_aggregate_context(ctx, sizeof(func_type)); + if (!p) + return {system::in_place_error, error(SQLITE_NOMEM)}; + c = mp11::tuple_apply(aggregate_function_maker<func_type>{p}, *fa); + } + return c->step(span_type{aa, static_cast<std::size_t>(len)}); + }); + }, + [](sqlite3_context* ctx) + { + auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx)); + auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0)); + + execute_context_function( + ctx, + [&]() -> result<decltype(c->final())> + { + if (c == nullptr) + { + auto p = sqlite3_aggregate_context(ctx, sizeof(func_type)); + if (!p) + return {system::in_place_error, error(SQLITE_NOMEM)}; + c = mp11::tuple_apply(aggregate_function_maker<func_type>{p}, *fa); + } + struct reaper {void operator()(func_type * c) { c->~func_type();}}; + std::unique_ptr<func_type, reaper> cl{c}; + return c->final(); + }); + }, + [](void * ptr) noexcept { delete_(static_cast<func_type*>(ptr));} + ); +} + +} + +BOOST_SQLITE_END_NAMESPACE + + +#endif //BOOST_SQLITE_DETAIL_AGGREGATE_FUNCTION_HPP diff --git a/include/boost/sqlite/detail/catch.hpp b/include/boost/sqlite/detail/catch.hpp new file mode 100644 index 0000000..e5b053f --- /dev/null +++ b/include/boost/sqlite/detail/catch.hpp @@ -0,0 +1,169 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_SQLITE_DETAIL_CATCH_HPP +#define BOOST_SQLITE_DETAIL_CATCH_HPP + +#include <boost/sqlite/detail/config.hpp> +#include <boost/sqlite/detail/exception.hpp> +#include <boost/sqlite/error.hpp> +#include <boost/sqlite/result.hpp> + +#include <boost/system/system_error.hpp> + +#include <stdexcept> + + +BOOST_SQLITE_BEGIN_NAMESPACE +namespace detail +{ + +template<typename Func, typename ... Args> +void execute_context_function_impl(std::false_type /* is_void */, + sqlite3_context * ctx, + Func && func, Args && ... args) + noexcept(noexcept(std::forward<Func>(func)(std::forward<Args>(args)...))) +{ + set_result(ctx, std::forward<Func>(func)(std::forward<Args>(args)...)); +} + +template<typename Func, typename ... Args> +void execute_context_function_impl(std::true_type /* is_void */, + sqlite3_context * ctx, + Func && func, Args && ... args) + noexcept(noexcept(std::forward<Func>(func)(std::forward<Args>(args)...))) +{ + std::forward<Func>(func)(std::forward<Args>(args)...); +} + +template<typename T> +int extract_error(char * &errMsg, result<T> & res) +{ + error err = std::move(res).error(); + if (err.info) + errMsg = err.info.release(); + return err.code; +} + + +template<typename Func, typename ... Args> +void execute_context_function(sqlite3_context * ctx, + Func && func, Args && ... args) noexcept +{ + using return_type = decltype(func(std::forward<Args>(args)...)); +#if !defined(BOOST_NO_EXCEPTIONS) + try + { +#endif + execute_context_function_impl(std::is_void<return_type>{}, ctx, + std::forward<Func>(func), + std::forward<Args>(args)...); +#if !defined(BOOST_NO_EXCEPTIONS) + } + catch(boost::system::system_error & se) + { + const auto msg = boost::sqlite::detail::get_message(se); + if (!msg.empty()) + sqlite3_result_error(ctx, msg.data(), msg.size()); + if (se.code().category() == boost::sqlite::sqlite_category()) + sqlite3_result_error_code(ctx, se.code().value()); + } + catch(std::bad_alloc &) { sqlite3_result_error_nomem(ctx); } + catch(std::length_error &) { sqlite3_result_error_toobig(ctx); } + catch(std::out_of_range &) { sqlite3_result_error_code(ctx, SQLITE_RANGE);} + catch(std::logic_error &le) + { + sqlite3_result_error(ctx, le.what(), std::strlen(le.what())); + sqlite3_result_error_code(ctx, SQLITE_MISUSE); + } + catch(std::exception & ex) + { + sqlite3_result_error(ctx, ex.what(), std::strlen(ex.what())); + } + catch(...) {sqlite3_result_error_code(ctx, SQLITE_ERROR); } +#endif +} + +} +BOOST_SQLITE_END_NAMESPACE + +#if defined(BOOST_NO_EXCEPTIONS) +#define BOOST_SQLITE_TRY +#define BOOST_SQLITE_CATCH_RESULT(ctx) +#define BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(msg) +#define BOOST_SQLITE_CATCH_AND_RETURN() + +#else + +#define BOOST_SQLITE_TRY try +#define BOOST_SQLITE_CATCH_RESULT(ctx) \ +catch(boost::system::system_error & se) \ +{ \ + if (se.code().category() == boost::sqlite::sqlite_category()) \ + sqlite3_result_error_code(ctx, se.code().value()); \ + const auto msg = boost::sqlite::detail::get_message(se); \ + if (!msg.empty()) \ + sqlite3_result_error(ctx, msg.data(), msg.size()); \ +} \ +catch(std::bad_alloc &) { sqlite3_result_error_nomem(ctx); } \ +catch(std::length_error &) { sqlite3_result_error_toobig(ctx); } \ +catch(std::out_of_range &) { sqlite3_result_error_code(ctx, SQLITE_RANGE);} \ +catch(std::logic_error &le) \ +{ \ + sqlite3_result_error(ctx, le.what(), std::strlen(le.what())); \ + sqlite3_result_error_code(ctx, SQLITE_MISUSE); \ +} \ +catch(std::exception & ex) \ +{ \ + sqlite3_result_error(ctx, ex.what(), std::strlen(ex.what())); \ +} \ +catch(...) {sqlite3_result_error_code(ctx, SQLITE_ERROR); } + + +#define BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(msg) \ +catch (boost::system::system_error & se) \ +{ \ + auto code = SQLITE_ERROR; \ + if (se.code().category() == boost::sqlite::sqlite_category()) \ + code = se.code().value(); \ + const auto pre = boost::sqlite::detail::get_message(se); \ + msg = boost::sqlite::error_info(pre).release(); \ + return code; \ +} \ +catch(std::bad_alloc &) { return SQLITE_NOMEM; } \ +catch(std::length_error &) { return SQLITE_TOOBIG; } \ +catch(std::out_of_range &) { return SQLITE_RANGE;} \ +catch(std::logic_error &le) \ +{ \ + msg = boost::sqlite::error_info(le.what()).release(); \ + return SQLITE_MISUSE; \ +} \ +catch(std::exception & ex) \ +{ \ + msg = boost::sqlite::error_info(ex.what()).release(); \ + return SQLITE_ERROR; \ +} \ +catch(...) {return SQLITE_ERROR; } \ + + +#define BOOST_SQLITE_CATCH_AND_RETURN() \ +catch (boost::system::system_error & se) \ +{ \ + auto code = SQLITE_ERROR; \ + if (se.code().category() == boost::sqlite::sqlite_category()) \ + code = se.code().value(); \ + return code; \ +} \ +catch(std::bad_alloc &) { return SQLITE_NOMEM; } \ +catch(std::length_error &) { return SQLITE_TOOBIG; } \ +catch(std::out_of_range &) { return SQLITE_RANGE;} \ +catch(std::logic_error &) { return SQLITE_MISUSE;} \ +catch(...) { return SQLITE_ERROR; } \ + +#endif + +#endif //BOOST_SQLITE_DETAIL_CATCH_HPP diff --git a/include/boost/sqlite/detail/config.hpp b/include/boost/sqlite/detail/config.hpp new file mode 100644 index 0000000..2126f60 --- /dev/null +++ b/include/boost/sqlite/detail/config.hpp @@ -0,0 +1,83 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_SQLITE_DETAIL_CONFIG_HPP +#define BOOST_SQLITE_DETAIL_CONFIG_HPP + +#include <boost/config.hpp> +#include <boost/core/detail/string_view.hpp> + +#if defined(BOOST_SQLITE_COMPILE_EXTENSION) +#include <sqlite3ext.h> +#define BOOST_SQLITE_COMPILING_EXTENSION 1 +#define BOOST_SQLITE_BEGIN_NAMESPACE namespace boost { namespace sqlite { inline namespace ext { +#define BOOST_SQLITE_END_NAMESPACE } } } +#else +#include <sqlite3.h> +#define BOOST_SQLITE_BEGIN_NAMESPACE namespace boost { namespace sqlite { +#define BOOST_SQLITE_END_NAMESPACE } } +#endif + +// copied from boost.json +#if defined(BOOST_SQLITE_DOCS) +# define BOOST_SQLITE_DECL +#else +# if (defined(BOOST_SQLITE_DYN_LINK) || defined(BOOST_ALL_DYN_LINK)) && !defined(BOOST_SQLITE_STATIC_LINK) +# if defined(BOOST_SQLITE_SOURCE) +# define BOOST_SQLITE_DECL BOOST_SYMBOL_EXPORT +# else +# define BOOST_SQLITE_DECL BOOST_SYMBOL_IMPORT +# endif +# endif // static lib +# ifndef BOOST_SQLITE_DECL +# define BOOST_SQLITE_DECL +# endif +# if !defined(BOOST_SQLITE_SOURCE) && !defined(BOOST_ALL_NO_LIB) && !defined(BOOST_SQLITE_NO_LIB) +# define BOOST_LIB_NAME boost_sqlite +# if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_SQLITE_DYN_LINK) +# define BOOST_DYN_LINK +# endif +# include <boost/config/auto_link.hpp> +# endif +#endif + +BOOST_SQLITE_BEGIN_NAMESPACE + + +#if defined(BOOST_SQLITE_COMPILE_EXTENSION) +extern const sqlite3_api_routines *sqlite3_api; +#endif + +using string_view = boost::core::string_view; + +BOOST_SQLITE_END_NAMESPACE + +#define BOOST_SQLITE_RETURN_EC(ev) \ +do \ +{ \ + static constexpr auto loc##__LINE__((BOOST_CURRENT_LOCATION)); \ + return ::boost::system::error_code(ev, boost::sqlite::sqlite_category(), &loc##__LINE__); \ +} \ +while (false) + +#define BOOST_SQLITE_ASSIGN_EC(ec, ev) \ +do \ +{ \ + static constexpr auto loc##__LINE__((BOOST_CURRENT_LOCATION)); \ + ec.assign(ev, boost::sqlite::sqlite_category(), &loc##__LINE__); \ +} \ +while (false) + +#if defined(BOOST_SQLITE_NO_VIRTUAL) +#define BOOST_SQLITE_VIRTUAL +#define BOOST_SQLITE_PURE +#else +#define BOOST_SQLITE_VIRTUAL virtual +#define BOOST_SQLITE_PURE = 0 +#endif + +#endif // BOOST_SQLITE_DETAIL_HPP
\ No newline at end of file diff --git a/include/boost/sqlite/detail/exception.hpp b/include/boost/sqlite/detail/exception.hpp new file mode 100644 index 0000000..9f29cbe --- /dev/null +++ b/include/boost/sqlite/detail/exception.hpp @@ -0,0 +1,48 @@ +// Copyright (c) 2023 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#ifndef BOOST_SQLITE_DETAIL_EXCEPTION_HPP +#define BOOST_SQLITE_DETAIL_EXCEPTION_HPP + +#include <boost/sqlite/detail/config.hpp> +#include <boost/sqlite/error.hpp> + +BOOST_SQLITE_BEGIN_NAMESPACE +namespace detail +{ + +BOOST_SQLITE_DECL +BOOST_NORETURN void throw_error_code(const boost::system::error_code & ec, + const boost::source_location & loc = BOOST_CURRENT_LOCATION); + +BOOST_SQLITE_DECL +BOOST_NORETURN void throw_error_code(const boost::system::error_code & ec, + const error_info & ei, + const boost::source_location & loc = BOOST_CURRENT_LOCATION); + +BOOST_SQLITE_DECL +BOOST_NORETURN void throw_out_of_range(const char * msg, + const boost::source_location & loc); + +BOOST_SQLITE_DECL +BOOST_NORETURN void throw_invalid_argument(const char * msg, + const boost::source_location & loc); + +inline core::string_view get_message(const system::system_error & se) +{ + auto ec_len = se.code().what().size(); + auto se_len = std::strlen(se.what()); + + if (ec_len == se_len) + return core::string_view(); + else + return core::string_view(se.what(), se_len - (ec_len + 2)); +} + + +} +BOOST_SQLITE_END_NAMESPACE + + +#endif //BOOST_SQLITE_DETAIL_EXCEPTION_HPP diff --git a/include/boost/sqlite/detail/scalar_function.hpp b/include/boost/sqlite/detail/scalar_function.hpp new file mode 100644 index 0000000..ee16a23 --- /dev/null +++ b/include/boost/sqlite/detail/scalar_function.hpp @@ -0,0 +1,172 @@ +// Copyright (c) 2023 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#ifndef BOOST_SQLITE_DETAIL_SCALAR_FUNCTION_HPP +#define BOOST_SQLITE_DETAIL_SCALAR_FUNCTION_HPP + +#include <boost/sqlite/detail/config.hpp> +#include <boost/sqlite/cstring_ref.hpp> +#include <boost/sqlite/memory.hpp> +#include <boost/sqlite/result.hpp> +#include <boost/sqlite/value.hpp> + +#include <boost/callable_traits/args.hpp> +#include <boost/callable_traits/return_type.hpp> +#include <boost/callable_traits/has_void_return.hpp> +#include <boost/core/span.hpp> + + +BOOST_SQLITE_BEGIN_NAMESPACE + +template<typename ... Args> +struct context; + +namespace detail +{ + +template<typename Func, typename ... Args, std::size_t Extent> +auto create_scalar_function_impl(sqlite3 * db, + cstring_ref name, + Func && func, + int flags, + std::tuple<context<Args...>, boost::span<value, Extent>> * , + std::false_type /* void return */, + std::false_type /* is pointer */) -> int +{ + using func_type = typename std::decay<Func>::type; + return sqlite3_create_function_v2( + db, name.c_str(), + Extent == boost::dynamic_extent ? -1 : static_cast<int>(Extent), + SQLITE_UTF8 | flags, + new (memory_tag{}) func_type(std::forward<Func>(func)), + +[](sqlite3_context* ctx, int len, sqlite3_value** args) + { + auto cc = context<Args...>(ctx); + auto aa = reinterpret_cast<value*>(args); + auto &f = *reinterpret_cast<func_type*>(sqlite3_user_data(ctx)); + boost::span<value, Extent> vals{aa, static_cast<std::size_t>(len)}; + + execute_context_function(ctx, f, cc, vals); + }, nullptr, nullptr, + +[](void * ptr) noexcept {delete_(static_cast<func_type*>(ptr));} + ); +} + +template<typename Func, typename ... Args, std::size_t Extent> +auto create_scalar_function_impl(sqlite3 * db, + cstring_ref name, + Func && func, int flags, + std::tuple<context<Args...>, boost::span<value, Extent>> * , + std::true_type /* void return */, + std::false_type /* is pointer */) -> int +{ + using func_type = typename std::decay<Func>::type; + return sqlite3_create_function_v2( + db, + name.c_str(), + (Extent == boost::dynamic_extent) ? -1 : static_cast<int>(Extent), + SQLITE_UTF8 | flags, + new (memory_tag{}) func_type(std::forward<Func>(func)), + +[](sqlite3_context* ctx, int len, sqlite3_value** args) + { + auto cc = context<Args...>(ctx); + auto aa = reinterpret_cast<value*>(args); + auto &f = *reinterpret_cast<func_type*>(sqlite3_user_data(ctx)); + boost::span<value, Extent> vals{aa, static_cast<std::size_t>(len)}; + + execute_context_function( + ctx, + [&]() + { + f(cc, vals); + return result<void>(); + }); + + }, nullptr, nullptr, + +[](void * ptr){delete_(static_cast<func_type*>(ptr));} + ); +} + + +template<typename Func, typename ... Args, std::size_t Extent> +auto create_scalar_function_impl(sqlite3 * db, + cstring_ref name, + Func * func, int flags, + std::tuple<context<Args...>, boost::span<value, Extent>> * , + std::false_type /* void return */, + std::true_type /* is pointer */) -> int +{ + return sqlite3_create_function_v2( + db, name.c_str(), + Extent == boost::dynamic_extent ? -1 : static_cast<int>(Extent), + SQLITE_UTF8 | flags, + reinterpret_cast<void*>(func), + +[](sqlite3_context* ctx, int len, sqlite3_value** args) + { + auto cc = context<Args...>(ctx); + auto aa = reinterpret_cast<value*>(args); + auto f = reinterpret_cast<Func*>(sqlite3_user_data(ctx)); + + boost::span<value, Extent> vals{aa, static_cast<std::size_t>(len)}; + + execute_context_function( + ctx, f, cc, vals); + }, nullptr, nullptr, nullptr); +} + +template<typename Func, typename ... Args, std::size_t Extent> +auto create_scalar_function_impl(sqlite3 * db, + cstring_ref name, + Func * func, int flags, + std::tuple<context<Args...>, boost::span<value, Extent>> * , + std::true_type /* void return */, + std::true_type /* is pointer */) -> int +{ + return sqlite3_create_function_v2( + db, + name.c_str(), + (Extent == boost::dynamic_extent) ? -1 : static_cast<int>(Extent), + SQLITE_UTF8 | flags, + reinterpret_cast<void*>(func), + +[](sqlite3_context* ctx, int len, sqlite3_value** args) + { + auto cc = context<Args...>(ctx); + auto aa = reinterpret_cast<value*>(args); + auto f = *reinterpret_cast<Func*>(sqlite3_user_data(ctx)); + boost::span<value, Extent> vals{aa, static_cast<std::size_t>(len)}; + execute_context_function( + ctx, + [&]() + { + f(cc, vals); + return result<void>(); + }); + + }, nullptr, nullptr, nullptr); +} + +template<typename Func> +auto create_scalar_function(sqlite3 * db, + cstring_ref name, + Func && func, + int flags) + -> decltype(create_scalar_function_impl( + db, name, std::forward<Func>(func), flags, + static_cast<callable_traits::args_t<Func>*>(nullptr), + callable_traits::has_void_return<Func>{}, + std::is_pointer<typename std::decay<Func>::type>{} + )) +{ + return create_scalar_function_impl(db, name, std::forward<Func>(func), flags, + static_cast<callable_traits::args_t<Func>*>(nullptr), + callable_traits::has_void_return<Func>{}, + std::is_pointer<typename std::decay<Func>::type>{}); +} + + +} + +BOOST_SQLITE_END_NAMESPACE + +#endif //BOOST_SQLITE_DETAIL_SCALAR_FUNCTION_HPP diff --git a/include/boost/sqlite/detail/vtable.hpp b/include/boost/sqlite/detail/vtable.hpp new file mode 100644 index 0000000..f85f44c --- /dev/null +++ b/include/boost/sqlite/detail/vtable.hpp @@ -0,0 +1,611 @@ +// Copyright (c) 2023 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#ifndef BOOST_SQLITE_DETAIL_VTABLE_HPP +#define BOOST_SQLITE_DETAIL_VTABLE_HPP + +#include <boost/sqlite/detail/config.hpp> +#include <boost/sqlite/detail/catch.hpp> +#include <boost/sqlite/vtable.hpp> + +BOOST_SQLITE_BEGIN_NAMESPACE +namespace detail +{ +struct vtab_impl +{ + +template<typename Module> +static int connect(sqlite3 * db, void * pAux, int argc, const char * const * argv, + sqlite3_vtab **ppVTab, char** errMsg) +{ + using table_type = typename Module::table_type; + auto &impl = *static_cast<Module*>(pAux); + BOOST_SQLITE_TRY + { + result<table_type> rtab = impl.connect( + sqlite::connection(db, false), + argc, argv); + + if (rtab.has_error()) + return extract_error(*errMsg, rtab); + + auto tab = make_unique<table_type>(std::move(*rtab)); + tab->db_ = db; + auto code = sqlite3_declare_vtab(db, tab->declaration()); + if (code != SQLITE_OK) + return code; + + sqlite::vtab::module_config cfg{db}; + auto r = tab->config(cfg); + if (r.has_error()) + return extract_error(*errMsg, r); + + *ppVTab = tab.release(); + + return SQLITE_OK; + } + BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(*errMsg) +} + + +template<typename Module> +static int create(sqlite3 * db, void * pAux, int argc, const char * const * argv, + sqlite3_vtab **ppVTab, char** errMsg) +{ + using table_type = typename Module::table_type; + auto &impl = *static_cast<Module*>(pAux); + BOOST_SQLITE_TRY + { + result<table_type> rtab = impl.create( + sqlite::connection(db, false), + argc, argv); + + if (rtab.has_error()) + return extract_error(*errMsg, rtab); + + auto tab = make_unique<table_type>(std::move(*rtab)); + tab->db_ = db; + + auto code = sqlite3_declare_vtab(db, tab->declaration()); + if (code != SQLITE_OK) + return code; + + sqlite::vtab::module_config mc{db}; + auto r = tab->config(mc); + if (r.has_error()) + return extract_error(*errMsg, r); + + *ppVTab = tab.release(); + + return SQLITE_OK; + } + BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(*errMsg) +} + +template<typename Table> +static int disconnect(sqlite3_vtab * tab) +{ + BOOST_SQLITE_TRY + { + auto tb = static_cast<Table*>(tab); + tb->~Table(); + sqlite3_free(tb); + return SQLITE_OK; + } + BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(tab->zErrMsg); +} + +template<typename Table> +static int destroy(sqlite3_vtab * tab) +{ + BOOST_SQLITE_TRY + { + auto tb = static_cast<Table*>(tab); + auto res = tb->destroy(); + tb->~Table(); + sqlite3_free(tb); + if (res.has_error()) + return std::move(res).error().code; + return SQLITE_OK; + } + BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(tab->zErrMsg); +} + +template<typename Module, typename Table> +static void assign_create(sqlite3_module & md, const Module &, + const sqlite::vtab::eponymous_module<Table> & base) +{ + md.xConnect = md.xCreate = &connect<Module>; + md.xDisconnect = md.xDestroy = &disconnect<Table>; + if (base.eponymous_only()) + md.xCreate = nullptr; +} + +template<typename Module, typename Table> +static void assign_create(sqlite3_module & md, const Module &, + const sqlite::vtab::module<Table> &) +{ + md.xConnect = &connect<Module>; + md.xDisconnect = &disconnect<Table>; + md.xCreate = &create<Module>; + md.xDestroy = &destroy<Table>; +} + +template<typename Table> +static int open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) +{ + auto tab = static_cast<Table *>(pVTab); + + BOOST_SQLITE_TRY + { + auto res = tab->open(); + if (res.has_error()) + return extract_error(pVTab->zErrMsg, res); + *ppCursor = new (memory_tag{}) typename Table::cursor_type(std::move(*res)); + return SQLITE_OK; + } + BOOST_SQLITE_CATCH_AND_RETURN(); +} + +template<typename Cursor> +static int close(sqlite3_vtab_cursor * cursor) +{ + auto p = static_cast<Cursor *>(cursor); + + BOOST_SQLITE_TRY + { + p->~Cursor(); + } + BOOST_SQLITE_CATCH_AND_RETURN(); + + sqlite3_free(p); + return SQLITE_OK; + +} + +template<typename Table> +static int best_index(sqlite3_vtab *pVTab, sqlite3_index_info* info) +{ + BOOST_SQLITE_TRY + { + auto tb = static_cast<Table*>(pVTab); + + sqlite::vtab::index_info ii(tb->db_, info); + auto r = tb->best_index(ii); + if (r.has_error()) + return extract_error(pVTab->zErrMsg, r); + + return SQLITE_OK; + } + BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg); +} + + +template<typename Cursor> +static int filter(sqlite3_vtab_cursor* pCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast<Cursor*>(pCursor); + + auto r = cr->filter(idxNum, idxStr, + boost::span<value>{reinterpret_cast<value*>(argv), + static_cast<std::size_t>(argc)}); + if (r.has_error()) + return extract_error(pCursor->pVtab->zErrMsg, r); + + return SQLITE_OK; + } + BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pCursor->pVtab->zErrMsg); +} + + +template<typename Cursor> +static int next(sqlite3_vtab_cursor* pCursor) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast<Cursor*>(pCursor); + + auto r = cr->next(); + if (r.has_error()) + return extract_error(pCursor->pVtab->zErrMsg, r); + + return SQLITE_OK; + } + BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pCursor->pVtab->zErrMsg); +} + + +template<typename Cursor> +static int eof(sqlite3_vtab_cursor* pCursor) +{ + return static_cast<Cursor*>(pCursor)->eof() ? 1 : 0; +} + +template<typename Cursor> +static auto column(sqlite3_vtab_cursor* pCursor, + sqlite3_context * ctx, int idx) + -> typename std::enable_if<!std::is_void<typename Cursor::column_type>::value, int>::type +{ +#if SQLITE_VERSION_NUMBER >= 3032000 + bool no_change = sqlite3_vtab_nochange(ctx) != 0; +#else + bool no_change = false; +#endif + auto cr = static_cast<Cursor*>(pCursor); + execute_context_function( + ctx, + [&]{ + return cr->column(idx, no_change); + }); + + return SQLITE_OK; +} + + +template<typename Cursor> +static auto column(sqlite3_vtab_cursor* pCursor, + sqlite3_context * ctx, int idx) + -> typename std::enable_if<std::is_void<typename Cursor::column_type>::value, int>::type +{ +#if SQLITE_VERSION_NUMBER >= 3032000 + bool no_change = sqlite3_vtab_nochange(ctx) != 0; +#else + bool no_change = false; +#endif + auto cr = static_cast<Cursor*>(pCursor); + BOOST_SQLITE_TRY + { + cr->column(context<>{ctx}, idx, no_change); + } + BOOST_SQLITE_CATCH_RESULT(ctx); + return SQLITE_OK; +} + +template<typename Cursor> +static int row_id(sqlite3_vtab_cursor* pCursor, sqlite3_int64 *pRowid) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast<Cursor*>(pCursor); + + auto r = cr->row_id(); + if (r.has_error()) + return extract_error(pCursor->pVtab->zErrMsg, r); + *pRowid = *r; + return SQLITE_OK; + } + BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pCursor->pVtab->zErrMsg); +} + +template<typename Table> +static int update(sqlite3_vtab * pVTab, int argc, sqlite3_value ** argv, sqlite3_int64 * pRowid) +{ + using table_type = Table; + BOOST_SQLITE_TRY + { + auto & mod = *static_cast<table_type *>(pVTab); + auto db = mod.db_; + if (argc == 1 && sqlite3_value_type(argv[0]) != SQLITE_NULL) + { + auto r = mod.delete_(sqlite::value(*argv)); + if (r.has_error()) + return extract_error(pVTab->zErrMsg, r); + } + else if (argc > 1 && sqlite3_value_type(argv[0]) == SQLITE_NULL) + { + auto r = mod.insert(value{argv[1]}, + boost::span<value>{reinterpret_cast<value *>(argv + 2), + static_cast<std::size_t>(argc - 2)}, + sqlite3_vtab_on_conflict(db)); + if (r.has_error()) + return extract_error(pVTab->zErrMsg, r); + *pRowid = r.value(); + } + else if (argc > 1 && sqlite3_value_type(argv[0]) != SQLITE_NULL) + { + auto r = mod.update(sqlite::value(*argv), value{argv[1]}, // ID + boost::span<value>{reinterpret_cast<value *>(argv + 2), + static_cast<std::size_t>(argc - 2)}, + sqlite3_vtab_on_conflict(db)); + + if (r.has_error()) + return extract_error(pVTab->zErrMsg, r); + *pRowid = r.value(); + } + else + { + pVTab->zErrMsg = sqlite3_mprintf("Misuse of update api"); + return SQLITE_MISUSE; + } + + } + BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg) + return SQLITE_OK; +} + +template<typename Module> +static void assign_update(sqlite3_module & md, const Module &, + std::true_type /* modifiable */) +{ + md.xUpdate = &update<typename Module::table_type>; +} + +template<typename Module> +static void assign_update(sqlite3_module & /*md*/, const Module &, + std::false_type /* modifiable */) +{ +} + +template<typename Table> +static int begin(sqlite3_vtab* pVTab) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast<Table*>(pVTab); + + auto r = cr->begin(); + if (r.has_error()) + return extract_error(pVTab->zErrMsg, r); + + return SQLITE_OK; + } + BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg); +} + +template<typename Table> +static int sync(sqlite3_vtab* pVTab) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast<Table*>(pVTab); + + auto r = cr->sync(); + if (r.has_error()) + return extract_error(pVTab->zErrMsg, r); + + return SQLITE_OK; + } + BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg); +} + +template<typename Table> +static int commit(sqlite3_vtab* pVTab) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast<Table*>(pVTab); + + auto r = cr->commit(); + if (r.has_error()) + return extract_error(pVTab->zErrMsg, r); + + return SQLITE_OK; + } + BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg); +} + +template<typename Table> +static int rollback(sqlite3_vtab* pVTab) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast<Table*>(pVTab); + + auto r = cr->rollback(); + if (r.has_error()) + return extract_error(pVTab->zErrMsg, r); + + return SQLITE_OK; + } + BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg); +} + +template<typename Module> +static void assign_transaction(sqlite3_module & /*md*/, const Module &, + std::false_type /* modifiable */) +{ +} + +template<typename Module> +static void assign_transaction(sqlite3_module & md, const Module &, + std::true_type /* modifiable */) +{ + md.xBegin = &begin <typename Module::table_type>; + md.xSync = &sync <typename Module::table_type>; + md.xCommit = &commit <typename Module::table_type>; + md.xRollback = &rollback<typename Module::table_type>; +} + +template<typename Table> +static int find_function(sqlite3_vtab *pVtab, int nArg, const char *zName, + void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), + void **ppArg) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast<Table*>(pVtab); + + auto r = cr->find_function( + nArg, zName, sqlite::vtab::function_setter(pxFunc, ppArg)); + if (r.has_error()) + return extract_error(pVtab->zErrMsg, r); + + return SQLITE_OK; + } + BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVtab->zErrMsg); +} + +template<typename Module> +static void assign_find_function(sqlite3_module & /*md*/, const Module &, + std::false_type /* overloadable */) +{ +} + +template<typename Module> +static void assign_find_function(sqlite3_module & md, const Module &, + std::true_type /* overloadable */) +{ + md.xFindFunction = &find_function<typename Module::table_type>; +} + +template<typename Table> +static int rename(sqlite3_vtab* pVTab, const char * name) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast<Table*>(pVTab); + + auto r = cr->rename(name); + if (r.has_error()) + return extract_error(pVTab->zErrMsg, r); + + return SQLITE_OK; + } + BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg); +} + +template<typename Module> +static void assign_rename(sqlite3_module & /*md*/, const Module &, + std::false_type /* renamable */) +{ +} + +template<typename Module> +static void assign_rename(sqlite3_module & md, const Module &, + std::true_type /* renamable */) +{ + md.xRename = &rename<typename Module::table_type>; +} +#if SQLITE_VERSION_NUMBER >= 3007007 + +template<typename Table> +static int savepoint(sqlite3_vtab* pVTab, int i) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast<Table*>(pVTab); + + auto r = cr->savepoint(i); + if (r.has_error()) + return extract_error(pVTab->zErrMsg, r); + + return SQLITE_OK; + } + BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg); +} + +template<typename Table> +static int release(sqlite3_vtab* pVTab, int i) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast<Table*>(pVTab); + + auto r = cr->release(i); + if (r.has_error()) + return extract_error(pVTab->zErrMsg, r); + + return SQLITE_OK; + } + BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg); +} + +template<typename Table> +static int rollback_to(sqlite3_vtab* pVTab, int i) +{ + BOOST_SQLITE_TRY + { + auto cr = static_cast<Table*>(pVTab); + + auto r = cr->rollback_to(i); + if (r.has_error()) + return extract_error(pVTab->zErrMsg, r); + + return SQLITE_OK; + } + BOOST_SQLITE_CATCH_ASSIGN_STR_AND_RETURN(pVTab->zErrMsg); +} + +template<typename Module> +static void assign_recursive_transaction(sqlite3_module & /*md*/, const Module &, + std::false_type /* recursive_transaction */) +{ +} + +template<typename Module> +static void assign_recursive_transaction(sqlite3_module & md, const Module &, + std::true_type /* recursive_transaction */) +{ + md.xSavepoint = &savepoint <typename Module::table_type>; + md.xRelease = &release <typename Module::table_type>; + md.xRollbackTo = &rollback_to<typename Module::table_type>; +} + +#endif + +#if SQLITE_VERSION_NUMBER >= 3026000 + +template<typename Table> +static void assign_shadow_name(sqlite3_module & /*md*/, const sqlite::vtab::module<Table> &) {} + +template<typename Table> +static void assign_shadow_name(sqlite3_module & /*md*/, const sqlite::vtab::eponymous_module<Table> &) {} + +template<typename Module, + bool (*Func)(const char *) = &Module::shadow_name> +static void assign_shadow_name(sqlite3_module & md, const Module & mod) +{ + md.xShadowName = +[](const char * name){return Func(name) != 0;}; +} + +#endif + +}; + +template<typename Module> +const sqlite3_module make_module(const Module & mod) +{ + sqlite3_module md; + std::memset(&md, 0, sizeof(sqlite3_module)); + +#if SQLITE_VERSION_NUMBER < 3007007 + md.iVersion = 1; +#elif SQLITE_VERSION_NUMBER < 3026000 + md.iVersion = 2; +#else + md.iVersion = 3; +#endif + using table_type = typename Module::table_type; + using cursor_type = typename table_type::cursor_type; + vtab_impl::assign_create(md, mod, mod); + md.xBestIndex = &vtab_impl::best_index<table_type>; + md.xOpen = &vtab_impl::open <table_type>; + md.xClose = &vtab_impl::close <cursor_type>; + md.xFilter = &vtab_impl::filter <cursor_type>; + md.xNext = &vtab_impl::next <cursor_type>; + md.xEof = &vtab_impl::eof <cursor_type>; + md.xColumn = &vtab_impl::column <cursor_type>; + md.xRowid = &vtab_impl::row_id <cursor_type>; + vtab_impl::assign_update (md, mod, std::is_base_of<sqlite::vtab::modifiable, table_type>{}); + vtab_impl::assign_transaction (md, mod, std::is_base_of<sqlite::vtab::transaction, table_type>{}); + vtab_impl::assign_find_function(md, mod, std::is_base_of<sqlite::vtab::overload_functions, table_type>{}); + vtab_impl::assign_rename (md, mod, std::is_base_of<sqlite::vtab::renamable, table_type>{}); +#if SQLITE_VERSION_NUMBER >= 3007007 + vtab_impl::assign_recursive_transaction(md, mod, std::is_base_of<sqlite::vtab::recursive_transaction, table_type>{}); +#endif +#if SQLITE_VERSION_NUMBER >= 3026000 + vtab_impl::assign_shadow_name(md, mod); +#endif + return md; +} + +} + +BOOST_SQLITE_END_NAMESPACE + +#endif //BOOST_SQLITE_DETAIL_VTABLE_HPP diff --git a/include/boost/sqlite/detail/window_function.hpp b/include/boost/sqlite/detail/window_function.hpp new file mode 100644 index 0000000..df8475c --- /dev/null +++ b/include/boost/sqlite/detail/window_function.hpp @@ -0,0 +1,238 @@ +// Copyright (c) 2023 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#ifndef BOOST_SQLITE_IMPL_WINDOW_FUNCTION_HPP +#define BOOST_SQLITE_IMPL_WINDOW_FUNCTION_HPP +#if SQLITE_VERSION_NUMBER >= 3025000 + +#include <boost/sqlite/detail/config.hpp> +#include <boost/sqlite/cstring_ref.hpp> +#include <boost/sqlite/memory.hpp> +#include <boost/sqlite/result.hpp> +#include <boost/sqlite/value.hpp> + +BOOST_SQLITE_BEGIN_NAMESPACE + +namespace detail +{ + +template<typename Func> +struct window_function_maker +{ + void * mem; + + template<typename ... Args> + Func* operator()(Args && ... args) + { + return new (mem) Func(std::forward<Args>(args)...); + } +}; + +template<typename Func, typename Args> +int create_window_function(sqlite3 * db, cstring_ref name, Args && args, int flags, + std::true_type /* is void */) +{ + using args_type = callable_traits::args_t<decltype(&Func::step)>; + using span_type = typename std::tuple_element<1U, args_type>::type; + using func_type = typename std::decay<Func>::type; + using func_args_type = typename std::decay<Args>::type; + + return sqlite3_create_window_function( + db, name.c_str(), + span_type::extent == boost::dynamic_extent ? -1 : static_cast<int>(span_type::extent), + SQLITE_UTF8 | flags, + new (memory_tag{}) func_args_type(std::forward<Args>(args)), + +[](sqlite3_context* ctx, int len, sqlite3_value** args) noexcept //xStep + { + auto aa = reinterpret_cast<value*>(args); + auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx)); + auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0)); + + execute_context_function( + ctx, + [&]() -> result<void> + { + if (c == nullptr) + { + auto p = sqlite3_aggregate_context(ctx, sizeof(func_type)); + if (!p) + return error(SQLITE_NOMEM); + c = mp11::tuple_apply(window_function_maker<func_type>{p}, *fa); + } + c->step(span_type{aa, static_cast<std::size_t>(len)}); + return {}; + }); + + }, + [](sqlite3_context* ctx) // xFinal + { + auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx)); + auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0)); + + execute_context_function( + ctx, + [&]() -> result<decltype(c->value())> + { + if (c == nullptr) + { + auto p = sqlite3_aggregate_context(ctx, sizeof(func_type)); + if (!p) + return error(SQLITE_NOMEM); + c = mp11::tuple_apply(window_function_maker<func_type>{p}, *fa); + } + struct reaper {void operator()(func_type * c) { c->~func_type();}}; + std::unique_ptr<func_type, reaper> cl{c}; + + return c->value(); + }); + }, + [](sqlite3_context* ctx) //xValue + { + auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx)); + auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0)); + execute_context_function( + ctx, + [&]() -> result<decltype(c->value())> + { + if (c == nullptr) + { + auto p = sqlite3_aggregate_context(ctx, sizeof(func_type)); + if (!p) + return error(SQLITE_NOMEM); + c = mp11::tuple_apply(window_function_maker<func_type>{p}, *fa); + } + return c->value(); + }); + + }, + +[](sqlite3_context* ctx, int len, sqlite3_value** args) // xInverse + { + auto aa = reinterpret_cast<value*>(args); + auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx)); + auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0)); + execute_context_function( + ctx, + [&]() -> result<decltype(c->inverse(span_type{aa, static_cast<std::size_t>(len)}))> + { + if (c == nullptr) + { + auto p = sqlite3_aggregate_context(ctx, sizeof(func_type)); + if (!p) + return error(SQLITE_NOMEM); + c = mp11::tuple_apply(window_function_maker<func_type>{p}, *fa); + } + c->inverse(span_type{aa, static_cast<std::size_t>(len)}); + return {}; + }); + + }, + [](void * ptr) /* xDestroy */ { delete_(static_cast<func_type*>(ptr));} + ); +} + +template<typename Func, typename Args> +int create_window_function(sqlite3 * db, cstring_ref name, Args && args, int flags, + std::false_type /* is void */) +{ + using args_type = callable_traits::args_t<decltype(&Func::step)>; + using span_type = typename std::tuple_element<1U, args_type>::type; + using func_type = typename std::decay<Func>::type; + using func_args_type = typename std::decay<Args>::type; + + return sqlite3_create_window_function( + db, name.c_str(), + span_type::extent == boost::dynamic_extent ? -1 : static_cast<int>(span_type::extent), + SQLITE_UTF8 | flags, + new (memory_tag{}) func_args_type(std::forward<Args>(args)), + +[](sqlite3_context* ctx, int len, sqlite3_value** args) noexcept //xStep + { + auto aa = reinterpret_cast<value*>(args); + auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx)); + auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0)); + + execute_context_function( + ctx, + [&]() -> result<decltype(c->step(span_type{aa, static_cast<std::size_t>(len)}))> + { + if (c == nullptr) + { + auto p = sqlite3_aggregate_context(ctx, sizeof(func_type)); + if (!p) + return {system::in_place_error, error(SQLITE_NOMEM)}; + c = mp11::tuple_apply(window_function_maker<func_type>{p}, *fa); + } + return c->step(span_type{aa, static_cast<std::size_t>(len)}); + }); + + }, + [](sqlite3_context* ctx) // xFinal + { + auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx)); + auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0)); + execute_context_function( + ctx, + [&]() -> result<decltype(c->value())> + { + if (c == nullptr) + { + auto p = sqlite3_aggregate_context(ctx, sizeof(func_type)); + if (!p) + return {system::in_place_error, error(SQLITE_NOMEM)}; + c = mp11::tuple_apply(window_function_maker<func_type>{p}, *fa); + } + + struct reaper {void operator()(func_type * c) { c->~func_type();}}; + std::unique_ptr<func_type, reaper> cl{c}; + return c->value(); + }); + }, + [](sqlite3_context* ctx) //xValue + { + auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx)); + auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0)); + execute_context_function( + ctx, + [&]() -> result<decltype(c->value())> + { + if (c == nullptr) + { + auto p = sqlite3_aggregate_context(ctx, sizeof(func_type)); + if (!p) + return {system::in_place_error, error(SQLITE_NOMEM)}; + c = mp11::tuple_apply(window_function_maker<func_type>{p}, *fa); + } + return c->value(); + }); + + }, + +[](sqlite3_context* ctx, int len, sqlite3_value** args) // xInverse + { + auto aa = reinterpret_cast<value*>(args); + auto fa = reinterpret_cast<func_args_type*>(sqlite3_user_data(ctx)); + auto c = static_cast<func_type*>(sqlite3_aggregate_context(ctx, 0)); + execute_context_function( + ctx, + [&]() -> result<decltype(c->inverse(span_type{aa, static_cast<std::size_t>(len)}))> + { + if (c == nullptr) + { + auto p = sqlite3_aggregate_context(ctx, sizeof(func_type)); + if (!p) + return {system::in_place_error, error(SQLITE_NOMEM)}; + c = mp11::tuple_apply(window_function_maker<func_type>{p}, *fa); + } + return c->inverse(span_type{aa, static_cast<std::size_t>(len)}); + }); + + }, + [](void * ptr) /* xDestroy */ { delete_(static_cast<func_type*>(ptr));} + ); +} + + +} + +BOOST_SQLITE_END_NAMESPACE +#endif // SQLITE_VERSION +#endif //BOOST_SQLITE_IMPL_WINDOW_FUNCTION_HPP |