summaryrefslogtreecommitdiff
path: root/include/boost/sqlite/detail
diff options
context:
space:
mode:
authorJohn Turner <jturner.usa@gmail.com>2025-09-14 00:16:10 -0400
committerJohn Turner <jturner.usa@gmail.com>2025-09-14 00:16:10 -0400
commitefcea3a80da7c4479d5fe168435ecc9fd06bdc72 (patch)
tree5cb0177e17b1b00a177f2e830e809f606334571b /include/boost/sqlite/detail
downloadsqlite-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.hpp166
-rw-r--r--include/boost/sqlite/detail/catch.hpp169
-rw-r--r--include/boost/sqlite/detail/config.hpp83
-rw-r--r--include/boost/sqlite/detail/exception.hpp48
-rw-r--r--include/boost/sqlite/detail/scalar_function.hpp172
-rw-r--r--include/boost/sqlite/detail/vtable.hpp611
-rw-r--r--include/boost/sqlite/detail/window_function.hpp238
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