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 built aggregation function × value-type combination has its own
13 * @c Aggregator subclass defined locally in this file (e.g. @c SumAgg<long>,
14 * @c MinAgg<double>, @c ChooseAgg<long>). Only the aggregates the Monte-Carlo
15 * sampler and the subset enumerator evaluate directly are built: the numeric
16 * ones (SUM / COUNT / MIN / MAX / AVG) and CHOOSE (the categorical analog,
17 * decided by the exhaustive subset enumerator). The boolean (bool_or /
18 * bool_and) and array_agg aggregates are resolved to a Boolean subcircuit by
19 * the m-semiring HAVING rewrite (@c having_semantics) and so never reach
20 * @c makeAggregator.
21 */
22#include "Aggregation.h"
23
24#include <string>
25#include <stdexcept>
26
27extern "C" {
28#include "utils/lsyscache.h"
29#include "utils/elog.h"
30#include "provsql_utils.h"
31}
32
33#include "provsql_error.h"
34
36{
37 char *fname = get_func_name(oid);
38
39 if(fname == nullptr)
40 provsql_error("Invalid OID for aggregation function: %d", oid);
41
42 std::string func_name {fname};
43 pfree(fname);
44
46
47 if(func_name == "count") {
49 } else if(func_name == "sum") {
51 } else if(func_name == "min") {
53 } else if(func_name == "max") {
55 } else if(func_name == "choose") {
57 } else if(func_name == "avg") {
59 } else if(func_name == "array_agg") {
61 } else if(func_name == "bool_and" || func_name == "every") {
63 } else if(func_name == "bool_or") {
65 } else {
66 provsql_error("Aggregation operator %s not supported", func_name.c_str());
67 }
68
69 return op;
70}
71
72ComparisonOperator cmpOpFromOid(Oid op_oid, bool &ok)
73{
74 ok = false;
75 char *opname = get_opname(op_oid);
76 if(opname == nullptr)
78
79 std::string s {opname};
80 pfree(opname);
81
82 ok = true;
83 if(s == "=") return ComparisonOperator::EQ;
84 if(s == "<>") return ComparisonOperator::NE;
85 if(s == "<") return ComparisonOperator::LT;
86 if(s == "<=") return ComparisonOperator::LE;
87 if(s == ">") return ComparisonOperator::GT;
88 if(s == ">=") return ComparisonOperator::GE;
89
90 ok = false;
92}
93
114
115template <class ...>
116struct False : std::bool_constant<false> { };
117
118/**
119 * @brief Base aggregator template for scalar types (int, float, bool, string).
120 *
121 * @tparam T The C++ type of the accumulated value.
122 */
123template <class T>
125protected:
126 T value{}; ///< Current accumulated value
127 bool has = false; ///< @c true once the first non-NULL input has been seen
128
129public:
130 /** @brief Return the accumulated value, or NULL if no inputs were seen. */
131 AggValue finalize() const override {
132 if (has) return AggValue {value}; else return AggValue{};
133 }
134 /** @brief Return the value type corresponding to @c T. */
135 ValueType inputType() const override {
136 if constexpr (std::is_same_v<T,long>)
137 return ValueType::INT;
138 else if constexpr (std::is_same_v<T,double>)
139 return ValueType::FLOAT;
140 else if constexpr (std::is_same_v<T,bool>)
141 return ValueType::BOOLEAN;
142 else if constexpr (std::is_same_v<T,std::string>)
143 return ValueType::STRING;
144 else
145 static_assert(False<T>{});
146 }
147};
148
149/** @brief Aggregator implementing SUM for integer or float types. */
150template <class T>
152 using StandardAgg<T>::value;
153 using StandardAgg<T>::has;
154
155 void add(const AggValue& x) override {
156 if (x.getType() == ValueType::NONE) return;
157 const T& v = std::get<T>(x.v);
158 value += v;
159 has = true;
160 }
161};
162
163/** @brief Aggregator implementing MIN for integer or float types. */
164template <class T>
165struct MinAgg : StandardAgg<T> {
166 using StandardAgg<T>::value;
167 using StandardAgg<T>::has;
168
169 void add(const AggValue& x) override {
170 if (x.getType() == ValueType::NONE) return;
171 const T& v = std::get<T>(x.v);
172 if(has) {
173 if(v < value) value = v;
174 } else {
175 value = v;
176 has = true;
177 }
178 }
179};
180
181/** @brief Aggregator implementing MAX for integer or float types. */
182template <class T>
183struct MaxAgg : StandardAgg<T> {
184 using StandardAgg<T>::value;
185 using StandardAgg<T>::has;
186
187 void add(const AggValue& x) override {
188 if (x.getType() == ValueType::NONE) return;
189 const T& v = std::get<T>(x.v);
190 if(has) {
191 if(v > value) value = v;
192 } else {
193 value = v;
194 has = true;
195 }
196 }
197};
198
199/** @brief Aggregator implementing CHOOSE (returns the first non-NULL input). */
200template <class T>
202 using StandardAgg<T>::value;
203 using StandardAgg<T>::has;
204
205 void add(const AggValue& x) override {
206 if (x.getType() == ValueType::NONE) return;
207 if(!has)
208 value = std::get<T>(x.v);
209 has = true;
210 }
211};
212
213/** @brief Aggregator implementing AVG; always returns a float result. */
214template <class T>
216protected:
217 double sum = 0; ///< Running sum of all non-NULL input values
218 unsigned count = 0; ///< Number of non-NULL inputs seen so far
219 bool has = false; ///< @c true once the first non-NULL input has been seen
220
221public:
222 void add(const AggValue& x) override {
223 if (x.getType() == ValueType::NONE) return;
224 const T& v = std::get<T>(x.v);
225 sum += v;
226 ++count;
227 has = true;
228 }
229 AggValue finalize() const override {
230 if (has) return AggValue {sum/count}; else return AggValue{};
231 }
232 ValueType inputType() const override {
233 if constexpr (std::is_same_v<T,long>)
234 return ValueType::INT;
235 else if constexpr (std::is_same_v<T,double>)
236 return ValueType::FLOAT;
237 else
238 static_assert(False<T>{});
239 }
240 ValueType resultType() const override {
241 return ValueType::FLOAT;
242 }
243};
244
245// Constructs the deterministic accumulator the Monte-Carlo sampler and the
246// exhaustive subset enumerator push per-world values into. The numeric
247// aggregates (SUM / COUNT / MIN / MAX / AVG) and CHOOSE are built; the boolean
248// (bool_or / bool_and) and array_agg aggregates never reach this factory: the
249// m-semiring HAVING rewrite in having_semantics resolves them to a Boolean
250// subcircuit before probability evaluation, so no such gate_agg survives to the
251// sampler. They are rejected explicitly rather than handled.
252std::unique_ptr<Aggregator> makeAggregator(AggregationOperator op, ValueType t) {
253 switch (op) {
255 // Each row contributes 1 (count(*)) or 0/1 (count(expr)), so the count is
256 // the sum of the contributions; the operator stays COUNT so the empty set
257 // reads as 0 rather than a sum's NULL.
258 if (t == ValueType::INT) return std::make_unique<SumAgg<long> >();
259 throw std::runtime_error("COUNT expects an integer-valued contribution");
261 switch (t) {
262 case ValueType::INT: return std::make_unique<SumAgg<long> >();
263 case ValueType::FLOAT: return std::make_unique<SumAgg<double> >();
264 default: throw std::runtime_error("SUM not supported for this type");
265 }
267 switch (t) {
268 case ValueType::INT: return std::make_unique<MinAgg<long> >();
269 case ValueType::FLOAT: return std::make_unique<MinAgg<double> >();
270 default: throw std::runtime_error("MIN not supported for this type");
271 }
273 switch (t) {
274 case ValueType::INT: return std::make_unique<MaxAgg<long> >();
275 case ValueType::FLOAT: return std::make_unique<MaxAgg<double> >();
276 default: throw std::runtime_error("MAX not supported for this type");
277 }
279 switch (t) {
280 case ValueType::INT: return std::make_unique<AvgAgg<long> >();
281 case ValueType::FLOAT: return std::make_unique<AvgAgg<double> >();
282 default: throw std::runtime_error("AVG not supported for this type");
283 }
285 switch(t) {
286 case ValueType::BOOLEAN: return std::make_unique<ChooseAgg<bool> >();
287 case ValueType::INT: return std::make_unique<ChooseAgg<long> >();
288 case ValueType::FLOAT: return std::make_unique<ChooseAgg<double> >();
289 case ValueType::STRING: return std::make_unique<ChooseAgg<std::string> >();
290 default: throw std::runtime_error("CHOOSE not supported for this type");
291 }
296 // Resolved to a Boolean subcircuit by the HAVING rewrite; never sampled.
297 throw std::runtime_error(
298 "makeAggregator: boolean/array_agg aggregates are handled by the "
299 "m-semiring HAVING rewrite, not the deterministic sampler");
300 }
301
302 throw std::logic_error("Unhandled AggregationOperator");
303}
ArithmeticOperator arithOpFromTag(unsigned tag, bool &ok)
Map a gate_arith operator tag to an ArithmeticOperator.
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:51
@ OR
Boolean OR aggregate.
Definition Aggregation.h:58
@ MAX
MAX → input type.
Definition Aggregation.h:55
@ COUNT
COUNT(*) or COUNT(expr) → integer.
Definition Aggregation.h:52
@ AND
Boolean AND aggregate.
Definition Aggregation.h:57
@ SUM
SUM → integer or float.
Definition Aggregation.h:53
@ ARRAY_AGG
Array aggregation.
Definition Aggregation.h:60
@ NONE
No aggregation (returns NULL).
Definition Aggregation.h:61
@ MIN
MIN → input type.
Definition Aggregation.h:54
@ CHOOSE
Arbitrary selection (pick one element).
Definition Aggregation.h:59
@ AVG
AVG → float.
Definition Aggregation.h:56
ComparisonOperator
SQL comparison operators used in gate_cmp circuit gates.
Definition Aggregation.h:39
@ LT
Less than (<).
Definition Aggregation.h:43
@ GT
Greater than (>).
Definition Aggregation.h:45
@ LE
Less than or equal (<=).
Definition Aggregation.h:42
@ NE
Not equal (<>).
Definition Aggregation.h:41
@ GE
Greater than or equal (>=).
Definition Aggregation.h:44
ValueType
Runtime type tag for aggregate values.
Definition Aggregation.h:91
@ INT
Signed 64-bit integer.
Definition Aggregation.h:92
@ STRING
Text string.
Definition Aggregation.h:95
@ NONE
No value (NULL).
@ BOOLEAN
Boolean.
Definition Aggregation.h:94
@ FLOAT
Double-precision float.
Definition Aggregation.h:93
ArithmeticOperator
Arithmetic operations carried by gate_arith circuit gates.
Definition Aggregation.h:74
@ POW
binary power
Definition Aggregation.h:82
@ MAX
n-ary maximum (order statistic)
Definition Aggregation.h:80
@ DIV
binary quotient
Definition Aggregation.h:78
@ PERCENTILE
continuous percentile over interleaved [indicator, value] wires
Definition Aggregation.h:85
@ NEG
unary negation
Definition Aggregation.h:79
@ EXP
unary exponential
Definition Aggregation.h:84
@ TIMES
n-ary product
Definition Aggregation.h:76
@ MIN
n-ary minimum (order statistic)
Definition Aggregation.h:81
@ LN
unary natural logarithm
Definition Aggregation.h:83
@ MINUS
binary difference
Definition Aggregation.h:77
Uniform error-reporting macros for ProvSQL.
#define provsql_error(fmt,...)
Report a fatal ProvSQL error and abort the current transaction.
Core types, constants, and utilities shared across ProvSQL.
provsql_arith_op
Arithmetic operator tags used by gate_arith.
@ PROVSQL_ARITH_PERCENTILE
continuous percentile (order-statistic aggregate): wires are interleaved [ind_1, x_1,...
@ PROVSQL_ARITH_DIV
binary, child0 / child1
@ PROVSQL_ARITH_LN
unary, natural logarithm of child0 (a negative draw raises at evaluation)
@ PROVSQL_ARITH_PLUS
n-ary, sum of children
@ PROVSQL_ARITH_POW
binary, child0 ^ child1 (real branch only: a negative base drawn with a non-integer exponent raises a...
@ PROVSQL_ARITH_NEG
unary, -child0
@ PROVSQL_ARITH_MINUS
binary, child0 - child1
@ PROVSQL_ARITH_EXP
unary, e^child0
@ PROVSQL_ARITH_TIMES
n-ary, product of children
@ PROVSQL_ARITH_MIN
n-ary, min of children (order statistic; least / min aggregate)
@ PROVSQL_ARITH_MAX
n-ary, max of children (order statistic; greatest / max aggregate)
A dynamically-typed aggregate value.
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.
Abstract interface for an incremental aggregate accumulator.
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().
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).
void add(const AggValue &x) override
Incorporate one input value into the running aggregate.
Aggregator implementing MAX for integer or float types.
void add(const AggValue &x) override
Incorporate one input value into the running aggregate.
Aggregator implementing MIN for integer or float types.
void add(const AggValue &x) override
Incorporate one input value into the running aggregate.
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.
void add(const AggValue &x) override
Incorporate one input value into the running aggregate.