ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
gate_builders.c
Go to the documentation of this file.
1/**
2 * @file gate_builders.c
3 * @brief The gate-building SQL functions the query rewriter calls once per
4 * row or per group, in C.
5 *
6 * These functions used to be PL/pgSQL. Each inner statement of a PL/pgSQL
7 * function runs through SPI, an executor start-up and shutdown of about 2 µs,
8 * and their keys went through the text form of @c uuid[] and back; at one
9 * call per output row this was most of the cost of provenance tracking. The
10 * C versions build the same gates at the same addresses: a gate is addressed
11 * by the version-5 UUID, in the ProvSQL namespace, of a text name
12 * (@c "times{u1,u2}", ...), and the names built here are, byte for byte, the
13 * ones the PL/pgSQL code built. The regression test @c gate_builders keeps
14 * the former bodies as reference functions and compares the two.
15 */
16#include "postgres.h"
17#include "fmgr.h"
18#include "catalog/namespace.h"
19#include "catalog/pg_type.h"
20#include "parser/parse_coerce.h"
21#include "lib/stringinfo.h"
22#include "utils/array.h"
23#include "utils/builtins.h"
24#include "utils/hsearch.h"
25#include "utils/lsyscache.h"
26#include "utils/memutils.h"
27#include "utils/uuid.h"
28
29#include "agg_token.h"
30#include "provsql_mmap.h"
31#include "provsql_utils.h"
32
33/** @brief @c uuid_ns_provsql(), the namespace of every gate address. */
34static const unsigned char provsql_ns[UUID_LEN] = {
35 0x92, 0x0d, 0x4f, 0x02, 0x87, 0x18, 0x53, 0x19,
36 0x95, 0x32, 0xd4, 0xab, 0x83, 0xa6, 0x44, 0x89
37};
38
39/**
40 * @brief Start the name of a gate address.
41 *
42 * The buffer opens with the namespace, which the version-5 UUID hashes in
43 * front of the name, so that the hash runs over the buffer as it is.
44 */
45static void name_begin(StringInfo buf, const char *prefix) {
46 initStringInfo(buf);
47 appendBinaryStringInfo(buf, (const char *)provsql_ns, UUID_LEN);
48 appendStringInfoString(buf, prefix);
49}
50
51/** @brief Append a UUID in the text form of @c uuid_out. */
52static void name_add_uuid(StringInfo buf, const pg_uuid_t *u) {
53 static const char hex[] = "0123456789abcdef";
54 char out[36];
55 int i, p = 0;
56
57 for (i = 0; i < UUID_LEN; ++i) {
58 if (i == 4 || i == 6 || i == 8 || i == 10)
59 out[p++] = '-';
60 out[p++] = hex[u->data[i] >> 4];
61 out[p++] = hex[u->data[i] & 0xF];
62 }
63 appendBinaryStringInfo(buf, out, 36);
64}
65
66/** @brief Append the text form of a @c uuid[], @c "{u1,u2,...}". */
67static void name_add_uuid_array(StringInfo buf, const pg_uuid_t *u, int n) {
68 int i;
69
70 appendStringInfoChar(buf, '{');
71 for (i = 0; i < n; ++i) {
72 if (i > 0)
73 appendStringInfoChar(buf, ',');
74 name_add_uuid(buf, &u[i]);
75 }
76 appendStringInfoChar(buf, '}');
77}
78
79/** @brief The version-5 UUID of the name accumulated in @p buf. */
80static pg_uuid_t name_end(StringInfo buf) {
81 unsigned char digest[20];
82 pg_uuid_t u;
83
84 provsql_sha1((const unsigned char *)buf->data, buf->len, digest);
85 memcpy(u.data, digest, UUID_LEN);
86 u.data[6] = (unsigned char)((u.data[6] & 0x0F) | 0x50);
87 u.data[8] = (unsigned char)((u.data[8] & 0x3F) | 0x80);
88 pfree(buf->data);
89 return u;
90}
91
92/** @brief The UUID @p text (one of the constants of @c provsql_utils.h). */
93static pg_uuid_t constant_uuid(const char *text) {
94 return *DatumGetUUIDP(DirectFunctionCall1(uuid_in, CStringGetDatum(text)));
95}
96
97static const pg_uuid_t *address_of_zero(void) {
98 static pg_uuid_t u;
99 static bool known = false;
100 if (!known) { u = constant_uuid(PROVSQL_GATE_ZERO_UUID); known = true; }
101 return &u;
102}
103
104static const pg_uuid_t *address_of_one(void) {
105 static pg_uuid_t u;
106 static bool known = false;
107 if (!known) { u = constant_uuid(PROVSQL_GATE_ONE_UUID); known = true; }
108 return &u;
109}
110
111static int token_cmp(const void *a, const void *b) {
112 return memcmp(a, b, UUID_LEN);
113}
114
115static Datum uuid_result(const pg_uuid_t *u) {
116 pg_uuid_t *r = (pg_uuid_t *)palloc(UUID_LEN);
117 memcpy(r, u, UUID_LEN);
118 return UUIDPGetDatum(r);
119}
120
121/**
122 * @brief The elements of a @c uuid[] that are neither NULL nor @p neutral,
123 * in order. @return their number; @p *out is palloc'd.
124 */
125static int filtered_tokens(ArrayType *arr, const pg_uuid_t *neutral,
126 pg_uuid_t **out) {
127 Datum *elems;
128 bool *nulls;
129 int n, i, kept = 0;
130
131 deconstruct_array(arr, UUIDOID, UUID_LEN, false, 'c', &elems, &nulls, &n);
132 *out = (pg_uuid_t *)palloc(sizeof(pg_uuid_t) * (n > 0 ? n : 1));
133 for (i = 0; i < n; ++i) {
134 pg_uuid_t *u;
135 if (nulls[i])
136 continue;
137 u = DatumGetUUIDP(elems[i]);
138 if (memcmp(u->data, neutral->data, UUID_LEN) == 0)
139 continue;
140 (*out)[kept++] = *u;
141 }
142 return kept;
143}
144
145/** @brief The canonical address, @c "<prefix>{sorted children}". */
146static pg_uuid_t canonical_address(const char *canonical_prefix,
147 const pg_uuid_t *children, int n) {
148 StringInfoData buf;
149 pg_uuid_t *sorted;
150
151 sorted = (pg_uuid_t *)palloc(sizeof(pg_uuid_t) * (n > 0 ? n : 1));
152 memcpy(sorted, children, sizeof(pg_uuid_t) * n);
153 qsort(sorted, n, sizeof(pg_uuid_t), token_cmp);
154 name_begin(&buf, canonical_prefix);
155 if (n > 0)
156 name_add_uuid_array(&buf, sorted, n);
157 pfree(sorted);
158 return name_end(&buf);
159}
160
161/* -------------------------------------------------------------------------
162 * Planted gates
163 *
164 * The reachability route compiles, at plan time, a certified circuit
165 * equivalent to a sum or product of correlated tokens of the working table it
166 * has just filled, and wants the query to use that circuit where the generic
167 * rewriting builds the sum or the product. It pre-creates ("plants") a gate
168 * at the canonical address of the multiset of tokens, and provenance_plus /
169 * provenance_times return that gate when they are given that very multiset.
170 *
171 * Which addresses are planted is known to the backend that planted them, and
172 * to no one else: the functions below look them up in a table local to the
173 * backend, never in the store. This is enough, because the sum or product
174 * in question is computed above a scan of the working table, a temporary
175 * table: PostgreSQL runs it in the backend that owns the table, not in a
176 * parallel worker, and that backend is the one which lowered the CTE. When
177 * nothing is planted, which is the case of every session that ran no
178 * reachability query, the canonical address is not even computed.
179 *
180 * A planted gate is needed for as long as its working table exists: a cached
181 * plan keeps reading the table, and is invalidated when the table is dropped,
182 * so that the next execution lowers the CTE and plants again. The entries
183 * are therefore grouped by working table, and discarded with it. Gates of
184 * the store outlive an aborted transaction, so an entry never names a gate
185 * that does not exist.
186 * ------------------------------------------------------------------------- */
187
188/** @brief A working table with planted gates. */
189typedef struct PlantedScope {
190 int id;
191 Oid relid; /**< The table, as created by the lowering */
192 char name[NAMEDATALEN];
195
196typedef struct PlantedEntry {
197 pg_uuid_t address; /**< Hash key */
198 int scope;
200
201static HTAB *planted = NULL;
204static int planted_next_id = 0;
205
206/** @brief OID of the temporary table @p name of this backend, if any. */
207static Oid temp_table_oid(const char *name) {
208 Oid nsp = LookupExplicitNamespace("pg_temp", true);
209 return OidIsValid(nsp) ? get_relname_relid(name, nsp) : InvalidOid;
210}
211
212static void planted_forget(PlantedScope *scope) {
213 HASH_SEQ_STATUS seq;
214 PlantedEntry *e;
215 PlantedScope **link;
216
217 if (planted != NULL) {
218 hash_seq_init(&seq, planted);
219 while ((e = (PlantedEntry *)hash_seq_search(&seq)) != NULL)
220 if (e->scope == scope->id)
221 hash_search(planted, &e->address, HASH_REMOVE, NULL);
222 }
223 for (link = &planted_scopes; *link != NULL; link = &(*link)->next)
224 if (*link == scope) {
225 *link = scope->next;
226 break;
227 }
228 if (planted_current == scope)
229 planted_current = NULL;
230 pfree(scope);
231}
232
233/**
234 * @brief Open the scope of the working table @p name, just (re)created:
235 * forget what was planted for the table it replaces, and for every
236 * working table that no longer exists.
237 */
238static PlantedScope *planted_open_scope(const char *name, bool reset) {
239 PlantedScope *s, *next, *found = NULL;
240
241 for (s = planted_scopes; s != NULL; s = next) {
242 next = s->next;
243 if (strcmp(s->name, name) == 0) {
244 if (reset)
246 else
247 found = s;
248 } else if (temp_table_oid(s->name) != s->relid)
250 }
251 if (found == NULL) {
252 found = (PlantedScope *)MemoryContextAllocZero(TopMemoryContext,
253 sizeof(PlantedScope));
254 found->id = planted_next_id++;
255 strlcpy(found->name, name, NAMEDATALEN);
256 found->next = planted_scopes;
257 planted_scopes = found;
258 }
259 found->relid = temp_table_oid(name);
260 return found;
261}
262
263static bool is_planted(const pg_uuid_t *address) {
264 return hash_search(planted, address, HASH_FIND, NULL) != NULL;
265}
266
267pg_uuid_t provsql_plant_canonical(const char *work_name, gate_type type,
268 const pg_uuid_t *children, int n,
269 const pg_uuid_t *target,
270 unsigned info1, unsigned info2) {
271 PlantedScope *scope;
272 PlantedEntry *e;
273 pg_uuid_t address;
274 bool found;
275
276 if (type != gate_plus && type != gate_times)
277 provsql_error("only plus and times gates can be planted");
278 scope = work_name != NULL ? planted_open_scope(work_name, false)
280 if (scope == NULL)
281 provsql_error("planting a gate outside the lowering of a recursive CTE");
282
283 address = canonical_address(type == gate_plus ? "plus-canonical"
284 : "times-canonical",
285 children, n);
286 provsql_internal_create_gate_with(&address, type, 1, target,
287 true, info1, info2, NULL);
288
289 if (planted == NULL) {
290 HASHCTL ctl;
291 memset(&ctl, 0, sizeof(ctl));
292 ctl.keysize = sizeof(pg_uuid_t);
293 ctl.entrysize = sizeof(PlantedEntry);
294 ctl.hcxt = TopMemoryContext;
295 planted = hash_create("ProvSQL planted gates", 256, &ctl,
296 HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
297 }
298 e = (PlantedEntry *)hash_search(planted, &address, HASH_ENTER, &found);
299 e->scope = scope->id;
300 return address;
301}
302
303PG_FUNCTION_INFO_V1(planted_scope);
304/** @brief SQL entry point of @c planted_open_scope(), called by the drivers
305 * of recursive CTEs right after they (re)create the working table. */
306Datum planted_scope(PG_FUNCTION_ARGS) {
307 char *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
309 PG_RETURN_VOID();
310}
311
312PG_FUNCTION_INFO_V1(plant_canonical);
313/** @brief SQL entry point of @c provsql_plant_canonical(). */
314Datum plant_canonical(PG_FUNCTION_ARGS) {
315 char *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
316 char *type_name = text_to_cstring(PG_GETARG_TEXT_PP(1));
317 pg_uuid_t *children, address;
318 pg_uuid_t nothing;
319 int n;
320
321 memset(&nothing, 0xFF, sizeof(nothing));
322 n = filtered_tokens(PG_GETARG_ARRAYTYPE_P(2), &nothing, &children);
323 address = provsql_plant_canonical(
324 name, strcmp(type_name, "plus") == 0 ? gate_plus :
325 strcmp(type_name, "times") == 0 ? gate_times : gate_invalid,
326 children, n, PG_GETARG_UUID_P(3),
327 (unsigned)PG_GETARG_INT32(4), (unsigned)PG_GETARG_INT32(5));
328 return uuid_result(&address);
329}
330
331/**
332 * @brief A gate of type @p type over @p children, unless this backend planted
333 * one at the canonical address of their multiset.
334 */
335static pg_uuid_t nary_gate(gate_type type, const char *plain_prefix,
336 const char *canonical_prefix,
337 const pg_uuid_t *children, int n) {
338 StringInfoData buf;
339 pg_uuid_t token;
340
341 if (planted != NULL && hash_get_num_entries(planted) > 0) {
342 pg_uuid_t canonical = canonical_address(canonical_prefix, children, n);
343 if (is_planted(&canonical))
344 return canonical;
345 }
346
347 name_begin(&buf, plain_prefix);
348 if (n > 0)
349 name_add_uuid_array(&buf, children, n);
350 token = name_end(&buf);
351 provsql_internal_create_gate(&token, type, (unsigned)n, n > 0 ? children : NULL);
352 return token;
353}
354
355PG_FUNCTION_INFO_V1(provenance_times);
356/**
357 * @brief ⊗ of a list of tokens.
358 *
359 * A NULL token is the token slot of an untracked source, which is certain: it
360 * reads as the ⊗-neutral 𝟙 and is dropped, like @c gate_one itself. No
361 * survivor gives 𝟙, a single one is returned as it is.
362 */
363Datum provenance_times(PG_FUNCTION_ARGS) {
364 pg_uuid_t *tokens, result;
365 int n;
366
367 if (PG_ARGISNULL(0))
368 return uuid_result(address_of_one());
369 n = filtered_tokens(PG_GETARG_ARRAYTYPE_P(0), address_of_one(), &tokens);
370 if (n == 0)
371 return uuid_result(address_of_one());
372 if (n == 1)
373 return uuid_result(&tokens[0]);
374 result = nary_gate(gate_times, "times", "times-canonical", tokens, n);
375 return uuid_result(&result);
376}
377
378PG_FUNCTION_INFO_V1(provenance_plus);
379/**
380 * @brief ⊕ of an array of tokens. Strict.
381 *
382 * A NULL token stands for a row absent from the disjunction: it reads as the
383 * ⊕-neutral 𝟘 and is dropped, like @c gate_zero itself. No survivor gives 𝟘,
384 * a single one is returned as it is. (The PL/pgSQL version returned, for no
385 * survivor, a @c plus gate without children: @c array_length of the empty
386 * selection is NULL, which sent that case to the general branch. Evaluators
387 * read such a gate as 𝟘, and stored circuits may contain it.)
388 */
389Datum provenance_plus(PG_FUNCTION_ARGS) {
390 pg_uuid_t *tokens, result;
391 int n;
392
393 n = filtered_tokens(PG_GETARG_ARRAYTYPE_P(0), address_of_zero(), &tokens);
394 if (n == 0)
396 if (n == 1)
397 return uuid_result(&tokens[0]);
398 result = nary_gate(gate_plus, "plus", "plus-canonical", tokens, n);
399 return uuid_result(&result);
400}
401
402/* -------------------------------------------------------------------------
403 * Values
404 *
405 * A value gate is addressed by "value" followed by CAST(val AS varchar), and
406 * carries that text as its extra. The cast is resolved as the parser resolves
407 * it (a cast function, the output function, or nothing for a text-like type),
408 * once per call site: true::varchar is 'true' where the output function of
409 * boolean writes 't'.
410 * ------------------------------------------------------------------------- */
411
412typedef struct VarcharCast {
413 Oid type;
414 CoercionPathType path;
415 FmgrInfo fn;
416 int nargs;
418
419/** @brief @c CAST(val AS varchar) of argument @p argno, as a C string. */
420static char *varchar_of_argument(FunctionCallInfo fcinfo, int argno) {
421 VarcharCast *cast = (VarcharCast *)fcinfo->flinfo->fn_extra;
422 Oid type = get_fn_expr_argtype(fcinfo->flinfo, argno);
423 Datum val = PG_GETARG_DATUM(argno);
424
425 if (!OidIsValid(type))
426 provsql_error("cannot determine the type of the value");
427 if (cast == NULL || cast->type != type) {
428 Oid funcid = InvalidOid;
429
430 if (cast == NULL)
431 cast = (VarcharCast *)MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
432 sizeof(VarcharCast));
433 cast->path = find_coercion_pathway(VARCHAROID, type, COERCION_EXPLICIT,
434 &funcid);
435 if (cast->path == COERCION_PATH_FUNC) {
436 fmgr_info_cxt(funcid, &cast->fn, fcinfo->flinfo->fn_mcxt);
437 cast->nargs = get_func_nargs(funcid);
438 } else if (cast->path != COERCION_PATH_RELABELTYPE) {
439 bool is_varlena;
440 cast->path = COERCION_PATH_COERCEVIAIO;
441 getTypeOutputInfo(type, &funcid, &is_varlena);
442 fmgr_info_cxt(funcid, &cast->fn, fcinfo->flinfo->fn_mcxt);
443 }
444 cast->type = type;
445 fcinfo->flinfo->fn_extra = cast;
446 }
447
448 switch (cast->path) {
449 case COERCION_PATH_FUNC: {
450 Datum r;
451 if (cast->nargs == 1)
452 r = FunctionCall1(&cast->fn, val);
453 else if (cast->nargs == 2)
454 r = FunctionCall2(&cast->fn, val, Int32GetDatum(-1));
455 else
456 r = FunctionCall3(&cast->fn, val, Int32GetDatum(-1), BoolGetDatum(true));
457 return text_to_cstring(DatumGetTextPP(r));
458 }
459 case COERCION_PATH_RELABELTYPE:
460 return text_to_cstring(DatumGetTextPP(val));
461 default:
462 return OutputFunctionCall(&cast->fn, val);
463 }
464}
465
466/* The value gates this backend has written. The same few values come back
467 * row after row (the 1 of every count), and a gate of the store stays as it
468 * is once written, so there is nothing to repeat. Emptied when it grows
469 * large, and by circuit_cleanup, which may remove gates. */
470static HTAB *written_values = NULL;
471#define WRITTEN_VALUES_MAX 65536
472
474 if (written_values != NULL) {
475 hash_destroy(written_values);
476 written_values = NULL;
477 }
478 while (planted_scopes != NULL)
480}
481
482/** @brief The value gate of the text @p str, at the address named @p name
483 * (@c "value" followed by @p str, or @c "null"). */
484static pg_uuid_t value_gate(const char *prefix, const char *suffix,
485 const char *extra) {
486 StringInfoData buf;
487 pg_uuid_t token;
488 bool found;
489
490 name_begin(&buf, prefix);
491 appendStringInfoString(&buf, suffix);
492 token = name_end(&buf);
493
494 if (written_values != NULL
495 && hash_get_num_entries(written_values) >= WRITTEN_VALUES_MAX) {
496 hash_destroy(written_values);
497 written_values = NULL;
498 }
499 if (written_values == NULL) {
500 HASHCTL ctl;
501 memset(&ctl, 0, sizeof(ctl));
502 ctl.keysize = sizeof(pg_uuid_t);
503 ctl.entrysize = sizeof(pg_uuid_t);
504 ctl.hcxt = TopMemoryContext;
505 written_values = hash_create("ProvSQL value gates written", 256, &ctl,
506 HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
507 }
508 if (hash_search(written_values, &token, HASH_FIND, NULL) == NULL) {
510 false, 0, 0, extra);
511 hash_search(written_values, &token, HASH_ENTER, &found);
512 }
513 return token;
514}
515
516/** @brief The semimod gate @p value_token ⊗ @p token. */
517static Datum semimod_gate(const pg_uuid_t *value_token, const pg_uuid_t *token) {
518 StringInfoData buf;
519 pg_uuid_t semimod, children[2];
520
521 name_begin(&buf, "semimod");
522 name_add_uuid(&buf, value_token);
523 name_add_uuid(&buf, token);
524 semimod = name_end(&buf);
525 children[0] = *token;
526 children[1] = *value_token;
527 provsql_internal_create_gate(&semimod, gate_semimod, 2, children);
528 return uuid_result(&semimod);
529}
530
531static const pg_uuid_t *semimod_token_argument(FunctionCallInfo fcinfo) {
532 if (PG_ARGISNULL(1))
533 provsql_error("provenance_semimod: the provenance token must not be NULL");
534 return PG_GETARG_UUID_P(1);
535}
536
537PG_FUNCTION_INFO_V1(provenance_semimod);
538/**
539 * @brief The semimodule gate @c val ⊗ @c token of an aggregated row.
540 *
541 * A NULL value does not take part in the aggregate (SQL aggregates skip NULL
542 * inputs; @c count(*) passes a constant 1): no gate, NULL is returned and
543 * @c provenance_aggregate drops it.
544 */
545Datum provenance_semimod(PG_FUNCTION_ARGS) {
546 const pg_uuid_t *token;
547 pg_uuid_t value_token;
548 char *str;
549
550 if (PG_ARGISNULL(0))
551 PG_RETURN_NULL();
552 token = semimod_token_argument(fcinfo);
553 str = varchar_of_argument(fcinfo, 0);
554 value_token = value_gate("value", str, str);
555 return semimod_gate(&value_token, token);
556}
557
558PG_FUNCTION_INFO_V1(provenance_semimod_nullable);
559/**
560 * @brief @c provenance_semimod for the aggregates that see their NULL inputs
561 * (@c array_agg, @c json_agg, ...): a NULL value gives a gate over the
562 * constant value gate @c gate_null().
563 */
564Datum provenance_semimod_nullable(PG_FUNCTION_ARGS) {
565 const pg_uuid_t *token = semimod_token_argument(fcinfo);
566 pg_uuid_t value_token;
567
568 if (PG_ARGISNULL(0))
569 value_token = value_gate("null", "", "NULL");
570 else {
571 char *str = varchar_of_argument(fcinfo, 0);
572 value_token = value_gate("value", str, str);
573 }
574 return semimod_gate(&value_token, token);
575}
576
577static bool same_token(const pg_uuid_t *a, const pg_uuid_t *b);
578
579PG_FUNCTION_INFO_V1(provenance_semimod_nested);
580/**
581 * @brief The semimodule gate of a row whose value is itself an aggregate
582 * result: @c semimod(the inner aggregate's gate, @p token).
583 *
584 * The contribution of an outer aggregate that reads an aggregate result of
585 * another kind (an avg of a count, a max of a sum, an aggregate of an
586 * arithmetic expression over aggregates). Such a value is not one value of
587 * the database but one per possible world, so the M side of the semimod is
588 * the inner aggregate's own gate rather than a @c gate_value: an evaluator
589 * that reads a value per world (the sampler) resolves it, and the closed
590 * forms, which read the M side as a constant, decline it.
591 */
592Datum provenance_semimod_nested(PG_FUNCTION_ARGS) {
593 const pg_uuid_t *token;
594 agg_token *aggtok;
595 pg_uuid_t inner;
596
597 if (PG_ARGISNULL(0))
598 PG_RETURN_NULL();
599 token = semimod_token_argument(fcinfo);
600 aggtok = (agg_token *)PG_GETARG_POINTER(0);
601 inner = *DatumGetUUIDP(DirectFunctionCall1(uuid_in,
602 CStringGetDatum(aggtok->tok)));
603 return semimod_gate(&inner, token);
604}
605
606PG_FUNCTION_INFO_V1(provenance_semimod_flat);
607/**
608 * @brief The contributions @c semimod(v_i, token ⊗ k_i) of the aggregate
609 * result @c val, whose gate aggregates the contributions
610 * @c semimod(v_i, k_i), for an aggregate of the same kind over it.
611 *
612 * @c token ⊗ (⊕ k_i ⊗ v_i) = ⊕ (token ⊗ k_i) ⊗ v_i: the outer aggregate of
613 * the rows of the groups, in every semiring. An empty array for a NULL
614 * value, which the outer aggregate skips.
615 */
616Datum provenance_semimod_flat(PG_FUNCTION_ARGS) {
617 const pg_uuid_t *token = semimod_token_argument(fcinfo);
618 agg_token *aggtok;
619 pg_uuid_t agg, *children = NULL, *out;
620 unsigned n = 0, i;
621 gate_type type;
622
623 if (PG_ARGISNULL(0))
624 PG_RETURN_ARRAYTYPE_P(construct_empty_array(UUIDOID));
625 aggtok = (agg_token *)PG_GETARG_POINTER(0);
626 agg = *DatumGetUUIDP(DirectFunctionCall1(uuid_in,
627 CStringGetDatum(aggtok->tok)));
628 type = provsql_fetch_gate(&agg, &n, &children);
629 if (type != gate_agg)
630 provsql_error("provenance_semimod_flat: not the result of an aggregate");
631
632 out = (pg_uuid_t *)palloc(sizeof(pg_uuid_t) * (n > 0 ? n : 1));
633 for (i = 0; i < n; ++i) {
634 unsigned m = 0;
635 pg_uuid_t *wires = NULL, scaled, pair[2];
636
637 if (provsql_fetch_gate(&children[i], &m, &wires) != gate_semimod || m != 2)
638 provsql_error("provenance_semimod_flat: unexpected contribution");
639 pair[0] = *token;
640 pair[1] = wires[0];
641 if (same_token(token, address_of_one()))
642 scaled = wires[0];
643 else if (same_token(&wires[0], address_of_one()))
644 scaled = *token;
645 else
646 scaled = nary_gate(gate_times, "times", "times-canonical", pair, 2);
647 out[i] = *DatumGetUUIDP(semimod_gate(&wires[1], &scaled));
648 free(wires);
649 }
650 free(children);
651 {
652 Datum *elems = (Datum *)palloc(sizeof(Datum) * (n > 0 ? n : 1));
653 for (i = 0; i < n; ++i)
654 elems[i] = UUIDPGetDatum(&out[i]);
655 PG_RETURN_ARRAYTYPE_P(construct_array(elems, (int)n, UUIDOID, UUID_LEN,
656 false, 'c'));
657 }
658}
659
660PG_FUNCTION_INFO_V1(provenance_aggregate);
661/**
662 * @brief The @c agg gate of a group, paired with the aggregate's value.
663 *
664 * The address hashes everything the gate records: the aggregate function
665 * and the scalar flag (info1 and the high bit of info2), the children, then
666 * the result type (the low bits of info2) and the value text (the extra),
667 * after a colon each. A gate's infos and text are written once, so what is
668 * written has to follow from the address: @c SUM(x) and @c AVG(x) over the
669 * same children, a scalar and a grouped aggregate, @c array_agg over
670 * integers and over their texts (same children, another result type), or a
671 * floating-point sum whose rounding depends on the plan, are all different
672 * gates. NULL children are rows whose value was NULL; they are dropped.
673 * No child gives 𝟘 for a group, and an agg gate without children for a
674 * scalar aggregation, whose value over no row is still defined.
675 */
676Datum provenance_aggregate(PG_FUNCTION_ARGS) {
677 int32 aggfnoid, aggtype;
678 bool is_scalar = !PG_ARGISNULL(4) && PG_GETARG_BOOL(4);
679 pg_uuid_t *tokens = NULL, agg, nothing;
680 char *val = NULL;
681 int n = 0;
682 agg_token *result;
683
684 if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
685 provsql_error("provenance_aggregate: the aggregate function and its "
686 "result type must not be NULL");
687 aggfnoid = PG_GETARG_INT32(0);
688 aggtype = PG_GETARG_INT32(1);
689 if (!PG_ARGISNULL(2))
690 val = varchar_of_argument(fcinfo, 2);
691 if (!PG_ARGISNULL(3)) {
692 memset(&nothing, 0xFF, sizeof(nothing));
693 n = filtered_tokens(PG_GETARG_ARRAYTYPE_P(3), &nothing, &tokens);
694 }
695
696 /* A group without rows is no group; a scalar aggregation over no row has a
697 * value (0 for a count), that of an agg gate without children. */
698 if (n == 0 && !is_scalar)
699 agg = *address_of_zero();
700 else {
701 StringInfoData buf;
702
703 name_begin(&buf, "agg");
704 appendStringInfo(&buf, "%d", aggfnoid);
705 name_add_uuid_array(&buf, tokens, n);
706 if (is_scalar)
707 appendStringInfoChar(&buf, 'S');
708 appendStringInfo(&buf, ":%d", aggtype);
709 if (val != NULL)
710 appendStringInfo(&buf, ":%s", val);
711 agg = name_end(&buf);
712 provsql_internal_create_gate_with(&agg, gate_agg, (unsigned)n, tokens,
713 true, (unsigned)aggfnoid,
714 is_scalar ? ((unsigned)aggtype | 0x80000000u)
715 : (unsigned)aggtype,
716 val);
717 }
718
719 /* A NULL value over no row at all is SQL's NULL: there is nothing to
720 * keep. Over rows absent from the database as it is (the displayed value
721 * reads only the present ones), the group exists in other worlds: the
722 * token keeps its circuit, with a NULL value (an empty val). */
723 if (val == NULL && n == 0)
724 PG_RETURN_NULL();
725 result = (agg_token *)palloc0(sizeof(agg_token));
726 {
727 StringInfoData t;
728 initStringInfo(&t);
729 name_add_uuid(&t, &agg);
730 memcpy(result->tok, t.data, 36);
731 pfree(t.data);
732 }
733 if (val != NULL)
734 agg_token_set_value(result, val, strlen(val));
735 PG_RETURN_POINTER(result);
736}
737
738static bool same_token(const pg_uuid_t *a, const pg_uuid_t *b) {
739 return memcmp(a->data, b->data, UUID_LEN) == 0;
740}
741
742PG_FUNCTION_INFO_V1(provenance_monus);
743/**
744 * @brief @c token1 ⊖ @c token2.
745 *
746 * A NULL second argument is the row without a match in the outer join of the
747 * difference: nothing to subtract, and X ⊖ 𝟘 = X. (Not the NULL ≡ 𝟙 of
748 * @c provenance_times: each combinator reads NULL as its own neutral.)
749 * X ⊖ X = 𝟘 and 𝟘 ⊖ X = 𝟘 are applied as well.
750 */
751Datum provenance_monus(PG_FUNCTION_ARGS) {
752 const pg_uuid_t *token1, *token2;
753 StringInfoData buf;
754 pg_uuid_t monus, children[2];
755
756 if (PG_ARGISNULL(0))
757 ereport(ERROR,
758 (errmsg("provenance_monus is called with first argument NULL")));
759 token1 = PG_GETARG_UUID_P(0);
760 if (PG_ARGISNULL(1))
761 return uuid_result(token1);
762 token2 = PG_GETARG_UUID_P(1);
763
764 if (same_token(token1, token2) || same_token(token1, address_of_zero()))
766 if (same_token(token2, address_of_zero()))
767 return uuid_result(token1);
768
769 name_begin(&buf, "monus");
770 name_add_uuid(&buf, token1);
771 name_add_uuid(&buf, token2);
772 monus = name_end(&buf);
773 children[0] = *token1;
774 children[1] = *token2;
775 provsql_internal_create_gate(&monus, gate_monus, 2, children);
776 return uuid_result(&monus);
777}
778
779PG_FUNCTION_INFO_V1(provenance_delta);
780/**
781 * @brief δ(@c token). A NULL token is an untracked source, 𝟙, and δ(𝟙) = 𝟙;
782 * δ(𝟘) = 𝟘.
783 */
784Datum provenance_delta(PG_FUNCTION_ARGS) {
785 const pg_uuid_t *token;
786 StringInfoData buf;
787 pg_uuid_t delta;
788
789 if (PG_ARGISNULL(0))
790 return uuid_result(address_of_one());
791 token = PG_GETARG_UUID_P(0);
792 if (same_token(token, address_of_zero()) || same_token(token, address_of_one()))
793 return uuid_result(token);
794
795 name_begin(&buf, "delta");
796 name_add_uuid(&buf, token);
797 delta = name_end(&buf);
798 provsql_internal_create_gate(&delta, gate_delta, 1, token);
799 return uuid_result(&delta);
800}
801
802PG_FUNCTION_INFO_V1(provenance_cmp);
803/**
804 * @brief The comparison gate @c left @c op @c right of a HAVING condition.
805 *
806 * A comparison with a NULL operand is unknown in every possible world: the
807 * row is annotated 𝟘. Hence not strict: a NULL result would read as the
808 * neutral of ⊗ and turn "unknown" into "certainly true".
809 */
810Datum provenance_cmp(PG_FUNCTION_ARGS) {
811 const pg_uuid_t *left, *right;
812 Oid op;
813 StringInfoData buf;
814 pg_uuid_t cmp, children[2];
815
816 if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2))
818 left = PG_GETARG_UUID_P(0);
819 op = PG_GETARG_OID(1);
820 right = PG_GETARG_UUID_P(2);
821
822 name_begin(&buf, "cmp");
823 name_add_uuid(&buf, left);
824 appendStringInfo(&buf, "%u", op);
825 name_add_uuid(&buf, right);
826 cmp = name_end(&buf);
827 children[0] = *left;
828 children[1] = *right;
830 true, (unsigned)op, 0, NULL);
831 return uuid_result(&cmp);
832}
833
834PG_FUNCTION_INFO_V1(annotate);
835/**
836 * @brief A transparent @c annotation gate over @c token, carrying @c extra.
837 *
838 * The address hashes @c extra along with the child: two annotations of one
839 * token with different texts are different gates. NULL on a NULL token.
840 */
841Datum annotate(PG_FUNCTION_ARGS) {
842 const pg_uuid_t *token;
843 char *extra = NULL;
844 StringInfoData buf;
845 pg_uuid_t annotated;
846
847 if (PG_ARGISNULL(0))
848 PG_RETURN_NULL();
849 token = PG_GETARG_UUID_P(0);
850 if (!PG_ARGISNULL(1))
851 extra = text_to_cstring(PG_GETARG_TEXT_PP(1));
852
853 name_begin(&buf, "annotation");
854 name_add_uuid(&buf, token);
855 if (extra != NULL)
856 appendStringInfoString(&buf, extra);
857 annotated = name_end(&buf);
859 false, 0, 0, extra);
860 return uuid_result(&annotated);
861}
862
863PG_FUNCTION_INFO_V1(inversion_free_key);
864/**
865 * @brief The order key of an input on the inversion-free route,
866 * @c "K<factor> <bytes of root>:<root><bytes of sec>:<sec>". Strict.
867 */
868Datum inversion_free_key(PG_FUNCTION_ARGS) {
869 text *root = PG_GETARG_TEXT_PP(0), *sec = PG_GETARG_TEXT_PP(1);
870 StringInfoData buf;
871
872 initStringInfo(&buf);
873 appendStringInfo(&buf, "K%d %d:", PG_GETARG_INT32(2),
874 (int)VARSIZE_ANY_EXHDR(root));
875 appendBinaryStringInfo(&buf, VARDATA_ANY(root), VARSIZE_ANY_EXHDR(root));
876 appendStringInfo(&buf, "%d:", (int)VARSIZE_ANY_EXHDR(sec));
877 appendBinaryStringInfo(&buf, VARDATA_ANY(sec), VARSIZE_ANY_EXHDR(sec));
878 PG_RETURN_TEXT_P(cstring_to_text_with_len(buf.data, buf.len));
879}
880
881/* -------------------------------------------------------------------------
882 * The other content-addressed builders: assumption wrappers, where-provenance
883 * gates, arithmetic. Each records what its address hashes, so the gate goes
884 * with its infos or text in one unanswered message.
885 * ------------------------------------------------------------------------- */
886
887/** @brief The @c assumed wrapper of @p token under @p assumption, with
888 * @p info1 as the route tag (0 for none). */
889static pg_uuid_t assumed_gate(const pg_uuid_t *token, const char *assumption,
890 unsigned info1) {
891 StringInfoData buf;
892 pg_uuid_t wrapped;
893
894 if (strcmp(assumption, "boolean") != 0 && strcmp(assumption, "absorptive") != 0)
895 ereport(ERROR, (errmsg("provenance_assume: unknown assumption %s", assumption)));
896 name_begin(&buf, "assumed");
897 appendStringInfoString(&buf, assumption);
898 name_add_uuid(&buf, token);
899 wrapped = name_end(&buf);
901 info1 != 0, info1, 0, assumption);
902 return wrapped;
903}
904
905PG_FUNCTION_INFO_V1(provenance_assume);
906/** @brief Wrap @c token in the assumption marker @c assumption
907 * (@c 'boolean' or @c 'absorptive'). NULL on a NULL token. */
908Datum provenance_assume(PG_FUNCTION_ARGS) {
909 pg_uuid_t wrapped;
910
911 if (PG_ARGISNULL(0))
912 PG_RETURN_NULL();
913 if (PG_ARGISNULL(1))
914 ereport(ERROR, (errmsg("provenance_assume: unknown assumption NULL")));
915 wrapped = assumed_gate(PG_GETARG_UUID_P(0),
916 text_to_cstring(PG_GETARG_TEXT_PP(1)), 0);
917 return uuid_result(&wrapped);
918}
919
920PG_FUNCTION_INFO_V1(assume_boolean);
921/** @brief The Boolean-assumption wrapper the safe-query rewriter puts on
922 * every per-row root, tagged @c PROVSQL_ROUTE_SQ_REWRITE. */
923Datum assume_boolean(PG_FUNCTION_ARGS) {
924 pg_uuid_t wrapped;
925
926 if (PG_ARGISNULL(0))
927 PG_RETURN_NULL();
928 wrapped = assumed_gate(PG_GETARG_UUID_P(0), "boolean",
930 return uuid_result(&wrapped);
931}
932
933PG_FUNCTION_INFO_V1(provenance_project);
934/**
935 * @brief The where-provenance @c project gate of @c token: its text lists,
936 * for each output position, the input position it comes from (NULL
937 * for a position given as 0), as @c '{{in,out},...}'.
938 */
939Datum provenance_project(PG_FUNCTION_ARGS) {
940 const pg_uuid_t *token;
941 ArrayType *positions;
942 Datum *elems;
943 bool *nulls;
944 int n, i;
945 StringInfoData buf, extra;
946 pg_uuid_t project;
947
948 if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
949 provsql_error("provenance_project: NULL argument");
950 token = PG_GETARG_UUID_P(0);
951 positions = PG_GETARG_ARRAYTYPE_P(1);
952 deconstruct_array(positions, INT4OID, sizeof(int32), true, 'i',
953 &elems, &nulls, &n);
954
955 /* The address hashes the array as its text form, '{p1,p2,...}'. */
956 name_begin(&buf, "project");
957 name_add_uuid(&buf, token);
958 appendStringInfoChar(&buf, '{');
959 initStringInfo(&extra);
960 appendStringInfoChar(&extra, '{');
961 for (i = 0; i < n; ++i) {
962 if (i > 0) {
963 appendStringInfoChar(&buf, ',');
964 appendStringInfoChar(&extra, ',');
965 }
966 if (nulls[i]) {
967 appendStringInfoString(&buf, "NULL");
968 appendStringInfo(&extra, "{NULL,%d}", i + 1);
969 } else {
970 int32 pos = DatumGetInt32(elems[i]);
971 appendStringInfo(&buf, "%d", pos);
972 if (pos == 0)
973 appendStringInfo(&extra, "{NULL,%d}", i + 1);
974 else
975 appendStringInfo(&extra, "{%d,%d}", pos, i + 1);
976 }
977 }
978 appendStringInfoChar(&buf, '}');
979 appendStringInfoChar(&extra, '}');
980 project = name_end(&buf);
982 false, 0, 0, n > 0 ? extra.data : NULL);
983 return uuid_result(&project);
984}
985
986PG_FUNCTION_INFO_V1(provenance_eq);
987/** @brief The where-provenance @c eq gate of @c token, equating positions
988 * @c pos1 and @c pos2 (its infos). */
989Datum provenance_eq(PG_FUNCTION_ARGS) {
990 const pg_uuid_t *token;
991 StringInfoData buf;
992 pg_uuid_t eq;
993 int32 pos1 = PG_ARGISNULL(1) ? 0 : PG_GETARG_INT32(1);
994 int32 pos2 = PG_ARGISNULL(2) ? 0 : PG_GETARG_INT32(2);
995
996 if (PG_ARGISNULL(0))
997 provsql_error("provenance_eq: NULL token");
998 token = PG_GETARG_UUID_P(0);
999 name_begin(&buf, "eq");
1000 name_add_uuid(&buf, token);
1001 if (!PG_ARGISNULL(1))
1002 appendStringInfo(&buf, "%d", pos1);
1003 appendStringInfoChar(&buf, ',');
1004 if (!PG_ARGISNULL(2))
1005 appendStringInfo(&buf, "%d", pos2);
1006 eq = name_end(&buf);
1008 true, (unsigned)pos1, (unsigned)pos2, NULL);
1009 return uuid_result(&eq);
1010}
1011
1012PG_FUNCTION_INFO_V1(provenance_arith);
1013/** @brief The @c arith gate applying operator @c op (a @c provsql_arith_op,
1014 * its info1) to @c children, in order. */
1015Datum provenance_arith(PG_FUNCTION_ARGS) {
1016 StringInfoData buf;
1017 pg_uuid_t arith, *children = NULL, nothing;
1018 int n = 0;
1019 int32 op = PG_ARGISNULL(0) ? 0 : PG_GETARG_INT32(0);
1020
1021 name_begin(&buf, "arith");
1022 if (!PG_ARGISNULL(0))
1023 appendStringInfo(&buf, "%d", op);
1024 if (!PG_ARGISNULL(1)) {
1025 ArrayType *arr = PG_GETARG_ARRAYTYPE_P(1);
1026 if (array_contains_nulls(arr))
1027 provsql_error("provenance_arith: children array must not contain NULL elements");
1028 memset(&nothing, 0xFF, sizeof(nothing));
1029 n = filtered_tokens(arr, &nothing, &children);
1030 name_add_uuid_array(&buf, children, n);
1031 }
1032 arith = name_end(&buf);
1033 provsql_internal_create_gate_with(&arith, gate_arith, (unsigned)n, children,
1034 true, (unsigned)op, 0, NULL);
1035 return uuid_result(&arith);
1036}
void provsql_sha1(const unsigned char *data, size_t len, unsigned char out[20])
SHA-1 of len bytes of data (RFC 3174).
void agg_token_set_value(agg_token *aggtok, const char *val, size_t len)
Set the value of aggtok to the len first bytes of val.
Definition agg_token.c:36
Aggregate-provenance token type used in SQL aggregate functions.
Datum provenance_monus(PG_FUNCTION_ARGS)
token1 ⊖ token2.
Datum provenance_semimod_flat(PG_FUNCTION_ARGS)
The contributions semimod(v_i, token ⊗ k_i) of the aggregate result val, whose gate aggregates the co...
Datum provenance_semimod_nested(PG_FUNCTION_ARGS)
The semimodule gate of a row whose value is itself an aggregate result: semimod(the inner aggregate's...
Datum provenance_times(PG_FUNCTION_ARGS)
⊗ of a list of tokens.
static void name_begin(StringInfo buf, const char *prefix)
Start the name of a gate address.
Datum provenance_arith(PG_FUNCTION_ARGS)
The arith gate applying operator op (a provsql_arith_op, its info1) to children, in order.
static HTAB * written_values
Datum provenance_eq(PG_FUNCTION_ARGS)
The where-provenance eq gate of token, equating positions pos1 and pos2 (its infos).
static pg_uuid_t value_gate(const char *prefix, const char *suffix, const char *extra)
The value gate of the text str, at the address named name ("value" followed by str,...
Datum inversion_free_key(PG_FUNCTION_ARGS)
The order key of an input on the inversion-free route, "K<factor> <bytes of root>:<root><bytes of sec...
pg_uuid_t provsql_plant_canonical(const char *work_name, gate_type type, const pg_uuid_t *children, int n, const pg_uuid_t *target, unsigned info1, unsigned info2)
Pre-create ("plant") a gate at the canonical address of a multiset of tokens, for the working table o...
static const pg_uuid_t * address_of_zero(void)
static pg_uuid_t nary_gate(gate_type type, const char *plain_prefix, const char *canonical_prefix, const pg_uuid_t *children, int n)
A gate of type type over children, unless this backend planted one at the canonical address of their ...
Datum annotate(PG_FUNCTION_ARGS)
A transparent annotation gate over token, carrying extra.
static const pg_uuid_t * address_of_one(void)
static pg_uuid_t name_end(StringInfo buf)
The version-5 UUID of the name accumulated in buf.
static pg_uuid_t constant_uuid(const char *text)
The UUID text (one of the constants of provsql_utils.h).
static pg_uuid_t assumed_gate(const pg_uuid_t *token, const char *assumption, unsigned info1)
The assumed wrapper of token under assumption, with info1 as the route tag (0 for none).
Datum provenance_aggregate(PG_FUNCTION_ARGS)
The agg gate of a group, paired with the aggregate's value.
static Datum semimod_gate(const pg_uuid_t *value_token, const pg_uuid_t *token)
The semimod gate value_token ⊗ token.
static void name_add_uuid_array(StringInfo buf, const pg_uuid_t *u, int n)
Append the text form of a uuid[], "{u1,u2,...}".
Datum provenance_plus(PG_FUNCTION_ARGS)
⊕ of an array of tokens.
static PlantedScope * planted_current
Datum planted_scope(PG_FUNCTION_ARGS)
SQL entry point of planted_open_scope(), called by the drivers of recursive CTEs right after they (re...
void provsql_gate_builders_forget(void)
Forget what gate_builders.c remembers of the store (planted gates, value gates written).
Datum provenance_cmp(PG_FUNCTION_ARGS)
The comparison gate left op right of a HAVING condition.
static HTAB * planted
static int token_cmp(const void *a, const void *b)
Datum provenance_assume(PG_FUNCTION_ARGS)
Wrap token in the assumption marker assumption ('boolean' or 'absorptive').
static int filtered_tokens(ArrayType *arr, const pg_uuid_t *neutral, pg_uuid_t **out)
The elements of a uuid[] that are neither NULL nor neutral, in order.
#define WRITTEN_VALUES_MAX
static pg_uuid_t canonical_address(const char *canonical_prefix, const pg_uuid_t *children, int n)
The canonical address, "<prefix>{sorted children}".
static bool is_planted(const pg_uuid_t *address)
Datum provenance_semimod(PG_FUNCTION_ARGS)
The semimodule gate val ⊗ token of an aggregated row.
static Oid temp_table_oid(const char *name)
OID of the temporary table name of this backend, if any.
static int planted_next_id
Datum assume_boolean(PG_FUNCTION_ARGS)
The Boolean-assumption wrapper the safe-query rewriter puts on every per-row root,...
static void planted_forget(PlantedScope *scope)
Datum provenance_project(PG_FUNCTION_ARGS)
The where-provenance project gate of token: its text lists, for each output position,...
Datum plant_canonical(PG_FUNCTION_ARGS)
SQL entry point of provsql_plant_canonical().
static Datum uuid_result(const pg_uuid_t *u)
static const unsigned char provsql_ns[UUID_LEN]
uuid_ns_provsql(), the namespace of every gate address.
Datum provenance_delta(PG_FUNCTION_ARGS)
δ(token).
static const pg_uuid_t * semimod_token_argument(FunctionCallInfo fcinfo)
static PlantedScope * planted_open_scope(const char *name, bool reset)
Open the scope of the working table name, just (re)created: forget what was planted for the table it ...
static PlantedScope * planted_scopes
static char * varchar_of_argument(FunctionCallInfo fcinfo, int argno)
CAST(val AS varchar) of argument argno, as a C string.
static bool same_token(const pg_uuid_t *a, const pg_uuid_t *b)
static void name_add_uuid(StringInfo buf, const pg_uuid_t *u)
Append a UUID in the text form of uuid_out.
Datum provenance_semimod_nullable(PG_FUNCTION_ARGS)
provenance_semimod for the aggregates that see their NULL inputs (array_agg, json_agg,...
#define provsql_error(fmt,...)
Report a fatal ProvSQL error and abort the current transaction.
void provsql_internal_create_gate(const pg_uuid_t *token, gate_type type, unsigned nb_children, const pg_uuid_t *children_data)
Internal entry point behind create_gate(): cache + worker IPC.
void provsql_internal_create_gate_with(const pg_uuid_t *token, gate_type type, unsigned nb_children, const pg_uuid_t *children, bool has_infos, unsigned info1, unsigned info2, const char *extra)
Create a gate together with its infos and its text, in one message that is not answered.
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.
Core types, constants, and utilities shared across ProvSQL.
#define PROVSQL_GATE_ZERO_UUID
UUID of the semiring zero gate: the result of the SQL function gate_zero(), created with the extensio...
#define PROVSQL_GATE_ONE_UUID
UUID of the semiring one gate, the result of gate_one().
@ gate_annotation
Transparent single-child wrapper carrying a query-level annotation in extra (inversion-free certifica...
@ gate_arith
n-ary arithmetic gate over scalar-valued children (info1 holds operator tag)
@ gate_assumed
Structural marker over a single child whose sub-circuit was computed under a Boolean-provenance assum...
@ PROVSQL_ROUTE_SQ_REWRITE
Hierarchical-CQ read-once rewrite (src/safe_query.c).
pg_uuid_t address
Hash key.
A working table with planted gates.
char name[NAMEDATALEN]
struct PlantedScope * next
Oid relid
The table, as created by the lowering.
CoercionPathType path
Aggregate token bundling a provenance UUID with a running value.
Definition agg_token.h:32
char tok[2 *UUID_LEN+5]
Provenance UUID as a text string.
Definition agg_token.h:33