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}
31
32#include "provsql_error.h"
33
35{
36 char *fname = get_func_name(oid);
37
38 if(fname == nullptr)
39 provsql_error("Invalid OID for aggregation function: %d", oid);
40
41 std::string func_name {fname};
42 pfree(fname);
43
45
46 if(func_name == "count") {
48 } else if(func_name == "sum") {
50 } else if(func_name == "min") {
52 } else if(func_name == "max") {
54 } else if(func_name == "choose") {
56 } else if(func_name == "avg") {
58 } else if(func_name == "array_agg") {
60 } else if(func_name == "bool_and" || func_name == "every") {
62 } else if(func_name == "bool_or") {
64 } else {
65 provsql_error("Aggregation operator %s not supported", func_name.c_str());
66 }
67
68 return op;
69}
70
71ComparisonOperator cmpOpFromOid(Oid op_oid, bool &ok)
72{
73 ok = false;
74 char *opname = get_opname(op_oid);
75 if(opname == nullptr)
77
78 std::string s {opname};
79 pfree(opname);
80
81 ok = true;
82 if(s == "=") return ComparisonOperator::EQ;
83 if(s == "<>") return ComparisonOperator::NE;
84 if(s == "<") return ComparisonOperator::LT;
85 if(s == "<=") return ComparisonOperator::LE;
86 if(s == ">") return ComparisonOperator::GT;
87 if(s == ">=") return ComparisonOperator::GE;
88
89 ok = false;
91}
92
93template <class ...>
94struct False : std::bool_constant<false> { };
95
96/**
97 * @brief Base aggregator template for scalar types (int, float, bool, string).
98 *
99 * @tparam T The C++ type of the accumulated value.
100 */
101template <class T>
103protected:
104 T value{}; ///< Current accumulated value
105 bool has = false; ///< @c true once the first non-NULL input has been seen
106
107public:
108 /** @brief Return the accumulated value, or NULL if no inputs were seen. */
109 AggValue finalize() const override {
110 if (has) return AggValue {value}; else return AggValue{};
111 }
112 /** @brief Return the value type corresponding to @c T. */
113 ValueType inputType() const override {
114 if constexpr (std::is_same_v<T,long>)
115 return ValueType::INT;
116 else if constexpr (std::is_same_v<T,double>)
117 return ValueType::FLOAT;
118 else if constexpr (std::is_same_v<T,bool>)
119 return ValueType::BOOLEAN;
120 else if constexpr (std::is_same_v<T,std::string>)
121 return ValueType::STRING;
122 else
123 static_assert(False<T>{});
124 }
125};
126
127/** @brief Aggregator implementing SUM for integer or float types. */
128template <class T>
130 using StandardAgg<T>::value;
131 using StandardAgg<T>::has;
132
133 void add(const AggValue& x) override {
134 if (x.getType() == ValueType::NONE) return;
135 const T& v = std::get<T>(x.v);
136 value += v;
137 has = true;
138 }
139};
140
141/** @brief Aggregator implementing MIN for integer or float types. */
142template <class T>
143struct MinAgg : StandardAgg<T> {
144 using StandardAgg<T>::value;
145 using StandardAgg<T>::has;
146
147 void add(const AggValue& x) override {
148 if (x.getType() == ValueType::NONE) return;
149 const T& v = std::get<T>(x.v);
150 if(has) {
151 if(v < value) value = v;
152 } else {
153 value = v;
154 has = true;
155 }
156 }
157};
158
159/** @brief Aggregator implementing MAX for integer or float types. */
160template <class T>
161struct MaxAgg : StandardAgg<T> {
162 using StandardAgg<T>::value;
163 using StandardAgg<T>::has;
164
165 void add(const AggValue& x) override {
166 if (x.getType() == ValueType::NONE) return;
167 const T& v = std::get<T>(x.v);
168 if(has) {
169 if(v > value) value = v;
170 } else {
171 value = v;
172 has = true;
173 }
174 }
175};
176
177/** @brief Aggregator implementing CHOOSE (returns the first non-NULL input). */
178template <class T>
180 using StandardAgg<T>::value;
181 using StandardAgg<T>::has;
182
183 void add(const AggValue& x) override {
184 if (x.getType() == ValueType::NONE) return;
185 if(!has)
186 value = std::get<T>(x.v);
187 has = true;
188 }
189};
190
191/** @brief Aggregator implementing AVG; always returns a float result. */
192template <class T>
194protected:
195 double sum = 0; ///< Running sum of all non-NULL input values
196 unsigned count = 0; ///< Number of non-NULL inputs seen so far
197 bool has = false; ///< @c true once the first non-NULL input has been seen
198
199public:
200 void add(const AggValue& x) override {
201 if (x.getType() == ValueType::NONE) return;
202 const T& v = std::get<T>(x.v);
203 sum += v;
204 ++count;
205 has = true;
206 }
207 AggValue finalize() const override {
208 if (has) return AggValue {sum/count}; else return AggValue{};
209 }
210 ValueType inputType() const override {
211 if constexpr (std::is_same_v<T,long>)
212 return ValueType::INT;
213 else if constexpr (std::is_same_v<T,double>)
214 return ValueType::FLOAT;
215 else
216 static_assert(False<T>{});
217 }
218 ValueType resultType() const override {
219 return ValueType::FLOAT;
220 }
221};
222
223// Constructs the deterministic accumulator the Monte-Carlo sampler and the
224// exhaustive subset enumerator push per-world values into. The numeric
225// aggregates (SUM / COUNT / MIN / MAX / AVG) and CHOOSE are built; the boolean
226// (bool_or / bool_and) and array_agg aggregates never reach this factory: the
227// m-semiring HAVING rewrite in having_semantics resolves them to a Boolean
228// subcircuit before probability evaluation, so no such gate_agg survives to the
229// sampler. They are rejected explicitly rather than handled.
230std::unique_ptr<Aggregator> makeAggregator(AggregationOperator op, ValueType t) {
231 switch (op) {
233 if (t == ValueType::INT) return std::make_unique<SumAgg<long> >();
234 throw std::runtime_error("COUNT is normalized to SUM(INT)");
236 switch (t) {
237 case ValueType::INT: return std::make_unique<SumAgg<long> >();
238 case ValueType::FLOAT: return std::make_unique<SumAgg<double> >();
239 default: throw std::runtime_error("SUM not supported for this type");
240 }
242 switch (t) {
243 case ValueType::INT: return std::make_unique<MinAgg<long> >();
244 case ValueType::FLOAT: return std::make_unique<MinAgg<double> >();
245 default: throw std::runtime_error("MIN not supported for this type");
246 }
248 switch (t) {
249 case ValueType::INT: return std::make_unique<MaxAgg<long> >();
250 case ValueType::FLOAT: return std::make_unique<MaxAgg<double> >();
251 default: throw std::runtime_error("MAX not supported for this type");
252 }
254 switch (t) {
255 case ValueType::INT: return std::make_unique<AvgAgg<long> >();
256 case ValueType::FLOAT: return std::make_unique<AvgAgg<double> >();
257 default: throw std::runtime_error("AVG not supported for this type");
258 }
260 switch(t) {
261 case ValueType::BOOLEAN: return std::make_unique<ChooseAgg<bool> >();
262 case ValueType::INT: return std::make_unique<ChooseAgg<long> >();
263 case ValueType::FLOAT: return std::make_unique<ChooseAgg<double> >();
264 case ValueType::STRING: return std::make_unique<ChooseAgg<std::string> >();
265 default: throw std::runtime_error("CHOOSE not supported for this type");
266 }
271 // Resolved to a Boolean subcircuit by the HAVING rewrite; never sampled.
272 throw std::runtime_error(
273 "makeAggregator: boolean/array_agg aggregates are handled by the "
274 "m-semiring HAVING rewrite, not the deterministic sampler");
275 }
276
277 throw std::logic_error("Unhandled AggregationOperator");
278}
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:67
@ INT
Signed 64-bit integer.
Definition Aggregation.h:68
@ STRING
Text string.
Definition Aggregation.h:71
@ NONE
No value (NULL).
Definition Aggregation.h:76
@ BOOLEAN
Boolean.
Definition Aggregation.h:70
@ FLOAT
Double-precision float.
Definition Aggregation.h:69
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:86
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:93
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.