boost_sqlite 1
A sqlite C++ library
Loading...
Searching...
No Matches
aggregate_function.hpp
1// Copyright (c) 2023 Klemens D. Morgenstern
2//
3// Distributed under the Boost Software License, Version 1.0. (See accompanying
4// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5#ifndef BOOST_SQLITE_DETAIL_AGGREGATE_FUNCTION_HPP
6#define BOOST_SQLITE_DETAIL_AGGREGATE_FUNCTION_HPP
7
8#include <boost/sqlite/detail/config.hpp>
9#include <boost/sqlite/detail/catch.hpp>
10#include <boost/sqlite/error.hpp>
11#include <boost/sqlite/cstring_ref.hpp>
12#include <boost/sqlite/memory.hpp>
13#include <boost/sqlite/result.hpp>
14#include <boost/sqlite/value.hpp>
15
16#include <boost/callable_traits/args.hpp>
17#include <boost/callable_traits/return_type.hpp>
18#include <boost/callable_traits/has_void_return.hpp>
19#include <boost/core/span.hpp>
20
21
22BOOST_SQLITE_BEGIN_NAMESPACE
23
24namespace detail
25{
26
27template<typename Func>
28int create_aggregate_function(sqlite3 * db, cstring_ref name, Func && func,
29 std::true_type /* void return */)
30{
31 using args_type = callable_traits::args_t<decltype(&Func::step)>;
32 using context_type = typename std::remove_reference<typename std::tuple_element<1u, args_type>::type>::type;
33 using span_type = typename std::tuple_element<2U, args_type>::type;
34 using func_type = typename std::decay<Func>::type;
35
36 return sqlite3_create_function_v2(
37 db, name.c_str(),
38 span_type::extent == boost::dynamic_extent ? -1 : static_cast<int>(span_type::extent),
39 SQLITE_UTF8,
40 new (memory_tag{}) func_type(std::forward<Func>(func)),
41 nullptr,
42 +[](sqlite3_context* ctx, int len, sqlite3_value** args)
43 {
44 auto aa = reinterpret_cast<value*>(args);
45 auto f = reinterpret_cast<Func*>(sqlite3_user_data(ctx));
46 auto c = static_cast<context_type*>(sqlite3_aggregate_context(ctx, 0));
47
48 execute_context_function(
49 ctx,
50 [&]() -> result<void>
51 {
52 if (c == nullptr)
53 {
54 auto p = sqlite3_aggregate_context(ctx, sizeof(context_type));
55 if (!p)
56 return error(SQLITE_NOMEM);
57 c = new (p) context_type();
58 }
59 f->step(*c, span_type{aa, static_cast<std::size_t>(len)});
60 return {};
61 });
62 },
63 [](sqlite3_context* ctx)
64 {
65 auto f = reinterpret_cast<Func*>(sqlite3_user_data(ctx));
66 auto c = static_cast<context_type*>(sqlite3_aggregate_context(ctx, 0));
67
68 execute_context_function(
69 ctx,
70 [&]() -> result<decltype(f->final(*c))>
71 {
72 if (c == nullptr)
73 {
74 auto p = sqlite3_aggregate_context(ctx, sizeof(context_type));
75 if (!p)
76 return error(SQLITE_NOMEM);
77 c = new (p) context_type();
78 }
79 struct reaper {void operator()(context_type * c) { c->~context_type();}};
80 std::unique_ptr<context_type, reaper> cl{c};
81 return f->final(*c);
82 });
83 },
84 [](void * ptr) noexcept { delete_(static_cast<func_type*>(ptr));}
85 );
86}
87
88
89template<typename Func>
90int create_aggregate_function(sqlite3 * db, cstring_ref name, Func && func,
91 std::false_type /* void return */)
92{
93 using args_type = callable_traits::args_t<decltype(&Func::step)>;
94 using context_type = typename std::remove_reference<typename std::tuple_element<1u, args_type>::type>::type;
95 using span_type = typename std::tuple_element<2U, args_type>::type;
96 using func_type = typename std::decay<Func>::type;
97
98 return sqlite3_create_function_v2(
99 db, name.c_str(),
100 span_type::extent == boost::dynamic_extent ? -1 : static_cast<int>(span_type::extent),
101 SQLITE_UTF8,
102 new (memory_tag{}) func_type(std::forward<Func>(func)),
103 nullptr,
104 +[](sqlite3_context* ctx, int len, sqlite3_value** args)
105 {
106 auto aa = reinterpret_cast<value*>(args);
107 auto f = reinterpret_cast<Func*>(sqlite3_user_data(ctx));
108 auto c = static_cast<context_type*>(sqlite3_aggregate_context(ctx, 0));
109
110 execute_context_function(
111 ctx,
112 [&]() -> result<void>
113 {
114 if (c == nullptr)
115 {
116 auto p = sqlite3_aggregate_context(ctx, sizeof(context_type));
117 if (!p)
118 return error(SQLITE_NOMEM);
119 c = new (p) context_type();
120 }
121 f->step(*c, span_type{aa, static_cast<std::size_t>(len)});
122 return {};
123 });
124 },
125 [](sqlite3_context* ctx)
126 {
127 auto f = reinterpret_cast<Func*>(sqlite3_user_data(ctx));
128 auto c = static_cast<context_type*>(sqlite3_aggregate_context(ctx, 0));
129
130 execute_context_function(
131 ctx,
132 [&]() -> result<decltype(f->final(*c))>
133 {
134 if (c == nullptr)
135 {
136 auto p = sqlite3_aggregate_context(ctx, sizeof(context_type));
137 if (!p)
138 return error(SQLITE_NOMEM);
139 c = new (p) context_type();
140 }
141 struct reaper {void operator()(context_type * c) { c->~context_type();}};
142 std::unique_ptr<context_type, reaper> cl{c};
143 return f->final(*c);
144 });
145 },
146 [](void * ptr) noexcept { delete_(static_cast<func_type*>(ptr));}
147 );
148}
149
150}
151
152BOOST_SQLITE_END_NAMESPACE
153
154
155#endif //BOOST_SQLITE_DETAIL_AGGREGATE_FUNCTION_HPP