Cppgres
Build Postgres extensions in C++
Loading...
Searching...
No Matches
aggregate.hpp
1#pragma once
2
3#include <memory>
4#include <type_traits>
5#include <utility>
6
7#include "function.hpp"
8#include "imports.h"
9
10namespace cppgres {
11
12template <class T, class... Args>
13concept aggregate = requires(T t, Args &&...args) {
14 { t.update(args...) };
15};
16
17template <class T, class... Args>
18concept finalizable_aggregate = aggregate<T, Args...> && requires(T t) {
19 { t.finalize() } -> convertible_into_datum;
20};
21
22template <class T, class... Args>
23concept serializable_aggregate = aggregate<T, Args...> && requires(T t, bytea &ba) {
24 { t.serialize() } -> std::same_as<bytea>;
25 { T(ba) } -> std::same_as<T>;
26};
27
28template <class T, class... Args>
29concept combinable_aggregate = aggregate<T, Args...> && requires(T &&t, T &&t1) {
30 { T(t, t1) } -> std::same_as<T>;
31};
32
33template <class Agg, typename... Args>
34Agg *construct_aggregate_state(abstract_memory_context &ctx, Args &&...args) {
35 auto *state = ctx.template alloc<Agg>();
36 std::construct_at(state, std::forward<Args>(args)...);
37 if constexpr (!std::is_trivially_destructible_v<Agg>) {
38 ctx.register_reset_callback([](void *arg) { std::destroy_at(static_cast<Agg *>(arg)); }, state);
39 }
40 return state;
41}
42
43template <class Agg, typename... Args>
44Agg *construct_aggregate_state(abstract_memory_context &&ctx, Args &&...args) {
45 return construct_aggregate_state<Agg>(ctx, std::forward<Args>(args)...);
46}
47
48template <class Agg, typename... InTs> datum aggregate_sfunc(value state, InTs... args) {
49
50 MemoryContext aggctx;
51 if (!ffi_guard{::AggCheckCallContext}(current_postgres_function::call_info().operator*(),
52 &aggctx)) {
53 report(ERROR, "not aggregate context");
54 }
55
56 if constexpr (!convertible_into_datum<Agg> && finalizable_aggregate<Agg, InTs...>) {
57 Agg *state0;
58 if (state.get_nullable_datum().is_null()) {
59 state0 = construct_aggregate_state<Agg>(memory_context(aggctx));
60 } else {
61 state0 = reinterpret_cast<Agg *>(
62 from_nullable_datum<void *>(state.get_nullable_datum(), state.get_type().oid));
63 }
64
65 state0->update(args...);
66
67 return datum_conversion<void *>::into_datum(reinterpret_cast<void *>(state0));
68 } else if constexpr (convertible_into_datum<Agg>) {
69 Agg state0 = datum_conversion<Agg>::from_nullable_datum(state.get_nullable_datum(), ANYOID);
70 state0.update(args...);
72 }
73 report(ERROR, "not supported");
74 __builtin_unreachable();
75}
76
77template <class Agg, typename... InTs> nullable_datum aggregate_ffunc(value state) {
78 if constexpr (finalizable_aggregate<Agg, InTs...>) {
79 Agg *state0;
80 if (state.get_nullable_datum().is_null()) {
81 state0 = construct_aggregate_state<Agg>(memory_context());
82 } else {
83 state0 = reinterpret_cast<Agg *>(
84 from_nullable_datum<void *>(state.get_nullable_datum(), state.get_type().oid));
85 }
86 return into_nullable_datum(state0->finalize());
87 } else {
88 report(ERROR, "this aggregate does not support final function");
89 __builtin_unreachable();
90 }
91}
92
93template <class Agg, typename... InTs> bytea aggregate_serial(value state) {
94 if constexpr (serializable_aggregate<Agg, InTs...>) {
95 if (state.get_type().oid == INTERNALOID) {
96 Agg *state0;
97 state0 = reinterpret_cast<Agg *>(
98 from_nullable_datum<void *>(state.get_nullable_datum(), state.get_type().oid));
99 bytea ba = state0->serialize();
100 return ba;
101 }
102 }
103 report(ERROR, "this aggregate does not support serialize");
104 __builtin_unreachable();
105}
106
107template <class Agg, typename... InTs> datum aggregate_deserial(bytea ba, value) {
108 if constexpr (serializable_aggregate<Agg, InTs...>) {
109 MemoryContext aggctx;
110 if (!ffi_guard{::AggCheckCallContext}(current_postgres_function::call_info().operator*(),
111 &aggctx)) {
112 report(ERROR, "not aggregate context");
113 }
114 Agg *state0 = construct_aggregate_state<Agg>(memory_context(aggctx), ba);
115 return datum_conversion<void *>::into_datum(reinterpret_cast<void *>(state0));
116 }
117 report(ERROR, "this aggregate does not support serialize");
118 __builtin_unreachable();
119}
120
121template <class Agg, typename... InTs> datum aggregate_combine(value state, value other) {
122 MemoryContext aggctx;
123 if (!ffi_guard{::AggCheckCallContext}(current_postgres_function::call_info().operator*(),
124 &aggctx)) {
125 report(ERROR, "not aggregate context");
126 }
127 if constexpr (combinable_aggregate<Agg, InTs...>) {
128 if constexpr (!convertible_into_datum<Agg> && finalizable_aggregate<Agg, InTs...>) {
129 Agg *state0;
130 if (state.get_nullable_datum().is_null()) {
131 state0 = construct_aggregate_state<Agg>(memory_context(aggctx));
132 } else {
133 state0 = reinterpret_cast<Agg *>(
134 from_nullable_datum<void *>(state.get_nullable_datum(), state.get_type().oid));
135 }
136
137 Agg *state1;
138 if (other.get_nullable_datum().is_null()) {
139 state1 = construct_aggregate_state<Agg>(memory_context(aggctx));
140 } else {
141 state1 = reinterpret_cast<Agg *>(
142 from_nullable_datum<void *>(other.get_nullable_datum(), other.get_type().oid));
143 }
144
145 Agg *newstate = construct_aggregate_state<Agg>(memory_context(aggctx), *state0, *state1);
146
147 return datum_conversion<void *>::into_datum(reinterpret_cast<void *>(newstate));
148 } else if constexpr (convertible_into_datum<Agg>) {
149 Agg state0 = datum_conversion<Agg>::from_nullable_datum(state.get_nullable_datum(), ANYOID);
150 Agg state1 = datum_conversion<Agg>::from_nullable_datum(other.get_nullable_datum(), ANYOID);
151 return datum_conversion<Agg>::into_datum(Agg(state0, state1));
152 }
153 }
154 report(ERROR, "not supported");
155 __builtin_unreachable();
156}
157
158} // namespace cppgres
159
160#define declare_aggregate(name, typname, ...) \
161 static_assert(::cppgres::aggregate<typname, ##__VA_ARGS__>); \
162 static_assert(::cppgres::convertible_into_datum<typname> || \
163 ::cppgres::finalizable_aggregate<typname, ##__VA_ARGS__>, \
164 "must be convertible to datum or have finalize()"); \
165 postgres_function(name##_sfunc, (cppgres::aggregate_sfunc<typname, ##__VA_ARGS__>)); \
166 postgres_function(name##_ffunc, (cppgres::aggregate_ffunc<typname, ##__VA_ARGS__>)); \
167 postgres_function(name##_serial, (cppgres::aggregate_serial<typname, ##__VA_ARGS__>)); \
168 postgres_function(name##_deserial, (cppgres::aggregate_deserial<typname, ##__VA_ARGS__>)); \
169 postgres_function(name##_combine, (cppgres::aggregate_combine<typname, ##__VA_ARGS__>));
Definition: aggregate.hpp:13
Definition: aggregate.hpp:29
Definition: datum.hpp:171
Definition: aggregate.hpp:18
Definition: aggregate.hpp:23
Definition: memory.hpp:14
static T from_nullable_datum(const nullable_datum &d, const oid oid, std::optional< memory_context > context=std::nullopt)=delete
Convert from a nullable datum.
static datum into_datum(const T &d)=delete
Convert datum into a type.