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