ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
provenance_evaluate_compiled.cpp
Go to the documentation of this file.
1/**
2 * @file provenance_evaluate_compiled.cpp
3 * @brief SQL function @c provsql.provenance_evaluate_compiled() – C++ semiring evaluation.
4 *
5 * Implements the compiled (C++ generic) variant of provenance circuit
6 * evaluation. Unlike @c provenance_evaluate() (which calls user-supplied
7 * PostgreSQL functions for each semiring operation), this function
8 * evaluates the circuit using one of the built-in C++ semiring
9 * implementations from the @c semiring/ directory.
10 *
11 * Supported semirings (selected by the @p semiring argument):
12 * - @c "boolean" → @c semiring::Boolean
13 * - @c "counting" → @c semiring::Counting
14 * - @c "formula" → @c semiring::Formula (symbolic representation as a formula)
15 * - @c "how" → @c semiring::How (canonical polynomial provenance ℕ[X])
16 * - @c "why" → @c semiring::Why (witness sets)
17 * - @c "which" → @c semiring::Which (lineage)
18 * - @c "boolexpr" → @c semiring::BoolExpr (Boolean circuit for probability)
19 * - @c "tropical" → @c semiring::Tropical (min-plus, shortest-cost)
20 * - @c "viterbi" → @c semiring::Viterbi (max-times, most-likely derivation)
21 * - @c "lukasiewicz" → @c semiring::Lukasiewicz (Łukasiewicz fuzzy t-norm)
22 * - @c "interval_union" → @c semiring::IntervalUnion (multirange union, PG14+)
23 * for any of <tt>tstzmultirange</tt>, <tt>nummultirange</tt>,
24 * <tt>int4multirange</tt>; selected by the result type of the call
25 * - @c "minmax" / @c "maxmin" → @c semiring::MinMax over any user-defined
26 * PostgreSQL enum type, selected by @c get_typtype() == @c TYPTYPE_ENUM
27 *
28 * Each compiled semiring exposes @c parse_leaf() and (for text-valued
29 * carriers) @c to_text() member functions, so this file is purely a
30 * dispatcher: it picks the right semiring instance from the
31 * (result-type, semiring-name) pair, then calls the @c pec() template
32 * which runs the same four-step pipeline for every semiring (build leaf
33 * mapping, evaluate HAVING sub-circuits, evaluate the main circuit,
34 * encode the result as a Datum).
35 */
36extern "C" {
37#include "postgres.h"
38#include "fmgr.h"
39#include "catalog/pg_type.h"
40#include "utils/uuid.h"
41#include "utils/builtins.h"
42#include "utils/lsyscache.h"
43#include "provsql_utils.h"
44#include "provsql_mmap.h"
45
46PG_FUNCTION_INFO_V1(provenance_evaluate_compiled);
47PG_FUNCTION_INFO_V1(plain_truth);
48}
49
50#include <string>
51#include <vector>
52#include <unordered_map>
53#include <algorithm>
54
55#include "Expectation.h"
56#include "having_semantics.hpp"
58#include "semiring/Boolean.h"
59#include "semiring/Counting.h"
60#include "semiring/Formula.h"
61#include "semiring/How.h"
62#include "semiring/Why.h"
63#include "semiring/Which.h"
64#include "semiring/BoolExpr.h"
65#include "semiring/Tropical.h"
66#include "semiring/Viterbi.h"
69#include "semiring/MinMax.h"
70
71const char *drop_temp_table = "DROP TABLE IF EXISTS tmp_uuids;";
72
73namespace {
74
75/**
76 * @brief Wrap a @c std::string in a Postgres @c text Datum.
77 */
78Datum text_datum(const std::string &s) {
79 text *result = (text *) palloc(VARHDRSZ + s.size());
80 SET_VARSIZE(result, VARHDRSZ + s.size());
81 memcpy(VARDATA(result), s.c_str(), s.size());
82 return PointerGetDatum(result);
83}
84
85// to_datum overloads: encode a semiring's evaluation result as a Postgres Datum.
86Datum to_datum(const semiring::Boolean &, bool v) { return BoolGetDatum(v); }
87Datum to_datum(const semiring::Counting &, unsigned v) { return Int32GetDatum(static_cast<int32>(v)); }
88Datum to_datum(const semiring::Tropical &, double v) { return Float8GetDatum(v); }
89Datum to_datum(const semiring::Viterbi &, double v) { return Float8GetDatum(v); }
90Datum to_datum(const semiring::Lukasiewicz &, double v) { return Float8GetDatum(v); }
91Datum to_datum(const semiring::Formula &sr, const std::string &v) { return text_datum(sr.to_text(v)); }
92Datum to_datum(const semiring::Why &sr, const semiring::why_provenance_t &v) { return text_datum(sr.to_text(v)); }
93Datum to_datum(const semiring::How &sr, const semiring::how_provenance_t &v) { return text_datum(sr.to_text(v)); }
94Datum to_datum(const semiring::Which &sr, const semiring::which_provenance_t &v) { return text_datum(sr.to_text(v)); }
95#if PG_VERSION_NUM >= 140000
96Datum to_datum(const semiring::IntervalUnion &, Datum v) { return v; }
97#endif
98Datum to_datum(const semiring::MinMax &, Datum v) { return v; }
99
100/**
101 * @brief Run the four-step compiled-evaluation pipeline for a semiring.
102 *
103 * 1. Build the input-gate → leaf-value mapping by parsing each leaf via
104 * @c sr.parse_leaf().
105 * 2. Rewrite HAVING comparison gates in @p c via @c provsql_having().
106 * 3. Evaluate the main circuit at @p g.
107 * 4. Encode the result as a Postgres Datum via @c to_datum(sr, ...).
108 *
109 * @tparam Sem The compiled semiring class.
110 * @param constants Extension OID cache.
111 * @param c Generic circuit to evaluate.
112 * @param g Root gate.
113 * @param sr Semiring instance.
114 * @param drop_table Whether the temporary UUID table should be dropped.
115 * @return The semiring evaluation result encoded as a Datum.
116 */
117template <typename Sem>
118Datum pec(
119 const constants_t &constants,
121 gate_t g,
122 const Sem &sr,
123 bool drop_table)
124{
125 using V = typename Sem::value_type;
126 std::unordered_map<gate_t, V> mapping;
127 initialize_provenance_mapping<V>(constants, c, mapping,
128 [&sr](const char *v) { return sr.parse_leaf(v); }, drop_table);
129
130 provsql_having(c, g, mapping, sr);
131 V out = c.evaluate<Sem>(g, mapping, sr);
132 return to_datum(sr, std::move(out));
133}
134
135/**
136 * @brief Evaluate the BoolExpr semiring; bypasses the GenericCircuit pipeline.
137 *
138 * With @p labels, each input gate renders as its mapped label; without,
139 * leaves render as the default @c x@<id@> placeholders.
140 */
141Datum pec_boolexpr(
142 BooleanCircuit &bc,
143 gate_t root,
144 const std::unordered_map<gate_t, std::string> *labels)
145{
146 return text_datum(labels ? bc.toString(root, *labels) : bc.toString(root));
147}
148
149} // namespace
150
151/**
152 * @brief Join a provenance mapping table with a set of UUIDs using SPI.
153 * @param table OID of the provenance mapping relation.
154 * @param uuids List of UUID strings to join against.
155 * @return @c true if a temporary table was created (caller must drop it).
156 */
157bool join_with_temp_uuids(Oid table, const std::vector<std::string> &uuids) {
158 if (SPI_connect() != SPI_OK_CONNECT)
159 throw CircuitException("SPI_connect failed");
160
161 // No mapping given: run an empty join, so the provenance mapping stays
162 // empty and every leaf falls back to the semiring's one (the
163 // absent-mapping convention).
164 if (!OidIsValid(table)) {
165 if (SPI_exec("SELECT ''::text AS value, NULL::uuid AS provenance WHERE false",
166 0) != SPI_OK_SELECT) {
167 SPI_finish();
168 throw CircuitException("Join query failed");
169 }
170 return false;
171 }
172
173 char *table_name = get_rel_name(table);
174 if (!table_name) {
175 SPI_finish();
176 throw CircuitException("Invalid OID: no such table");
177 }
178 // Schema-qualify so the lookup works regardless of search_path.
179 // get_rel_name alone would resolve unqualified, which fails when the
180 // mapping lives in a schema not on search_path (e.g. provsql_test in
181 // the regression DB, or any user schema reached via Studio with the
182 // default "public, provsql" search_path).
183 const char *qualified = quote_qualified_identifier(
184 get_namespace_name(get_rel_namespace(table)), table_name);
185
186 constexpr size_t nb_max_uuid_value = 10000000;
187
188 StringInfoData join_query;
189 initStringInfo(&join_query);
190 bool drop_table = false;
191
192 // Two different mechanisms to implement the join (unless there are no
193 // UUIDs):
194 // if there are less than nb_max_uuid_value UUIDs, we do a join
195 // with a VALUES() list;
196 // otherwise we create a temporary table where we dump the inserts
197 // and join with it.
198 if(uuids.size() == 0) {
199 appendStringInfo(&join_query,
200 "SELECT value, provenance FROM %s WHERE 'f'", qualified);
201 } else if(uuids.size() <= nb_max_uuid_value) {
202 appendStringInfo(&join_query,
203 "SELECT value, provenance FROM %s t JOIN (VALUES", qualified);
204 bool first=true;
205 for(auto u: uuids) {
206 appendStringInfo(&join_query, "%s('%s'::uuid)", first?"":",", u.c_str());
207 first=false;
208 }
209
210 appendStringInfo(&join_query, ") AS u(id) ON t.provenance=u.id");
211 } else {
212 const char *create_temp_table = "CREATE TEMP TABLE tmp_uuids(id uuid);";
213 if (SPI_exec(create_temp_table, 0) != SPI_OK_UTILITY) {
214 SPI_finish();
215 throw CircuitException("Failed to create temporary table");
216 }
217 drop_table = true;
218
219 constexpr size_t batch_size = 1000;
220
221 for (size_t offset = 0; offset < uuids.size(); offset += batch_size) {
222 StringInfoData insert_query;
223 initStringInfo(&insert_query);
224 appendStringInfo(&insert_query, "INSERT INTO tmp_uuids VALUES ");
225
226 size_t end = std::min(offset + batch_size, uuids.size());
227 for (size_t i = offset; i < end; ++i) {
228 appendStringInfo(&insert_query, "('%s')%s", uuids[i].c_str(), (i + 1 == end) ? "" : ",");
229 }
230
231 int retval=SPI_exec(insert_query.data, 0);
232 pfree(insert_query.data);
233
234 if(retval != SPI_OK_INSERT) {
235 SPI_exec(drop_temp_table, 0);
236 SPI_finish();
237 throw CircuitException("Batch insert into temp table failed");
238 }
239 }
240
241 appendStringInfo(&join_query,
242 "SELECT value, provenance FROM %s t JOIN tmp_uuids u ON t.provenance = u.id", qualified);
243 }
244
245 if (SPI_exec(join_query.data, 0) != SPI_OK_SELECT) {
246 if(drop_table)
247 SPI_exec(drop_temp_table, 0);
248 SPI_finish();
249 throw CircuitException("Join query failed");
250 }
251
252 return drop_table;
253}
254
255/**
256 * @brief Core implementation of compiled provenance evaluation.
257 * @param token UUID of the root provenance gate.
258 * @param table OID of the provenance mapping relation.
259 * @param semiring Name of the semiring to evaluate over.
260 * @param type OID of the result element type.
261 * @return Datum containing the semiring evaluation result.
262 */
264 (pg_uuid_t token, Oid table, const std::string &semiring, Oid type)
265{
266 constants_t constants = get_constants(true);
267
268 // boolexpr without a mapping has nothing to fetch from SPI: skip the
269 // GenericCircuit build + temp-UUID join and read the BooleanCircuit
270 // directly. Every other case (including boolexpr WITH a mapping)
271 // shares the prelude below.
272 if(semiring=="boolexpr" && !OidIsValid(table)) {
273 gate_t root;
274 BooleanCircuit bc = getBooleanCircuit(token, root);
275 return pec_boolexpr(bc, root, nullptr);
276 }
277
278 // The expectation evaluator reads its leaves directly from the
279 // gate_rv / gate_value extra fields; no leaf-mapping table is
280 // involved. Bypass the SPI prelude.
281 if(semiring=="expectation") {
282 if(type != constants.OID_TYPE_FLOAT)
283 throw CircuitException(
284 "Unknown element type for expectation: must be float8");
286 auto root = gc.getGate(uuid2string(token));
287 return Float8GetDatum(provsql::compute_expectation(gc, root));
288 }
289
291 auto g = c.getGate(uuid2string(token));
292 auto inputs = c.getInputs();
293
294 std::vector<std::string> inputs_uuid;
295 std::transform(inputs.begin(), inputs.end(), std::back_inserter(inputs_uuid), [&c](auto x) {
296 return c.getUUID(x);
297 });
298 bool drop_table = join_with_temp_uuids(table, inputs_uuid);
299
300 if(semiring=="boolexpr") {
301 // boolexpr with a mapping: build the gate-to-label map on the
302 // GenericCircuit (the mapping table is keyed by input UUIDs that
303 // don't survive translation to BooleanCircuit gate ids), then
304 // translate keys to bc gates via gc_to_bc so bc.toString can label
305 // each leaf with its mapped value.
306 gate_t root;
307 std::unordered_map<gate_t, std::string> gc_labels;
308 initialize_provenance_mapping<std::string>(constants, c, gc_labels, [](const char *v) {
309 return std::string(v);
310 }, drop_table);
311
312 std::unordered_map<gate_t, gate_t> gc_to_bc;
313 BooleanCircuit bc = getBooleanCircuit(c, token, root, gc_to_bc);
314
315 std::unordered_map<gate_t, std::string> bc_labels;
316 bc_labels.reserve(gc_labels.size());
317 for(const auto &kv : gc_labels) {
318 auto it = gc_to_bc.find(kv.first);
319 if(it != gc_to_bc.end())
320 bc_labels[it->second] = kv.second;
321 }
322 return pec_boolexpr(bc, root, &bc_labels);
323 }
324
325 if (type == constants.OID_TYPE_VARCHAR)
326 {
327 if (semiring == "formula") return pec(constants, c, g, semiring::Formula{}, drop_table);
328 if (semiring == "why") return pec(constants, c, g, semiring::Why{}, drop_table);
329 if (semiring == "how") return pec(constants, c, g, semiring::How{}, drop_table);
330 if (semiring == "which") return pec(constants, c, g, semiring::Which{}, drop_table);
331 throw CircuitException("Unknown semiring for type varchar: " + semiring);
332 }
333 if (type == constants.OID_TYPE_INT) {
334 if (semiring == "counting") return pec(constants, c, g, semiring::Counting{}, drop_table);
335 throw CircuitException("Unknown semiring for type int: " + semiring);
336 }
337 if (type == constants.OID_TYPE_FLOAT) {
338 if (semiring == "tropical") return pec(constants, c, g, semiring::Tropical{}, drop_table);
339 if (semiring == "tropical_nonneg")
340 return pec(constants, c, g, semiring::TropicalNonneg{}, drop_table);
341 if (semiring == "viterbi") return pec(constants, c, g, semiring::Viterbi{}, drop_table);
342 if (semiring == "lukasiewicz") return pec(constants, c, g, semiring::Lukasiewicz{}, drop_table);
343 throw CircuitException("Unknown semiring for type float: " + semiring);
344 }
345 if (type == constants.OID_TYPE_BOOL) {
346 if (semiring == "boolean") return pec(constants, c, g, semiring::Boolean{}, drop_table);
347 throw CircuitException("Unknown semiring for type bool: " + semiring);
348 }
349#if PG_VERSION_NUM >= 140000
350 if (type == constants.OID_TYPE_TSTZMULTIRANGE) {
351 if (semiring != "temporal" && semiring != "interval_union")
352 throw CircuitException("Unknown semiring for type tstzmultirange: " + semiring);
353 return pec(constants, c, g, semiring::IntervalUnion(constants.OID_TYPE_TSTZMULTIRANGE), drop_table);
354 }
355 if (type == constants.OID_TYPE_NUMMULTIRANGE) {
356 if (semiring != "interval_union")
357 throw CircuitException("Unknown semiring for type nummultirange: " + semiring);
358 return pec(constants, c, g, semiring::IntervalUnion(constants.OID_TYPE_NUMMULTIRANGE), drop_table);
359 }
360 if (type == constants.OID_TYPE_INT4MULTIRANGE) {
361 if (semiring != "interval_union")
362 throw CircuitException("Unknown semiring for type int4multirange: " + semiring);
363 return pec(constants, c, g, semiring::IntervalUnion(constants.OID_TYPE_INT4MULTIRANGE), drop_table);
364 }
365#endif
366 if (get_typtype(type) == TYPTYPE_ENUM) {
367 if (semiring == "minmax") return pec(constants, c, g, semiring::MinMax(type, false), drop_table);
368 if (semiring == "maxmin") return pec(constants, c, g, semiring::MinMax(type, true), drop_table);
369 throw CircuitException("Unknown semiring for enum type: " + semiring);
370 }
371 throw CircuitException("Unknown element type for provenance_evaluate_compiled");
372}
373
374/** @brief PostgreSQL-callable wrapper for provenance_evaluate_compiled(). */
375Datum provenance_evaluate_compiled(PG_FUNCTION_ARGS)
376{
377 /* The sr_* SQL wrappers are STRICT, but the function itself is not:
378 * guard the required arguments rather than rely on the wrappers. The
379 * mapping (argument 1) is legitimately NULL -- the no-mapping mode
380 * renders anonymous leaves -- and reads as InvalidOid below. */
381 if (PG_ARGISNULL(0) || PG_ARGISNULL(2))
382 PG_RETURN_NULL();
383
384 try {
385 Datum token = PG_GETARG_DATUM(0);
386
387 Oid table = PG_GETARG_OID(1);
388
389 text *t = PG_GETARG_TEXT_P(2);
390 std::string semiring(VARDATA(t),VARSIZE(t)-VARHDRSZ);
391
392 Oid type = get_fn_expr_argtype(fcinfo->flinfo, 3);
393
394 return provenance_evaluate_compiled_internal(*DatumGetUUIDP(token), table, semiring, type);
395 } catch(const std::exception &e) {
396 provsql_error("provenance_evaluate_compiled: %s", e.what());
397 } catch(...) {
398 provsql_error("provenance_evaluate_compiled: Unknown exception");
399 }
400
401 PG_RETURN_NULL();
402}
403
404namespace {
405
406/** Truth of the gates already met, in the database as it is: a gate never
407 * changes once created, nor does an input tuple's presence for the rows
408 * that read it. Bounded, so that a long session does not grow it forever. */
409std::unordered_map<std::string, bool> plain_truth_memo;
410constexpr size_t plain_truth_memo_max = 1000000;
411
412/** 1 / 0 for a gate whose truth follows from its type and its children's,
413 * -1 for one that needs the full evaluation (a comparison, ...). */
414int quick_plain_truth(const pg_uuid_t &token, unsigned depth)
415{
416 std::string key = uuid2string(token);
417 auto it = plain_truth_memo.find(key);
418 if (it != plain_truth_memo.end())
419 return it->second;
420 if (depth > 64)
421 return -1;
422
423 unsigned nb_children = 0;
424 pg_uuid_t *children = NULL;
425 gate_type type = provsql_fetch_gate(&token, &nb_children, &children);
426 int res = -1;
427
428 switch (type) {
429 case gate_input: case gate_mulinput: case gate_one:
430 res = 1;
431 break;
432 case gate_zero:
433 res = 0;
434 break;
435 case gate_times: case gate_plus: {
436 bool is_times = type == gate_times;
437 res = is_times ? 1 : 0;
438 for (unsigned i = 0; i < nb_children; ++i) {
439 int c = quick_plain_truth(children[i], depth + 1);
440 if (c < 0) { res = -1; break; }
441 if (is_times && !c) { res = 0; break; }
442 if (!is_times && c) { res = 1; break; }
443 }
444 break;
445 }
446 case gate_monus:
447 if (nb_children == 2) {
448 int a = quick_plain_truth(children[0], depth + 1);
449 int b = a == 1 ? quick_plain_truth(children[1], depth + 1) : 0;
450 res = a < 0 || b < 0 ? -1 : (a && !b);
451 }
452 break;
453 case gate_delta: case gate_project: case gate_eq: case gate_annotation:
454 case gate_assumed:
455 if (nb_children >= 1)
456 res = quick_plain_truth(children[0], depth + 1);
457 break;
458 case gate_cmp: {
459 /* A comparison of aggregate results holds, in the database as it is, by
460 * the values its sides record there: agg_guard_holds reads them off the
461 * gates. Deciding it that way rather than over the Boolean semiring
462 * matters, not merely for speed: the semiring reads a comparison through
463 * the worlds of what it aggregates, which is 2^n terms for n
464 * contributions, so a comparison over a few dozen rows -- one per row an
465 * exploded aggregate value produces -- would not finish. A comparison
466 * whose sides record no value (a random variable, a text aggregate)
467 * yields NULL and takes the evaluation below. */
468 constants_t constants = get_constants(false);
469 if (!constants.ok || !OidIsValid(constants.OID_FUNCTION_AGG_GUARD_HOLDS))
470 break;
471 {
472 FmgrInfo flinfo;
473 /* PostgreSQL 12 made the argument array variable-length, and with it
474 * LOCAL_FCINFO and fcinfo->args[]; before that the call info is a plain
475 * struct with fixed arg / argnull arrays. */
476#if PG_VERSION_NUM >= 120000
477 LOCAL_FCINFO(fcinfo, 1);
478#else
479 FunctionCallInfoData fcinfo_data;
480 FunctionCallInfo fcinfo = &fcinfo_data;
481#endif
482 Datum d;
483
484 fmgr_info(constants.OID_FUNCTION_AGG_GUARD_HOLDS, &flinfo);
485 InitFunctionCallInfoData(*fcinfo, &flinfo, 1, InvalidOid, NULL, NULL);
486#if PG_VERSION_NUM >= 120000
487 fcinfo->args[0].value = UUIDPGetDatum(&token);
488 fcinfo->args[0].isnull = false;
489#else
490 fcinfo->arg[0] = UUIDPGetDatum(&token);
491 fcinfo->argnull[0] = false;
492#endif
493 d = FunctionCallInvoke(fcinfo);
494 if (!fcinfo->isnull)
495 res = DatumGetBool(d) ? 1 : 0;
496 }
497 break;
498 }
499 default:
500 break;
501 }
502 free(children);
503
504 if (res >= 0) {
505 if (plain_truth_memo.size() >= plain_truth_memo_max)
506 plain_truth_memo.clear();
507 plain_truth_memo.emplace(std::move(key), res == 1);
508 }
509 return res;
510}
511
512} // namespace
513
514/**
515 * @brief Whether a token holds in the database as it is, every input tuple
516 * present: @c sr_boolean without a mapping.
517 *
518 * The rewriter filters by it the rows a displayed aggregate value reads, so
519 * that the value is what plain SQL computes on the same data: a row kept only
520 * for its provenance (the padding of a lowered outer join, a group a
521 * @c HAVING rejects, a row beyond a rank-filtered @c LIMIT) does not count.
522 * Gates whose truth follows from their children's (⊗, ⊕, ⊖, δ, ...) are
523 * walked directly, through the backend's gate cache; a comparison is left to
524 * the evaluation over the Boolean semiring, with no mapping to read.
525 */
526Datum plain_truth(PG_FUNCTION_ARGS)
527{
528 pg_uuid_t token;
529
530 if (PG_ARGISNULL(0))
531 PG_RETURN_BOOL(true);
532 token = *PG_GETARG_UUID_P(0);
533
534 int quick = quick_plain_truth(token, 0);
535 if (quick >= 0)
536 PG_RETURN_BOOL(quick == 1);
537
538 /* A token the Boolean semiring cannot evaluate (a comparison on a random
539 * variable, which has no value in the database as it is) holds: the row
540 * counts, as it did before this filter. */
541 bool res = true;
542 try {
544 gate_t g = c.getGate(uuid2string(token));
546 std::unordered_map<gate_t, bool> mapping;
547 provsql_having(c, g, mapping, sr);
548 res = c.evaluate<semiring::Boolean>(g, mapping, sr);
549 } catch(const std::exception &) {
550 res = true;
551 }
552 if (plain_truth_memo.size() >= plain_truth_memo_max)
553 plain_truth_memo.clear();
554 plain_truth_memo.emplace(uuid2string(token), res);
555 PG_RETURN_BOOL(res);
556}
Boolean-expression (lineage formula) semiring.
Boolean semiring ({false, true}, ∨, ∧, false, true).
BooleanCircuit getBooleanCircuit(GenericCircuit &gc, pg_uuid_t token, gate_t &gate, std::unordered_map< gate_t, gate_t > &gc_to_bc)
Build a BooleanCircuit from an already-loaded GenericCircuit.
GenericCircuit getGenericCircuit(pg_uuid_t token)
Build a GenericCircuit from the mmap store rooted at token.
gate_t
Strongly-typed gate identifier.
Definition Circuit.h:49
Counting semiring (ℕ, +, ×, 0, 1).
Analytical expectation / variance / moment evaluator over RV circuits.
Symbolic representation of provenance as a human-readable formula.
How-provenance m-semiring (canonical polynomial provenance ℕ[X]).
Interval-union m-semiring over PostgreSQL multirange types.
Łukasiewicz fuzzy m-semiring over .
Min-max and max-min m-semirings over PostgreSQL enum types.
Tropical (min-plus) m-semiring over .
Viterbi (max-times) m-semiring over .
Which-provenance (lineage) m-semiring.
Why-provenance semiring (set of witness sets).
Boolean circuit for provenance formula evaluation.
virtual std::string toString(gate_t g) const override
Return a textual description of gate g for debugging.
Exception type thrown by circuit operations on invalid input.
Definition Circuit.h:206
gate_t getGate(const uuid &u)
Return (or create) the gate associated with UUID u.
Definition Circuit.hpp:33
In-memory provenance circuit with semiring-generic evaluation.
S::value_type evaluate(gate_t g, std::unordered_map< gate_t, typename S::value_type > &provenance_mapping, S semiring) const
Evaluate the sub-circuit rooted at gate g over semiring semiring.
const std::set< gate_t > & getInputs() const
Return the set of input (leaf) gates.
The Boolean semiring over bool.
Definition Boolean.h:41
The counting semiring over unsigned.
Definition Counting.h:42
Symbolic provenance representation over std::string.
Definition Formula.h:140
value_type parse_leaf(const char *v) const
Definition Formula.h:471
std::string to_text(const value_type &s) const
Serialise a Formula evaluation as text.
Definition Formula.h:491
How-provenance m-semiring over .
Definition How.h:72
Interval-union m-semiring with Datum carrier, parameterised by a multirange type OID.
The Łukasiewicz fuzzy m-semiring over double.
Definition Lukasiewicz.h:58
Min-max / max-min m-semiring with Datum carrier over a PostgreSQL enum type.
Definition MinMax.h:77
Tropical (min-plus) m-semiring restricted to nonnegative costs.
Definition Tropical.h:122
Tropical (min-plus) m-semiring over double.
Definition Tropical.h:52
The Viterbi (max-times) m-semiring over double.
Definition Viterbi.h:50
Which-provenance (lineage) semiring.
Definition Which.h:58
Why-provenance semiring.
Definition Why.h:56
Provenance evaluation helper for HAVING-clause circuits.
void provsql_having(GenericCircuit &c, gate_t g, MapT &mapping, SemiringT S=SemiringT{})
Rewrite HAVING comparison gates in the circuit into their possible-world provenance (world enumeratio...
double compute_expectation(const GenericCircuit &gc, gate_t root, std::optional< gate_t > event_root)
Compute (or if event_root is set) over the scalar sub-circuit rooted at root.
std::map< how_monomial_t, unsigned > how_provenance_t
How-provenance value: each monomial mapped to its (positive) coefficient.
Definition How.h:62
std::set< label_set > why_provenance_t
Why-provenance value: the full set of all witnesses.
Definition Why.h:49
std::optional< std::set< std::string > > which_provenance_t
Which-provenance value: a set of labels, or (empty optional).
Definition Which.h:50
static Datum provenance_evaluate_compiled_internal(pg_uuid_t token, Oid table, const std::string &semiring, Oid type)
Core implementation of compiled provenance evaluation.
const char * drop_temp_table
DROP TABLE statement for the per-query temporary provenance mapping table.
bool join_with_temp_uuids(Oid table, const std::vector< std::string > &uuids)
Join a provenance mapping table with a set of UUIDs using SPI.
Datum provenance_evaluate_compiled(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for provenance_evaluate_compiled().
Datum plain_truth(PG_FUNCTION_ARGS)
Whether a token holds in the database as it is, every input tuple present: sr_boolean without a mappi...
Template helper for populating provenance mappings from SPI results.
void initialize_provenance_mapping(const constants_t &constants, GenericCircuit &c, std::unordered_map< gate_t, T > &provenance_mapping, const std::function< T(const char *)> &charp_to_value, bool drop_table)
Populate a provenance mapping from the current SPI result set.
#define provsql_error(fmt,...)
Report a fatal ProvSQL error and abort the current transaction.
gate_type provsql_fetch_gate(const pg_uuid_t *token, unsigned *nb_children_out, pg_uuid_t **children_out)
PostgreSQL-callable wrapper for get_gate_type().
Background worker and IPC primitives for mmap-backed circuit storage.
constants_t get_constants(bool failure_if_not_possible)
Retrieve the cached OID constants for the current database.
Core types, constants, and utilities shared across ProvSQL.
@ gate_annotation
Transparent single-child wrapper carrying a query-level annotation in extra (inversion-free certifica...
@ gate_assumed
Structural marker over a single child whose sub-circuit was computed under a Boolean-provenance assum...
string uuid2string(pg_uuid_t uuid)
Format a pg_uuid_t as a std::string.
Structure to store the value of various constants.
Oid OID_TYPE_VARCHAR
OID of the VARCHAR TYPE.
Oid OID_FUNCTION_AGG_GUARD_HOLDS
OID of agg_guard_holds(), the truth of a comparison of aggregate results in the database as it is,...
Oid OID_TYPE_FLOAT
OID of the FLOAT TYPE.
Oid OID_TYPE_INT
OID of the INT TYPE.
Oid OID_TYPE_TSTZMULTIRANGE
OID of the tstzmultirange TYPE (PG14+, InvalidOid otherwise).
bool ok
true if constants were loaded
Oid OID_TYPE_BOOL
OID of the BOOL TYPE.
Oid OID_TYPE_NUMMULTIRANGE
OID of the nummultirange TYPE (PG14+, InvalidOid otherwise).
Oid OID_TYPE_INT4MULTIRANGE
OID of the int4multirange TYPE (PG14+, InvalidOid otherwise).