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
36template <class T, class... Args>
37concept combinable_aggregate = aggregate<T, Args...> && requires(const T &t, const T &t1) {
38 { T(t, t1) } -> std::same_as<T>;
39};
40
41template <class Agg, typename... InTs> datum aggregate_sfunc(value state, InTs... args) {
42
43 MemoryContext aggctx;
44 if (!ffi_guard{::AggCheckCallContext}(current_postgres_function::call_info().operator*(),
45 &aggctx)) {
46 report(ERROR, "not aggregate context");
47 }
48
49 if constexpr (!convertible_into_datum<Agg> && finalizable_aggregate<Agg, InTs...>) {
50 Agg *state0;
51 if (state.get_nullable_datum().is_null()) {
52 state0 = memory_context(aggctx).construct<Agg>();
53 } else {
54 state0 = reinterpret_cast<Agg *>(
55 from_nullable_datum<void *>(state.get_nullable_datum(), state.get_type().oid));
56 }
57
58 state0->update(args...);
59
60 return datum_conversion<void *>::into_datum(reinterpret_cast<void *>(state0));
61 } else if constexpr (convertible_into_datum<Agg>) {
62 Agg state0 = datum_conversion<Agg>::from_nullable_datum(state.get_nullable_datum(), ANYOID);
63 state0.update(args...);
65 }
66 report(ERROR, "not supported");
67 __builtin_unreachable();
68}
69
70template <class Agg, typename... InTs> nullable_datum aggregate_ffunc(value state) {
71 if constexpr (finalizable_aggregate<Agg, InTs...>) {
72 Agg *state0;
73 if (state.get_nullable_datum().is_null()) {
74 state0 = memory_context().construct<Agg>();
75 } else {
76 state0 = reinterpret_cast<Agg *>(
77 from_nullable_datum<void *>(state.get_nullable_datum(), state.get_type().oid));
78 }
79 return into_nullable_datum(state0->finalize());
80 } else {
81 report(ERROR, "this aggregate does not support final function");
82 __builtin_unreachable();
83 }
84}
85
86template <class Agg, typename... InTs> bytea aggregate_serial(value state) {
87 if constexpr (serializable_aggregate<Agg, InTs...>) {
88 if (state.get_type().oid == INTERNALOID) {
89 Agg *state0;
90 state0 = reinterpret_cast<Agg *>(
91 from_nullable_datum<void *>(state.get_nullable_datum(), state.get_type().oid));
92 bytea ba = state0->serialize();
93 return ba;
94 }
95 }
96 report(ERROR, "this aggregate does not support serialize");
97 __builtin_unreachable();
98}
99
100template <class Agg, typename... InTs> datum aggregate_deserial(bytea ba, value) {
101 if constexpr (serializable_aggregate<Agg, InTs...>) {
102 MemoryContext aggctx;
103 if (!ffi_guard{::AggCheckCallContext}(current_postgres_function::call_info().operator*(),
104 &aggctx)) {
105 report(ERROR, "not aggregate context");
106 }
107 Agg *state0 = memory_context(aggctx).construct<Agg>(ba);
108 return datum_conversion<void *>::into_datum(reinterpret_cast<void *>(state0));
109 }
110 report(ERROR, "this aggregate does not support serialize");
111 __builtin_unreachable();
112}
113
114template <class Agg, typename... InTs> datum aggregate_combine(value state, value other) {
115 static_assert(!(std::is_constructible_v<Agg, Agg &, Agg &> &&
116 !std::is_constructible_v<Agg, const Agg &, const Agg &>),
117 "combining constructor must take const references: both operands remain owned by "
118 "the aggregate memory context and are destroyed when it is reset");
119 MemoryContext aggctx;
120 if (!ffi_guard{::AggCheckCallContext}(current_postgres_function::call_info().operator*(),
121 &aggctx)) {
122 report(ERROR, "not aggregate context");
123 }
124 if constexpr (combinable_aggregate<Agg, InTs...>) {
125 if constexpr (!convertible_into_datum<Agg> && finalizable_aggregate<Agg, InTs...>) {
126 Agg *state0;
127 if (state.get_nullable_datum().is_null()) {
128 state0 = memory_context(aggctx).construct<Agg>();
129 } else {
130 state0 = reinterpret_cast<Agg *>(
131 from_nullable_datum<void *>(state.get_nullable_datum(), state.get_type().oid));
132 }
133
134 Agg *state1;
135 if (other.get_nullable_datum().is_null()) {
136 state1 = memory_context(aggctx).construct<Agg>();
137 } else {
138 state1 = reinterpret_cast<Agg *>(
139 from_nullable_datum<void *>(other.get_nullable_datum(), other.get_type().oid));
140 }
141
142 Agg *newstate =
143 memory_context(aggctx).construct<Agg>(std::as_const(*state0), std::as_const(*state1));
144
145 return datum_conversion<void *>::into_datum(reinterpret_cast<void *>(newstate));
146 } else if constexpr (convertible_into_datum<Agg>) {
147 Agg state0 = datum_conversion<Agg>::from_nullable_datum(state.get_nullable_datum(), ANYOID);
148 Agg state1 = datum_conversion<Agg>::from_nullable_datum(other.get_nullable_datum(), ANYOID);
149 return datum_conversion<Agg>::into_datum(Agg(state0, state1));
150 }
151 }
152 report(ERROR, "not supported");
153 __builtin_unreachable();
154}
155
156} // namespace cppgres
157
158#define declare_aggregate(name, typname, ...) \
159 static_assert(::cppgres::aggregate<typname, ##__VA_ARGS__>); \
160 static_assert(::cppgres::convertible_into_datum<typname> || \
161 ::cppgres::finalizable_aggregate<typname, ##__VA_ARGS__>, \
162 "must be convertible to datum or have finalize()"); \
163 postgres_function(name##_sfunc, (cppgres::aggregate_sfunc<typname, ##__VA_ARGS__>)); \
164 postgres_function(name##_ffunc, (cppgres::aggregate_ffunc<typname, ##__VA_ARGS__>)); \
165 postgres_function(name##_serial, (cppgres::aggregate_serial<typname, ##__VA_ARGS__>)); \
166 postgres_function(name##_deserial, (cppgres::aggregate_deserial<typname, ##__VA_ARGS__>)); \
167 postgres_function(name##_combine, (cppgres::aggregate_combine<typname, ##__VA_ARGS__>));
Definition: aggregate.hpp:13
Aggregate state that can be combined with another instance (parallel aggregation)
Definition: aggregate.hpp:37
Definition: datum.hpp:171
Definition: aggregate.hpp:18
Definition: aggregate.hpp:23
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.
Definition: datum.hpp:39
Definition: guard.hpp:20
Definition: value.hpp:8