ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
Aggregation.cpp
Go to the documentation of this file.
1/**
2 * @file Aggregation.cpp
3 * @brief Aggregation operator and accumulator implementations.
4 *
5 * Implements the two factory functions declared in @c Aggregation.h:
6 * - @c getAggregationOperator(): maps a PostgreSQL aggregate function OID
7 * (looked up by name via @c get_func_name()) to an @c AggregationOperator
8 * enum value.
9 * - @c makeAggregator(): constructs a concrete @c Aggregator subclass
10 * for the given operator/type combination.
11 *
12 * Each aggregation function × value-type combination has its own
13 * @c Aggregator subclass defined locally in this file (e.g.
14 * @c SumAggregator<long>, @c MinAggregator<double>, @c ArrayAggregator<std::string>,
15 * etc.).
16 */
17#include "Aggregation.h"
18
19#include <string>
20#include <stdexcept>
21
22extern "C" {
23#include "utils/lsyscache.h"
24#include "utils/elog.h"
25}
26
27#include "provsql_error.h"
28
30{
31 char *fname = get_func_name(oid);
32
33 if(fname == nullptr)
34 provsql_error("Invalid OID for aggregation function: %d", oid);
35
36 std::string func_name {fname};
37 pfree(fname);
38
40
41 if(func_name == "count") {
43 } else if(func_name == "sum") {
45 } else if(func_name == "min") {
47 } else if(func_name == "max") {
49 } else if(func_name == "choose") {
51 } else if(func_name == "avg") {
53 } else if(func_name == "array_agg") {
55 } else if(func_name == "and" || func_name=="every") {
57 } else if(func_name == "or") {
59 } else {
60 provsql_error("Aggregation operator %s not supported", func_name.c_str());
61 }
62
63 return op;
64}
65
66/** @brief Aggregator that ignores all inputs and always returns NULL. */
68 void add(const AggValue& x) override {
69 }
70 AggValue finalize() const override {
71 return AggValue{};
72 }
73 AggregationOperator op() const override {
75 }
76 ValueType inputType() const override {
77 return ValueType::NONE;
78 }
79};
80
81template <class ...>
82struct False : std::bool_constant<false> { };
83
84/**
85 * @brief Base aggregator template for scalar types (int, float, bool, string).
86 *
87 * @tparam T The C++ type of the accumulated value.
88 */
89template <class T>
91protected:
92 T value{}; ///< Current accumulated value
93 bool has = false; ///< @c true once the first non-NULL input has been seen
94
95public:
96 /** @brief Return the accumulated value, or NULL if no inputs were seen. */
97 AggValue finalize() const override {
98 if (has) return AggValue {value}; else return AggValue{};
99 }
100 /** @brief Return the value type corresponding to @c T. */
101 ValueType inputType() const override {
102 if constexpr (std::is_same_v<T,long>)
103 return ValueType::INT;
104 else if constexpr (std::is_same_v<T,double>)
105 return ValueType::FLOAT;
106 else if constexpr (std::is_same_v<T,bool>)
107 return ValueType::BOOLEAN;
108 else if constexpr (std::is_same_v<T,std::string>)
109 return ValueType::STRING;
110 else
111 static_assert(False<T>{});
112 }
113};
114
115/** @brief Aggregator implementing SUM for integer or float types. */
116template <class T>
118 using StandardAgg<T>::value;
119 using StandardAgg<T>::has;
120
121 void add(const AggValue& x) override {
122 if (x.getType() == ValueType::NONE) return;
123 const T& v = std::get<T>(x.v);
124 value += v;
125 has = true;
126 }
127 AggregationOperator op() const override {
129 }
130};
131
132/** @brief Aggregator implementing MIN for integer or float types. */
133template <class T>
134struct MinAgg : StandardAgg<T> {
135 using StandardAgg<T>::value;
136 using StandardAgg<T>::has;
137
138 void add(const AggValue& x) override {
139 if (x.getType() == ValueType::NONE) return;
140 const T& v = std::get<T>(x.v);
141 if(has) {
142 if(v < value) value = v;
143 } else {
144 value = v;
145 has = true;
146 }
147 }
148 AggregationOperator op() const override {
150 }
151};
152
153/** @brief Aggregator implementing MAX for integer or float types. */
154template <class T>
155struct MaxAgg : StandardAgg<T> {
156 using StandardAgg<T>::value;
157 using StandardAgg<T>::has;
158
159 void add(const AggValue& x) override {
160 if (x.getType() == ValueType::NONE) return;
161 const T& v = std::get<T>(x.v);
162 if(has) {
163 if(v > value) value = v;
164 } else {
165 value = v;
166 has = true;
167 }
168 }
169 AggregationOperator op() const override {
171 }
172};
173
174/** @brief Aggregator implementing boolean AND (returns false if any input is false). */
175struct AndAgg : StandardAgg<bool> {
177 value=true;
178 }
179
180 void add(const AggValue& x) override {
181 if (x.getType() == ValueType::NONE) return;
182 auto b = std::get<bool>(x.v);
183 value = value && b;
184 has = true;
185 }
186 AggregationOperator op() const override {
188 }
189};
190
191/** @brief Aggregator implementing boolean OR (returns true if any input is true). */
192struct OrAgg : StandardAgg<bool> {
194 value=false;
195 }
196
197 void add(const AggValue& x) override {
198 if (x.getType() == ValueType::NONE) return;
199 auto b = std::get<bool>(x.v);
200 value = value || b;
201 has = true;
202 }
203 AggregationOperator op() const override {
205 }
206};
207
208/** @brief Aggregator implementing CHOOSE (returns the first non-NULL input). */
209template <class T>
211 using StandardAgg<T>::value;
212 using StandardAgg<T>::has;
213
214 void add(const AggValue& x) override {
215 if (x.getType() == ValueType::NONE) return;
216 if(!has)
217 value = std::get<T>(x.v);
218 has = true;
219 }
220 AggregationOperator op() const override {
222 }
223};
224
225/** @brief Aggregator implementing AVG; always returns a float result. */
226template <class T>
228protected:
229 double sum = 0; ///< Running sum of all non-NULL input values
230 unsigned count = 0; ///< Number of non-NULL inputs seen so far
231 bool has = false; ///< @c true once the first non-NULL input has been seen
232
233public:
234 void add(const AggValue& x) override {
235 if (x.getType() == ValueType::NONE) return;
236 const T& v = std::get<T>(x.v);
237 sum += v;
238 ++count;
239 has = true;
240 }
241 AggregationOperator op() const override {
243 }
244 AggValue finalize() const override {
245 if (has) return AggValue {sum/count}; else return AggValue{};
246 }
247 ValueType inputType() const override {
248 if constexpr (std::is_same_v<T,long>)
249 return ValueType::INT;
250 else if constexpr (std::is_same_v<T,double>)
251 return ValueType::FLOAT;
252 else
253 static_assert(False<T>{});
254 }
255 ValueType resultType() const override {
256 return ValueType::FLOAT;
257 }
258};
259
260/** @brief Aggregator implementing ARRAY_AGG; collects all non-NULL inputs into an array. */
261template<class T>
263protected:
264 std::vector<T> values; ///< Accumulated elements
265 using StandardAgg<T>::has;
266
267public:
268 void add(const AggValue& x) override {
269 if (x.getType() == ValueType::NONE) return;
270 const T& v = std::get<T>(x.v);
271 values.push_back(v);
272 has = true;
273 }
274 AggValue finalize() const override {
275 if (has) return AggValue {values}; else return AggValue{};
276 }
277 AggregationOperator op() const override {
279 }
280 ValueType resultType() const override {
281 if constexpr (std::is_same_v<T,long>)
283 else if constexpr (std::is_same_v<T,double>)
285 else if constexpr (std::is_same_v<T,bool>)
287 else if constexpr (std::is_same_v<T,std::string>)
289 else
290 static_assert(False<T>{});
291 }
292};
293
294std::unique_ptr<Aggregator> makeAggregator(AggregationOperator op, ValueType t) {
295 if(t==ValueType::NONE) return std::make_unique<NoneAgg>();
296
297 switch (op) {
299 if (t == ValueType::INT) return std::make_unique<SumAgg<long> >();
300 throw std::runtime_error("COUNT is normalized to SUM(INT)");
302 switch (t) {
303 case ValueType::INT: return std::make_unique<SumAgg<long> >();
304 case ValueType::FLOAT: return std::make_unique<SumAgg<double> >();
305 default: throw std::runtime_error("SUM not supported for this type");
306 }
308 switch (t) {
309 case ValueType::INT: return std::make_unique<MinAgg<long> >();
310 case ValueType::FLOAT: return std::make_unique<MinAgg<double> >();
311 default: throw std::runtime_error("MIN not supported for this type");
312 }
314 switch (t) {
315 case ValueType::INT: return std::make_unique<MaxAgg<long> >();
316 case ValueType::FLOAT: return std::make_unique<MaxAgg<double> >();
317 default: throw std::runtime_error("MAX not supported for this type");
318 }
320 switch (t) {
321 case ValueType::INT: return std::make_unique<AvgAgg<long> >();
322 case ValueType::FLOAT: return std::make_unique<AvgAgg<double> >();
323 default: throw std::runtime_error("AVG not supported for this type");
324 }
326 if (t == ValueType::BOOLEAN) return std::make_unique<AndAgg>();
327 throw std::runtime_error("AND requires BOOLEAN");
329 if (t == ValueType::BOOLEAN) return std::make_unique<OrAgg>();
330 throw std::runtime_error("OR requires BOOLEAN");
332 switch(t) {
333 case ValueType::BOOLEAN: return std::make_unique<ChooseAgg<bool> >();
334 case ValueType::INT: return std::make_unique<ChooseAgg<long> >();
335 case ValueType::FLOAT: return std::make_unique<ChooseAgg<double> >();
336 case ValueType::STRING: return std::make_unique<ChooseAgg<std::string> >();
337 default: throw std::runtime_error("CHOOSE not supported for this type");
338 }
340 switch(t) {
341 case ValueType::BOOLEAN: return std::make_unique<ArrayAgg<bool> >();
342 case ValueType::INT: return std::make_unique<ArrayAgg<long> >();
343 case ValueType::FLOAT: return std::make_unique<ArrayAgg<double> >();
344 case ValueType::STRING: return std::make_unique<ArrayAgg<std::string> >();
345 default: throw std::runtime_error("ARRAY_AGG not supported for this type");
346 }
348 return std::make_unique<NoneAgg>();
349 }
350
351 throw std::logic_error("Unhandled AggregationOperator");
352}
AggregationOperator getAggregationOperator(Oid oid)
Map a PostgreSQL aggregate function OID to an AggregationOperator.
std::unique_ptr< Aggregator > makeAggregator(AggregationOperator op, ValueType t)
Create a concrete Aggregator for the given operator and value type.
Typed aggregation value, operator, and aggregator abstractions.
AggregationOperator
SQL aggregation functions tracked by ProvSQL.
Definition Aggregation.h:50
@ OR
Boolean OR aggregate.
@ MAX
MAX → input type.
@ COUNT
COUNT(*) or COUNT(expr) → integer.
@ AND
Boolean AND aggregate.
@ SUM
SUM → integer or float.
@ ARRAY_AGG
Array aggregation.
@ NONE
No aggregation (returns NULL)
@ MIN
MIN → input type.
@ CHOOSE
Arbitrary selection (pick one element)
@ AVG
AVG → float.
ValueType
Runtime type tag for aggregate values.
Definition Aggregation.h:66
@ ARRAY_INT
Array of integers.
@ ARRAY_BOOLEAN
Array of booleans.
@ INT
Signed 64-bit integer.
@ STRING
Text string.
@ ARRAY_FLOAT
Array of floats.
@ ARRAY_STRING
Array of strings.
@ NONE
No value (NULL)
@ BOOLEAN
Boolean.
@ FLOAT
Double-precision float.
Uniform error-reporting macros for ProvSQL.
#define provsql_error(fmt,...)
Report a fatal ProvSQL error and abort the current transaction.
A dynamically-typed aggregate value.
Definition Aggregation.h:85
ValueType getType() const
Return the runtime type tag of this value.
std::variant< long, double, bool, std::string, std::vector< long >, std::vector< double >, std::vector< bool >, std::vector< std::string > > v
The variant holding the actual value.
Definition Aggregation.h:92
Abstract interface for an incremental aggregate accumulator.
Aggregator implementing boolean AND (returns false if any input is false).
void add(const AggValue &x) override
Incorporate one input value into the running aggregate.
AggregationOperator op() const override
Return the aggregation operator this accumulator implements.
Aggregator implementing ARRAY_AGG; collects all non-NULL inputs into an array.
void add(const AggValue &x) override
Incorporate one input value into the running aggregate.
ValueType resultType() const override
Return the type of the value returned by finalize().
AggregationOperator op() const override
Return the aggregation operator this accumulator implements.
AggValue finalize() const override
Return the final aggregate result.
std::vector< T > values
Accumulated elements.
Aggregator implementing AVG; always returns a float result.
bool has
true once the first non-NULL input has been seen
ValueType resultType() const override
Return the type of the value returned by finalize().
ValueType inputType() const override
Return the type of the input values accepted by add().
AggregationOperator op() const override
Return the aggregation operator this accumulator implements.
unsigned count
Number of non-NULL inputs seen so far.
void add(const AggValue &x) override
Incorporate one input value into the running aggregate.
AggValue finalize() const override
Return the final aggregate result.
double sum
Running sum of all non-NULL input values.
Aggregator implementing CHOOSE (returns the first non-NULL input).
AggregationOperator op() const override
Return the aggregation operator this accumulator implements.
void add(const AggValue &x) override
Incorporate one input value into the running aggregate.
Aggregator implementing MAX for integer or float types.
AggregationOperator op() const override
Return the aggregation operator this accumulator implements.
void add(const AggValue &x) override
Incorporate one input value into the running aggregate.
Aggregator implementing MIN for integer or float types.
AggregationOperator op() const override
Return the aggregation operator this accumulator implements.
void add(const AggValue &x) override
Incorporate one input value into the running aggregate.
Aggregator that ignores all inputs and always returns NULL.
AggValue finalize() const override
Return the final aggregate result.
ValueType inputType() const override
Return the type of the input values accepted by add().
AggregationOperator op() const override
Return the aggregation operator this accumulator implements.
void add(const AggValue &x) override
Incorporate one input value into the running aggregate.
Aggregator implementing boolean OR (returns true if any input is true).
void add(const AggValue &x) override
Incorporate one input value into the running aggregate.
AggregationOperator op() const override
Return the aggregation operator this accumulator implements.
Base aggregator template for scalar types (int, float, bool, string).
AggValue finalize() const override
Return the accumulated value, or NULL if no inputs were seen.
ValueType inputType() const override
Return the value type corresponding to T.
bool has
true once the first non-NULL input has been seen
T value
Current accumulated value.
Aggregator implementing SUM for integer or float types.
AggregationOperator op() const override
Return the aggregation operator this accumulator implements.
void add(const AggValue &x) override
Incorporate one input value into the running aggregate.