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
66ComparisonOperator cmpOpFromOid(Oid op_oid, bool &ok)
67{
68 ok = false;
69 char *opname = get_opname(op_oid);
70 if(opname == nullptr)
72
73 std::string s {opname};
74 pfree(opname);
75
76 ok = true;
77 if(s == "=") return ComparisonOperator::EQ;
78 if(s == "<>") return ComparisonOperator::NE;
79 if(s == "<") return ComparisonOperator::LT;
80 if(s == "<=") return ComparisonOperator::LE;
81 if(s == ">") return ComparisonOperator::GT;
82 if(s == ">=") return ComparisonOperator::GE;
83
84 ok = false;
86}
87
88/** @brief Aggregator that ignores all inputs and always returns NULL. */
90 void add(const AggValue& x) override {
91 }
92 AggValue finalize() const override {
93 return AggValue{};
94 }
95 AggregationOperator op() const override {
97 }
98 ValueType inputType() const override {
99 return ValueType::NONE;
100 }
101};
102
103template <class ...>
104struct False : std::bool_constant<false> { };
105
106/**
107 * @brief Base aggregator template for scalar types (int, float, bool, string).
108 *
109 * @tparam T The C++ type of the accumulated value.
110 */
111template <class T>
113protected:
114 T value{}; ///< Current accumulated value
115 bool has = false; ///< @c true once the first non-NULL input has been seen
116
117public:
118 /** @brief Return the accumulated value, or NULL if no inputs were seen. */
119 AggValue finalize() const override {
120 if (has) return AggValue {value}; else return AggValue{};
121 }
122 /** @brief Return the value type corresponding to @c T. */
123 ValueType inputType() const override {
124 if constexpr (std::is_same_v<T,long>)
125 return ValueType::INT;
126 else if constexpr (std::is_same_v<T,double>)
127 return ValueType::FLOAT;
128 else if constexpr (std::is_same_v<T,bool>)
129 return ValueType::BOOLEAN;
130 else if constexpr (std::is_same_v<T,std::string>)
131 return ValueType::STRING;
132 else
133 static_assert(False<T>{});
134 }
135};
136
137/** @brief Aggregator implementing SUM for integer or float types. */
138template <class T>
140 using StandardAgg<T>::value;
141 using StandardAgg<T>::has;
142
143 void add(const AggValue& x) override {
144 if (x.getType() == ValueType::NONE) return;
145 const T& v = std::get<T>(x.v);
146 value += v;
147 has = true;
148 }
149 AggregationOperator op() const override {
151 }
152};
153
154/** @brief Aggregator implementing MIN for integer or float types. */
155template <class T>
156struct MinAgg : StandardAgg<T> {
157 using StandardAgg<T>::value;
158 using StandardAgg<T>::has;
159
160 void add(const AggValue& x) override {
161 if (x.getType() == ValueType::NONE) return;
162 const T& v = std::get<T>(x.v);
163 if(has) {
164 if(v < value) value = v;
165 } else {
166 value = v;
167 has = true;
168 }
169 }
170 AggregationOperator op() const override {
172 }
173};
174
175/** @brief Aggregator implementing MAX for integer or float types. */
176template <class T>
177struct MaxAgg : StandardAgg<T> {
178 using StandardAgg<T>::value;
179 using StandardAgg<T>::has;
180
181 void add(const AggValue& x) override {
182 if (x.getType() == ValueType::NONE) return;
183 const T& v = std::get<T>(x.v);
184 if(has) {
185 if(v > value) value = v;
186 } else {
187 value = v;
188 has = true;
189 }
190 }
191 AggregationOperator op() const override {
193 }
194};
195
196/** @brief Aggregator implementing boolean AND (returns false if any input is false). */
197struct AndAgg : StandardAgg<bool> {
199 value=true;
200 }
201
202 void add(const AggValue& x) override {
203 if (x.getType() == ValueType::NONE) return;
204 auto b = std::get<bool>(x.v);
205 value = value && b;
206 has = true;
207 }
208 AggregationOperator op() const override {
210 }
211};
212
213/** @brief Aggregator implementing boolean OR (returns true if any input is true). */
214struct OrAgg : StandardAgg<bool> {
216 value=false;
217 }
218
219 void add(const AggValue& x) override {
220 if (x.getType() == ValueType::NONE) return;
221 auto b = std::get<bool>(x.v);
222 value = value || b;
223 has = true;
224 }
225 AggregationOperator op() const override {
227 }
228};
229
230/** @brief Aggregator implementing CHOOSE (returns the first non-NULL input). */
231template <class T>
233 using StandardAgg<T>::value;
234 using StandardAgg<T>::has;
235
236 void add(const AggValue& x) override {
237 if (x.getType() == ValueType::NONE) return;
238 if(!has)
239 value = std::get<T>(x.v);
240 has = true;
241 }
242 AggregationOperator op() const override {
244 }
245};
246
247/** @brief Aggregator implementing AVG; always returns a float result. */
248template <class T>
250protected:
251 double sum = 0; ///< Running sum of all non-NULL input values
252 unsigned count = 0; ///< Number of non-NULL inputs seen so far
253 bool has = false; ///< @c true once the first non-NULL input has been seen
254
255public:
256 void add(const AggValue& x) override {
257 if (x.getType() == ValueType::NONE) return;
258 const T& v = std::get<T>(x.v);
259 sum += v;
260 ++count;
261 has = true;
262 }
263 AggregationOperator op() const override {
265 }
266 AggValue finalize() const override {
267 if (has) return AggValue {sum/count}; else return AggValue{};
268 }
269 ValueType inputType() const override {
270 if constexpr (std::is_same_v<T,long>)
271 return ValueType::INT;
272 else if constexpr (std::is_same_v<T,double>)
273 return ValueType::FLOAT;
274 else
275 static_assert(False<T>{});
276 }
277 ValueType resultType() const override {
278 return ValueType::FLOAT;
279 }
280};
281
282/** @brief Aggregator implementing ARRAY_AGG; collects all non-NULL inputs into an array. */
283template<class T>
285protected:
286 std::vector<T> values; ///< Accumulated elements
287 using StandardAgg<T>::has;
288
289public:
290 void add(const AggValue& x) override {
291 if (x.getType() == ValueType::NONE) return;
292 const T& v = std::get<T>(x.v);
293 values.push_back(v);
294 has = true;
295 }
296 AggValue finalize() const override {
297 if (has) return AggValue {values}; else return AggValue{};
298 }
299 AggregationOperator op() const override {
301 }
302 ValueType resultType() const override {
303 if constexpr (std::is_same_v<T,long>)
305 else if constexpr (std::is_same_v<T,double>)
307 else if constexpr (std::is_same_v<T,bool>)
309 else if constexpr (std::is_same_v<T,std::string>)
311 else
312 static_assert(False<T>{});
313 }
314};
315
316std::unique_ptr<Aggregator> makeAggregator(AggregationOperator op, ValueType t) {
317 if(t==ValueType::NONE) return std::make_unique<NoneAgg>();
318
319 switch (op) {
321 if (t == ValueType::INT) return std::make_unique<SumAgg<long> >();
322 throw std::runtime_error("COUNT is normalized to SUM(INT)");
324 switch (t) {
325 case ValueType::INT: return std::make_unique<SumAgg<long> >();
326 case ValueType::FLOAT: return std::make_unique<SumAgg<double> >();
327 default: throw std::runtime_error("SUM not supported for this type");
328 }
330 switch (t) {
331 case ValueType::INT: return std::make_unique<MinAgg<long> >();
332 case ValueType::FLOAT: return std::make_unique<MinAgg<double> >();
333 default: throw std::runtime_error("MIN not supported for this type");
334 }
336 switch (t) {
337 case ValueType::INT: return std::make_unique<MaxAgg<long> >();
338 case ValueType::FLOAT: return std::make_unique<MaxAgg<double> >();
339 default: throw std::runtime_error("MAX not supported for this type");
340 }
342 switch (t) {
343 case ValueType::INT: return std::make_unique<AvgAgg<long> >();
344 case ValueType::FLOAT: return std::make_unique<AvgAgg<double> >();
345 default: throw std::runtime_error("AVG not supported for this type");
346 }
348 if (t == ValueType::BOOLEAN) return std::make_unique<AndAgg>();
349 throw std::runtime_error("AND requires BOOLEAN");
351 if (t == ValueType::BOOLEAN) return std::make_unique<OrAgg>();
352 throw std::runtime_error("OR requires BOOLEAN");
354 switch(t) {
355 case ValueType::BOOLEAN: return std::make_unique<ChooseAgg<bool> >();
356 case ValueType::INT: return std::make_unique<ChooseAgg<long> >();
357 case ValueType::FLOAT: return std::make_unique<ChooseAgg<double> >();
358 case ValueType::STRING: return std::make_unique<ChooseAgg<std::string> >();
359 default: throw std::runtime_error("CHOOSE not supported for this type");
360 }
362 switch(t) {
363 case ValueType::BOOLEAN: return std::make_unique<ArrayAgg<bool> >();
364 case ValueType::INT: return std::make_unique<ArrayAgg<long> >();
365 case ValueType::FLOAT: return std::make_unique<ArrayAgg<double> >();
366 case ValueType::STRING: return std::make_unique<ArrayAgg<std::string> >();
367 default: throw std::runtime_error("ARRAY_AGG not supported for this type");
368 }
370 return std::make_unique<NoneAgg>();
371 }
372
373 throw std::logic_error("Unhandled AggregationOperator");
374}
ComparisonOperator cmpOpFromOid(Oid op_oid, bool &ok)
Map a PostgreSQL comparison-operator OID to a ComparisonOperator.
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.
Definition Aggregation.h:57
@ MAX
MAX → input type.
Definition Aggregation.h:54
@ COUNT
COUNT(*) or COUNT(expr) → integer.
Definition Aggregation.h:51
@ AND
Boolean AND aggregate.
Definition Aggregation.h:56
@ SUM
SUM → integer or float.
Definition Aggregation.h:52
@ ARRAY_AGG
Array aggregation.
Definition Aggregation.h:59
@ NONE
No aggregation (returns NULL).
Definition Aggregation.h:60
@ MIN
MIN → input type.
Definition Aggregation.h:53
@ CHOOSE
Arbitrary selection (pick one element).
Definition Aggregation.h:58
@ AVG
AVG → float.
Definition Aggregation.h:55
ComparisonOperator
SQL comparison operators used in gate_cmp circuit gates.
Definition Aggregation.h:38
@ LT
Less than (<).
Definition Aggregation.h:42
@ GT
Greater than (>).
Definition Aggregation.h:44
@ LE
Less than or equal (<=).
Definition Aggregation.h:41
@ NE
Not equal (<>).
Definition Aggregation.h:40
@ GE
Greater than or equal (>=).
Definition Aggregation.h:43
ValueType
Runtime type tag for aggregate values.
Definition Aggregation.h:66
@ ARRAY_INT
Array of integers.
Definition Aggregation.h:71
@ ARRAY_BOOLEAN
Array of booleans.
Definition Aggregation.h:73
@ INT
Signed 64-bit integer.
Definition Aggregation.h:67
@ STRING
Text string.
Definition Aggregation.h:70
@ ARRAY_FLOAT
Array of floats.
Definition Aggregation.h:72
@ ARRAY_STRING
Array of strings.
Definition Aggregation.h:74
@ NONE
No value (NULL).
Definition Aggregation.h:75
@ BOOLEAN
Boolean.
Definition Aggregation.h:69
@ FLOAT
Double-precision float.
Definition Aggregation.h:68
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.
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.
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.