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