ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
provsql_utils.c
Go to the documentation of this file.
1/**
2 * @file provsql_utils.c
3 * @brief OID lookup, constants cache, and utility functions for ProvSQL.
4 *
5 * Implements the functions declared in @c provsql_utils.h:
6 * - @c get_constants(): retrieves and caches per-database OIDs for all
7 * ProvSQL types, functions, and operators.
8 * - @c find_equality_operator(): looks up the @c = operator OID for a
9 * given pair of types.
10 *
11 * The constants cache is a sorted, dynamically-grown array of
12 * @c database_constants_t records (one per PostgreSQL database OID)
13 * stored in process-local memory and searched with binary search.
14 * The @c reset_constants_cache() SQL function forces a cache invalidation
15 * for the current database, which is needed after @c ALTER EXTENSION.
16 *
17 * Several helper functions (@c get_func_oid, @c get_provsql_func_oid,
18 * @c OperatorGet, @c get_enum_oid, @c binary_oper_exact) are adapted
19 * from PostgreSQL source code that is not exported as a public API.
20 */
21#include "postgres.h"
22#include "access/htup_details.h"
23#if PG_VERSION_NUM >= 120000
24#include "access/table.h" /* table_open / table_close */
25#else
26#include "access/heapam.h" /* heap_open / heap_close (PG <12) */
27#define table_open(r, l) heap_open((r), (l))
28#define table_close(r, l) heap_close((r), (l))
29#endif
30#include "access/genam.h"
31#include "miscadmin.h"
32#include "catalog/indexing.h" /* ConstraintRelidTypidNameIndexId */
33#include "catalog/namespace.h"
34#include "catalog/pg_attribute.h"
35#include "catalog/pg_constraint.h"
36#include "catalog/pg_index.h"
37#include "catalog/pg_type.h"
38#include "catalog/pg_enum.h"
39#include "catalog/pg_namespace.h"
40#include "catalog/pg_operator.h"
41#include "catalog/pg_type.h"
42#include "fmgr.h"
43#include "nodes/value.h"
44#include "parser/parse_func.h"
45#include "utils/fmgroids.h"
46#include "utils/syscache.h"
47#include "utils/lsyscache.h"
48#include "utils/inval.h"
49
50#include <string.h>
51
52#include "provsql_utils.h"
53
54const char *gate_type_name[] = {
55 "input",
56 "plus",
57 "times",
58 "monus",
59 "project",
60 "zero",
61 "one",
62 "eq",
63 "agg",
64 "semimod",
65 "cmp",
66 "delta",
67 "value",
68 "mulinput",
69 "update",
70 "rv",
71 "arith",
72 "mixture",
73 "assumed",
74 "annotation",
75 "conditioned",
76 "mobius",
77 "invalid"
78};
79
80/**
81 * @brief Look up an exactly matching binary operator OID.
82 *
83 * Copied and adapted from @c parse_oper.c (PostgreSQL internals, not
84 * exported). Returns @c InvalidOid if no exact match exists.
85 *
86 * @param opname Qualified operator name (a @c List of @c String nodes).
87 * @param arg1 OID of the left operand type.
88 * @param arg2 OID of the right operand type.
89 * @return OID of the matching operator, or @c InvalidOid.
90 */
91static Oid
92binary_oper_exact(List *opname, Oid arg1, Oid arg2)
93{
94 Oid result;
95 bool was_unknown = false;
96
97 /* Unspecified type for one of the arguments? then use the other */
98 if ((arg1 == UNKNOWNOID) && (arg2 != InvalidOid))
99 {
100 arg1 = arg2;
101 was_unknown = true;
102 }
103 else if ((arg2 == UNKNOWNOID) && (arg1 != InvalidOid))
104 {
105 arg2 = arg1;
106 was_unknown = true;
107 }
108
109 result = OpernameGetOprid(opname, arg1, arg2);
110 if (OidIsValid(result))
111 return result;
112
113 if (was_unknown)
114 {
115 /* arg1 and arg2 are the same here, need only look at arg1 */
116 Oid basetype = getBaseType(arg1);
117
118 if (basetype != arg1)
119 {
120 result = OpernameGetOprid(opname, basetype, basetype);
121 if (OidIsValid(result))
122 return result;
123 }
124 }
125
126 return InvalidOid;
127}
128
129/* Adapted from PostgreSQL code that is not exported (see parse_oper.c
130 * and the static function oper_select_candidate therein).
131 */
132Oid find_equality_operator(Oid ltypeId, Oid rtypeId)
133{
134 List * const equals=list_make1(makeString("="));
135
136 FuncCandidateList clist;
137 Oid inputOids[2] = {ltypeId,rtypeId};
138 int ncandidates;
139
140 Oid result = binary_oper_exact(equals, ltypeId, rtypeId);
141
142 if(result!=InvalidOid)
143 return result;
144
145 clist = OpernameGetCandidates(equals, 'b', false);
146
147 ncandidates = func_match_argtypes(2, inputOids,
148 clist, &clist);
149
150 if (ncandidates == 0)
151 return InvalidOid;
152 else if (ncandidates == 1)
153 return clist->oid;
154
155 clist = func_select_candidate(2, inputOids, clist);
156
157 if(clist)
158 return clist->oid;
159 else
160 return InvalidOid;
161}
162
163/**
164 * @brief Return the OID of a globally qualified function named @p s.
165 *
166 * Looks up the function in the default search path. Returns 0 if no
167 * matching function is found.
168 *
169 * @param s Function name (unqualified).
170 * @return OID of the function, or 0 if not found.
171 */
172static Oid get_func_oid(char *s)
173{
174 FuncCandidateList fcl=FuncnameGetCandidates(
175 list_make1(makeString(s)),
176 -1,
177 NIL,
178 false,
179 false,
180#if PG_VERSION_NUM >= 140000
181 false,
182#endif
183 false);
184 if(fcl)
185 return fcl->oid;
186 else
187 return 0;
188}
189
190/**
191 * @brief Return the OID of a @c provsql-schema function named @p s.
192 *
193 * Looks up the function in the @c provsql schema. Returns 0 if not found.
194 *
195 * @param s Function name (without schema prefix).
196 * @return OID of the function, or 0 if not found.
197 */
198static Oid get_provsql_func_oid(char *s)
199{
200 FuncCandidateList fcl=FuncnameGetCandidates(
201 list_make2(makeString("provsql"),makeString(s)),
202 -1,
203 NIL,
204 false,
205 false,
206#if PG_VERSION_NUM >= 140000
207 false,
208#endif
209 false);
210 if(fcl)
211 return fcl->oid;
212 else
213 return 0;
214}
215
216/**
217 * @brief Retrieve operator and function OIDs for a named operator.
218 *
219 * Copied and adapted from @c pg_operator.c (PostgreSQL internals, not
220 * exported). Looks up the operator by name, namespace, and operand types
221 * in the system cache.
222 *
223 * @param operatorName Operator symbol string (e.g. @c "<>").
224 * @param operatorNamespace OID of the schema containing the operator.
225 * @param leftObjectId OID of the left operand type.
226 * @param rightObjectId OID of the right operand type.
227 * @param operatorObjectId Output: OID of the operator, or 0 if not found.
228 * @param functionObjectId Output: OID of the underlying function, or 0.
229 */
230static void OperatorGet(
231 const char *operatorName,
232 Oid operatorNamespace,
233 Oid leftObjectId,
234 Oid rightObjectId,
235 Oid *operatorObjectId,
236 Oid *functionObjectId)
237{
238 HeapTuple tup;
239 bool defined;
240
241 tup = SearchSysCache4(OPERNAMENSP,
242 PointerGetDatum(operatorName),
243 ObjectIdGetDatum(leftObjectId),
244 ObjectIdGetDatum(rightObjectId),
245 ObjectIdGetDatum(operatorNamespace));
246 if (HeapTupleIsValid(tup))
247 {
248 Form_pg_operator oprform = (Form_pg_operator) GETSTRUCT(tup);
249#if PG_VERSION_NUM >= 120000
250 *operatorObjectId = oprform->oid;
251#else
252 *operatorObjectId = HeapTupleGetOid(tup);
253#endif
254 *functionObjectId = oprform->oprcode;
255 defined = RegProcedureIsValid(oprform->oprcode);
256 ReleaseSysCache(tup);
257 }
258 else
259 {
260 defined = false;
261 }
262
263 if(!defined) {
264 *operatorObjectId = 0;
265 *functionObjectId = 0;
266 }
267}
268
269/**
270 * @brief Return the OID of a specific enum label within an enum type.
271 *
272 * @param enumtypoid OID of the enum type (e.g. @c provenance_gate).
273 * @param label C-string label of the enum value to look up.
274 * @return OID of the enum label's @c pg_enum row, or
275 * @c InvalidOid if the label is not present.
276 */
277static Oid get_enum_oid(Oid enumtypoid, const char *label)
278{
279 HeapTuple tup;
280 Oid ret;
281
282 tup = SearchSysCache2(ENUMTYPOIDNAME,
283 ObjectIdGetDatum(enumtypoid),
284 CStringGetDatum(label));
285 if (!HeapTupleIsValid(tup))
286 return InvalidOid;
287
288#if PG_VERSION_NUM >= 120000
289 ret = ((Form_pg_enum) GETSTRUCT(tup))->oid;
290#else
291 ret = HeapTupleGetOid(tup);
292#endif
293
294 ReleaseSysCache(tup);
295
296 return ret;
297}
298
299/**
300 * @brief Query the system catalogs to populate a fresh @c constants_t.
301 *
302 * Performs all OID lookups required by ProvSQL in a single pass through
303 * the system caches. The @c CheckOid() macro aborts (or returns early,
304 * depending on @p failure_if_not_possible) if any OID resolves to
305 * @c InvalidOid.
306 *
307 * @param failure_if_not_possible If @c true, raise a @c provsql_error when
308 * any OID cannot be resolved. If @c false, return a @c constants_t
309 * with @c ok==false instead.
310 * @return Fully populated @c constants_t on success, or @c ok==false on
311 * failure when @p failure_if_not_possible is @c false.
312 */
313static constants_t initialize_constants(bool failure_if_not_possible)
314{
315 constants_t constants;
316 constants.ok = false;
317
318 /** @brief Abort or return early if OID field @p o of @p constants is invalid. */
319 #define CheckOid(o) if(constants.o==InvalidOid) { \
320 if(failure_if_not_possible) \
321 provsql_error("Could not initialize provsql constants"); \
322 else \
323 return constants; }
324
325 constants.OID_SCHEMA_PROVSQL = get_namespace_oid("provsql", true);
326 CheckOid(OID_SCHEMA_PROVSQL);
327
328 constants.OID_TYPE_UUID = TypenameGetTypid("uuid");
329 CheckOid(OID_TYPE_UUID);
330
331 constants.OID_TYPE_GATE_TYPE = GetSysCacheOid2(
332 TYPENAMENSP,
333#if PG_VERSION_NUM >= 120000
334 Anum_pg_type_oid,
335#endif
336 CStringGetDatum("provenance_gate"),
337 ObjectIdGetDatum(constants.OID_SCHEMA_PROVSQL)
338 );
339 CheckOid(OID_TYPE_GATE_TYPE);
340
341 constants.OID_TYPE_AGG_TOKEN = GetSysCacheOid2(
342 TYPENAMENSP,
343#if PG_VERSION_NUM >= 120000
344 Anum_pg_type_oid,
345#endif
346 CStringGetDatum("agg_token"),
347 ObjectIdGetDatum(constants.OID_SCHEMA_PROVSQL)
348 );
349 CheckOid(OID_TYPE_AGG_TOKEN);
350
351 constants.OID_TYPE_UUID = TypenameGetTypid("uuid");
352 CheckOid(OID_TYPE_UUID);
353
354 constants.OID_TYPE_UUID_ARRAY = TypenameGetTypid("_uuid");
355 CheckOid(OID_TYPE_UUID_ARRAY);
356
357 constants.OID_TYPE_INT = TypenameGetTypid("int4");
358 CheckOid(OID_TYPE_INT);
359
360 constants.OID_TYPE_BOOL = TypenameGetTypid("bool");
361 CheckOid(OID_TYPE_BOOL);
362
363 constants.OID_TYPE_FLOAT = TypenameGetTypid("float8");
364 CheckOid(OID_TYPE_FLOAT);
365
366 constants.OID_TYPE_INT_ARRAY = TypenameGetTypid("_int4");
367 CheckOid(OID_TYPE_INT_ARRAY);
368
369 constants.OID_TYPE_VARCHAR = TypenameGetTypid("varchar");
370 CheckOid(OID_TYPE_VARCHAR);
371
372#if PG_VERSION_NUM >= 140000
373 constants.OID_TYPE_TSTZMULTIRANGE = TypenameGetTypid("tstzmultirange");
374 CheckOid(OID_TYPE_TSTZMULTIRANGE);
375 constants.OID_TYPE_NUMMULTIRANGE = TypenameGetTypid("nummultirange");
376 CheckOid(OID_TYPE_NUMMULTIRANGE);
377 constants.OID_TYPE_INT4MULTIRANGE = TypenameGetTypid("int4multirange");
378 CheckOid(OID_TYPE_INT4MULTIRANGE);
379#else
380 constants.OID_TYPE_TSTZMULTIRANGE = InvalidOid;
381 constants.OID_TYPE_NUMMULTIRANGE = InvalidOid;
382 constants.OID_TYPE_INT4MULTIRANGE = InvalidOid;
383#endif
384
385 constants.OID_FUNCTION_ARRAY_AGG = get_func_oid("array_agg");
386 CheckOid(OID_FUNCTION_ARRAY_AGG);
387
388 constants.OID_FUNCTION_PROVENANCE_PLUS = get_provsql_func_oid("provenance_plus");
389 CheckOid(OID_FUNCTION_PROVENANCE_PLUS);
390
391 constants.OID_FUNCTION_PROVENANCE_TIMES = get_provsql_func_oid("provenance_times");
392 CheckOid(OID_FUNCTION_PROVENANCE_TIMES);
393
394 constants.OID_FUNCTION_PROVENANCE_MONUS = get_provsql_func_oid("provenance_monus");
395 CheckOid(OID_FUNCTION_PROVENANCE_MONUS);
396
397 constants.OID_FUNCTION_PROVENANCE_PROJECT = get_provsql_func_oid("provenance_project");
398 CheckOid(OID_FUNCTION_PROVENANCE_PROJECT);
399
400 constants.OID_FUNCTION_PROVENANCE_EQ = get_provsql_func_oid("provenance_eq");
401 CheckOid(OID_FUNCTION_PROVENANCE_EQ);
402
403 constants.OID_FUNCTION_PROVENANCE = get_provsql_func_oid("provenance");
404 CheckOid(OID_FUNCTION_PROVENANCE);
405
406 constants.OID_FUNCTION_PROVENANCE_DELTA = get_provsql_func_oid("provenance_delta");
407 CheckOid(OID_FUNCTION_PROVENANCE_DELTA);
408
409 constants.OID_FUNCTION_PROVENANCE_AGGREGATE = get_provsql_func_oid("provenance_aggregate");
410 CheckOid(OID_FUNCTION_PROVENANCE_AGGREGATE);
411
412 constants.OID_FUNCTION_PROVENANCE_SEMIMOD = get_provsql_func_oid("provenance_semimod");
413 CheckOid(OID_FUNCTION_PROVENANCE_SEMIMOD);
414
415 constants.OID_FUNCTION_GATE_ZERO = get_provsql_func_oid("gate_zero");
416 CheckOid(OID_FUNCTION_GATE_ZERO);
417
418 constants.OID_FUNCTION_GATE_ONE = get_provsql_func_oid("gate_one");
419 CheckOid(OID_FUNCTION_GATE_ONE);
420
421 constants.OID_FUNCTION_PROVENANCE_CMP = get_provsql_func_oid("provenance_cmp");
422 CheckOid(OID_FUNCTION_PROVENANCE_CMP);
423
424 constants.OID_FUNCTION_AGG_TOKEN_UUID = get_provsql_func_oid("agg_token_uuid");
425 CheckOid(OID_FUNCTION_AGG_TOKEN_UUID);
426
427 /* The next two are used by the agg_token JOIN query rewriting. */
428 constants.OID_FUNCTION_GET_CHILDREN = get_provsql_func_oid("get_children");
429 CheckOid(OID_FUNCTION_GET_CHILDREN);
430
431 constants.OID_FUNCTION_GET_EXTRA = get_provsql_func_oid("get_extra");
432 CheckOid(OID_FUNCTION_GET_EXTRA);
433
434 /* Pick the unnest(anyarray) overload explicitly; PG 14+ also has
435 * unnest(anymultirange), which get_func_oid would pick up first on
436 * some versions. */
437 {
438 Oid argtypes[1] = { ANYARRAYOID };
439 constants.OID_UNNEST = LookupFuncName(list_make1(makeString("unnest")),
440 1, argtypes, true);
441 }
442 CheckOid(OID_UNNEST);
443
444 /* random_variable type and its operator procedures will ship in
445 * 1.5.0. Older schemas (notably the 1.0.0 baseline used by
446 * extension_upgrade) do not have them. Treat each lookup as
447 * optional -- if the catalog lacks the symbol, the OID stays
448 * InvalidOid and downstream code (rv_cmp_index, the planner-hook
449 * walker) silently no-ops on such schemas because real OpExpr
450 * funcoids never equal InvalidOid. Mirrors the
451 * GET_GATE_TYPE_OID_OPTIONAL pattern above. */
452 constants.OID_TYPE_RANDOM_VARIABLE = GetSysCacheOid2(
453 TYPENAMENSP,
454#if PG_VERSION_NUM >= 120000
455 Anum_pg_type_oid,
456#endif
457 CStringGetDatum("random_variable"),
458 ObjectIdGetDatum(constants.OID_SCHEMA_PROVSQL)
459 );
460
461 /* rv_aggregate_semimod helper used by the RV-returning aggregate
462 * rewrite (sum, avg, and any future aggregate whose result type is
463 * random_variable). The planner-hook routes on aggtype instead of a
464 * per-aggregate OID, so no individual aggregate OID needs to be
465 * cached here; an InvalidOid for this helper just leaves the rewrite
466 * disabled on older schemas that lack the continuous-distribution
467 * surface (1.0.0 baseline used by extension_upgrade). */
469 get_provsql_func_oid("rv_aggregate_semimod");
470
471 /* choose(anyelement): keeps the first non-NULL value of a group. Used by
472 * the scalar-subquery decorrelation to pick the single matched value.
473 * Optional lookup (0 on schemas predating it). */
474 constants.OID_FUNCTION_CHOOSE = get_provsql_func_oid("choose");
475
476 /* assume_boolean is installed by the 1.6.0 upgrade script. Treat
477 * its absence as a soft signal: on older schemas the safe-query
478 * rewriter (gated behind the 'boolean' provenance class) refuses to
479 * fire because it cannot mark the per-row root with a
480 * gate_assumed for downstream semiring-compatibility
481 * enforcement. Optional lookup matches the pattern used above for
482 * rv_aggregate_semimod. */
484 get_provsql_func_oid("assume_boolean");
485
486 /* annotate(uuid,text) -- transparent annotation wrapper carrying the
487 * inversion-free certificate / order keys. Optional like the helpers
488 * above: InvalidOid on an older schema simply disables the carrier. */
489 constants.OID_FUNCTION_ANNOTATE =
490 get_provsql_func_oid("annotate");
491
492 /* inversion_free_key(text,text,int) -- builds the per-input order-key string
493 * for the inversion-free path. Optional: InvalidOid on an older schema
494 * disables per-input markers (the path then declines and falls back). */
496 get_provsql_func_oid("inversion_free_key");
497
498 /* cond(uuid,uuid) / given(uuid) -- the conditioning operator and its
499 * whole-tuple output marker. Optional: InvalidOid on a schema predating
500 * the conditioning feature disables the given() rewrite. */
501 constants.OID_FUNCTION_COND = get_provsql_func_oid("cond");
502 constants.OID_FUNCTION_GIVEN = get_provsql_func_oid("given");
503
504 /* random_variable_cond(random_variable,uuid) and its Boolean-predicate
505 * placeholder random_variable_cond_predicate(random_variable,boolean): the
506 * planner rewrites "X | (predicate)" into the former over the gate built
507 * from the latter's Boolean operand. Optional (older schemas). */
508 constants.OID_FUNCTION_RV_COND =
509 get_provsql_func_oid("random_variable_cond");
510 constants.OID_FUNCTION_AGG_COND =
511 get_provsql_func_oid("agg_token_cond");
512 /* The "X | (predicate)" placeholders, one per carrier (and the prefix
513 * whole-tuple form). The planner rewrites each into the matching
514 * conditioning constructor over the gate built from the Boolean operand. */
516 get_provsql_func_oid("cond_predicate");
518 get_provsql_func_oid("random_variable_cond_predicate");
520 get_provsql_func_oid("agg_token_cond_predicate");
522 get_provsql_func_oid("given_predicate");
524 get_provsql_func_oid("regular_indicator");
525
526 /* random_variable_{eq,ne,le,lt,ge,gt} -- order matches the
527 * ComparisonOperator enum in src/Aggregation.h (EQ=0, NE=1, LE=2,
528 * LT=3, GE=4, GT=5). */
529 constants.OID_FUNCTION_RV_CMP[0] = get_provsql_func_oid("random_variable_eq");
530 constants.OID_FUNCTION_RV_CMP[1] = get_provsql_func_oid("random_variable_ne");
531 constants.OID_FUNCTION_RV_CMP[2] = get_provsql_func_oid("random_variable_le");
532 constants.OID_FUNCTION_RV_CMP[3] = get_provsql_func_oid("random_variable_lt");
533 constants.OID_FUNCTION_RV_CMP[4] = get_provsql_func_oid("random_variable_ge");
534 constants.OID_FUNCTION_RV_CMP[5] = get_provsql_func_oid("random_variable_gt");
535
536 OperatorGet("<>", PG_CATALOG_NAMESPACE, constants.OID_TYPE_UUID, constants.OID_TYPE_UUID, &constants.OID_OPERATOR_NOT_EQUAL_UUID, &constants.OID_FUNCTION_NOT_EQUAL_UUID);
537 CheckOid(OID_OPERATOR_NOT_EQUAL_UUID);
538 CheckOid(OID_FUNCTION_NOT_EQUAL_UUID);
539
540 /** @brief Look up the OID of provenance_gate enum value @p x and store it in constants. */
541 #define GET_GATE_TYPE_OID(x) { \
542 constants.GATE_TYPE_TO_OID[gate_ ## x] = get_enum_oid( \
543 constants.OID_TYPE_GATE_TYPE, \
544 #x); \
545 if(constants.GATE_TYPE_TO_OID[gate_ ## x]==InvalidOid) \
546 provsql_error("Could not initialize provsql gate type " #x); }
547
548 /** @brief Like @c GET_GATE_TYPE_OID but tolerates a missing enum value.
549 *
550 * Used for gate types added in releases newer than the oldest schema
551 * the @c extension_upgrade test exercises (currently 1.0.0). An
552 * intermediate state where a 1.0.0 database has been bound to a newer
553 * shared library is possible (e.g. between @c CREATE @c EXTENSION
554 * @c VERSION and @c ALTER @c EXTENSION @c UPDATE) and must not abort
555 * @c get_constants -- the missing OID stays @c InvalidOid and any
556 * attempt to actually create such a gate fails later in
557 * @c create_gate's "Invalid gate type" branch. When the upgrade
558 * scripts catch up, the lookup succeeds and the gate becomes usable
559 * normally. No state for @c InvalidOid is stored, so the field
560 * keeps its zero-init value (which is @c InvalidOid).
561 */
562 #define GET_GATE_TYPE_OID_OPTIONAL(x) { \
563 constants.GATE_TYPE_TO_OID[gate_ ## x] = get_enum_oid( \
564 constants.OID_TYPE_GATE_TYPE, \
565 #x); }
566
567 GET_GATE_TYPE_OID(input);
568 GET_GATE_TYPE_OID(plus);
569 GET_GATE_TYPE_OID(times);
570 GET_GATE_TYPE_OID(monus);
571 GET_GATE_TYPE_OID(project);
572 GET_GATE_TYPE_OID(zero);
576 GET_GATE_TYPE_OID(semimod);
578 GET_GATE_TYPE_OID(delta);
579 GET_GATE_TYPE_OID(value);
580 GET_GATE_TYPE_OID(mulinput);
581 GET_GATE_TYPE_OID(update);
586 /* The 'assumed' label was 'assumed_boolean' before 1.10.0; ALTER TYPE
587 * ... RENAME VALUE keeps the enum value's OID, so accepting the
588 * former label is equivalent -- and necessary when this cache is
589 * built in a session that later runs the upgrade chain across the
590 * rename (the extension-upgrade test does exactly that). */
591 if(constants.GATE_TYPE_TO_OID[gate_assumed] == InvalidOid)
592 constants.GATE_TYPE_TO_OID[gate_assumed] =
593 get_enum_oid(constants.OID_TYPE_GATE_TYPE, "assumed_boolean");
594 GET_GATE_TYPE_OID_OPTIONAL(annotation);
595 GET_GATE_TYPE_OID_OPTIONAL(conditioned);
597
598 constants.ok=true;
599
600 return constants;
601}
602
603static database_constants_t *constants_cache; ///< Per-database OID constants cache (sorted by database OID)
604static unsigned constants_cache_len=0; ///< Number of valid entries in @c constants_cache
605
606constants_t get_constants(bool failure_if_not_possible)
607{
608 int start=0, end=constants_cache_len-1;
609 database_constants_t *constants_cache2;
610 constants_t constants;
611
612 while(end>=start) {
613 unsigned mid=(start+end)/2;
614 if(constants_cache[mid].database<MyDatabaseId)
615 start=mid+1;
616 else if(constants_cache[mid].database>MyDatabaseId)
617 end=mid-1;
618 else
619 return constants_cache[mid].constants;
620 }
621
622 constants=initialize_constants(failure_if_not_possible);
623
624 /* Only memoize successful lookups. A failed lookup (ok==false) means
625 * the provsql extension was not fully visible to this backend at this
626 * point -- e.g. the connection's first planned query ran before
627 * CREATE EXTENSION, or during a DROP/CREATE/ALTER EXTENSION ... UPDATE
628 * window. Caching that failure would permanently disable provenance
629 * tracking on the backend: a long-lived pooled connection would never
630 * recover, silently dropping the provsql column on every query until
631 * reset_constants_cache() is called by hand. Returning without
632 * caching lets the next call retry until the extension is available. */
633 if(!constants.ok)
634 return constants;
635
636 constants_cache2=calloc(constants_cache_len+1, sizeof(database_constants_t));
637 for(unsigned i=0; i<start; ++i)
638 constants_cache2[i]=constants_cache[i];
639
640 constants_cache2[start].database=MyDatabaseId;
641 constants_cache2[start].constants=constants;
642
643 for(unsigned i=start; i<constants_cache_len; ++i)
644 constants_cache2[i+1]=constants_cache[i];
645 free(constants_cache);
646 constants_cache=constants_cache2;
648
649 return constants_cache[start].constants;
650}
651
652/* -------------------------------------------------------------------------
653 * Per-backend table-info cache
654 *
655 * Sorted array of @c table_info_cache_entry, binary-searched on @c relid.
656 * Used by @c provsql_lookup_table_info to amortise IPC across repeated
657 * lookups during query planning. Entries are invalidated by
658 * @c invalidate_table_info_cache_callback, which is hooked into
659 * PostgreSQL's relcache invalidation channel and so reacts to local
660 * DDL, cross-backend invalidations broadcast via
661 * @c CacheInvalidateRelcacheByRelid, and explicit "invalidate all"
662 * (relid == InvalidOid) events.
663 * ------------------------------------------------------------------------- */
664
666 Oid relid; ///< pg_class OID (sort key)
667 bool valid; ///< false => refresh on next access
668 bool present; ///< when valid: was a record found at the worker?
669 uint8 kind; ///< when present: provsql_table_kind value
670 uint16 block_key_n; ///< when present: number of block-key columns
671 AttrNumber block_key[PROVSQL_TABLE_INFO_MAX_BLOCK_KEY]; ///< when present: block-key column numbers
673
674static table_info_cache_entry *table_info_cache = NULL; ///< Sorted by @c relid
675static unsigned table_info_cache_len = 0;
677
678/** Find @p relid in the cache. Returns the index on hit; otherwise
679 * @c -1 and writes the insertion point to @p *insert_at. */
680static int table_info_cache_find(Oid relid, int *insert_at)
681{
682 int start = 0, end = (int)table_info_cache_len - 1;
683 while(end >= start) {
684 int mid = (start + end) / 2;
685 if(table_info_cache[mid].relid < relid)
686 start = mid + 1;
687 else if(table_info_cache[mid].relid > relid)
688 end = mid - 1;
689 else
690 return mid;
691 }
692 if(insert_at) *insert_at = start;
693 return -1;
694}
695
696/** Insert a fresh entry at @p pos (which must be the value returned by
697 * the most recent @c table_info_cache_find that reported a miss). */
698static void table_info_cache_insert(int pos, Oid relid, bool present,
699 const ProvenanceTableInfo *info)
700{
701 table_info_cache_entry *new_buf = calloc(table_info_cache_len + 1,
702 sizeof(table_info_cache_entry));
703 for(int i = 0; i < pos; ++i)
704 new_buf[i] = table_info_cache[i];
705 new_buf[pos].relid = relid;
706 new_buf[pos].valid = true;
707 new_buf[pos].present = present;
708 if(present) {
709 new_buf[pos].kind = info->kind;
710 new_buf[pos].block_key_n = info->block_key_n;
711 memcpy(new_buf[pos].block_key, info->block_key,
712 info->block_key_n * sizeof(AttrNumber));
713 }
714 for(unsigned i = (unsigned)pos; i < table_info_cache_len; ++i)
715 new_buf[i + 1] = table_info_cache[i];
716 free(table_info_cache);
717 table_info_cache = new_buf;
719}
720
721/** Relcache callback: PostgreSQL fires this whenever a relation's
722 * relcache entry is invalidated (locally or via shared invalidation
723 * from another backend). @p relid == @c InvalidOid means
724 * "invalidate everything." */
725static void invalidate_table_info_cache_callback(Datum arg, Oid relid)
726{
727 int pos;
728 (void) arg;
729 if(relid == InvalidOid) {
730 for(unsigned i = 0; i < table_info_cache_len; ++i)
731 table_info_cache[i].valid = false;
732 return;
733 }
734 pos = table_info_cache_find(relid, NULL);
735 if(pos >= 0)
736 table_info_cache[pos].valid = false;
737}
738
740{
741 int insert_at = 0;
742 int pos;
744 bool present;
745
747 CacheRegisterRelcacheCallback(invalidate_table_info_cache_callback,
748 (Datum) 0);
750 }
751
752 pos = table_info_cache_find(relid, &insert_at);
753
754 if(pos >= 0 && table_info_cache[pos].valid) {
756 if(!e->present)
757 return false;
758 out->relid = relid;
759 out->kind = e->kind;
760 out->block_key_n = e->block_key_n;
761 memcpy(out->block_key, e->block_key,
762 e->block_key_n * sizeof(AttrNumber));
763 return true;
764 }
765
766 present = provsql_fetch_table_info(relid, &info);
767
768 if(pos >= 0) {
769 /* Refresh an existing, now-stale slot in place. */
771 e->valid = true;
772 e->present = present;
773 if(present) {
774 e->kind = info.kind;
775 e->block_key_n = info.block_key_n;
776 memcpy(e->block_key, info.block_key,
777 info.block_key_n * sizeof(AttrNumber));
778 }
779 } else {
780 table_info_cache_insert(insert_at, relid, present, &info);
781 }
782
783 if(present) {
784 *out = info;
785 return true;
786 }
787 return false;
788}
789
790/* -------------------------------------------------------------------------
791 * Per-backend ancestry cache
792 *
793 * Structurally identical to the table-info cache above but keyed on
794 * just the ancestor half of the on-disk record. The two halves share
795 * the storage (one @c ProvenanceTableInfo per relation) but are
796 * cached separately so a hot-path safe-query lookup that only needs
797 * the kind doesn't pull the ancestor array, and vice versa. The
798 * relcache callback below reuses the channel of the kind cache so
799 * any @c set_ancestors / @c add_provenance / @c repair_key DDL
800 * broadcast invalidates both caches in lock-step.
801 * ------------------------------------------------------------------------- */
802
803typedef struct ancestry_cache_entry {
804 Oid relid; ///< pg_class OID (sort key)
805 bool valid; ///< false => refresh on next access
806 bool present; ///< when valid: did the worker return any ancestors?
807 uint16 ancestor_n; ///< when present: count
808 Oid ancestors[PROVSQL_TABLE_INFO_MAX_ANCESTORS]; ///< when present: ancestor OIDs
810
812static unsigned ancestry_cache_len = 0;
814
815static int ancestry_cache_find(Oid relid, int *insert_at)
816{
817 int start = 0, end = (int)ancestry_cache_len - 1;
818 while(end >= start) {
819 int mid = (start + end) / 2;
820 if(ancestry_cache[mid].relid < relid)
821 start = mid + 1;
822 else if(ancestry_cache[mid].relid > relid)
823 end = mid - 1;
824 else
825 return mid;
826 }
827 if(insert_at) *insert_at = start;
828 return -1;
829}
830
831static void ancestry_cache_insert(int pos, Oid relid, bool present,
832 uint16 ancestor_n, const Oid *ancestors)
833{
834 ancestry_cache_entry *new_buf = calloc(ancestry_cache_len + 1,
835 sizeof(ancestry_cache_entry));
836 for(int i = 0; i < pos; ++i)
837 new_buf[i] = ancestry_cache[i];
838 new_buf[pos].relid = relid;
839 new_buf[pos].valid = true;
840 new_buf[pos].present = present;
841 if(present) {
842 new_buf[pos].ancestor_n = ancestor_n;
843 memcpy(new_buf[pos].ancestors, ancestors,
844 ancestor_n * sizeof(Oid));
845 }
846 for(unsigned i = (unsigned)pos; i < ancestry_cache_len; ++i)
847 new_buf[i + 1] = ancestry_cache[i];
848 free(ancestry_cache);
849 ancestry_cache = new_buf;
851}
852
853static void invalidate_ancestry_cache_callback(Datum arg, Oid relid)
854{
855 int pos;
856 (void) arg;
857 if(relid == InvalidOid) {
858 for(unsigned i = 0; i < ancestry_cache_len; ++i)
859 ancestry_cache[i].valid = false;
860 return;
861 }
862 pos = ancestry_cache_find(relid, NULL);
863 if(pos >= 0)
864 ancestry_cache[pos].valid = false;
865}
866
867bool provsql_lookup_ancestry(Oid relid, uint16 *ancestor_n_out,
868 Oid *ancestors_out)
869{
870 int insert_at = 0;
871 int pos;
872 uint16 n = 0;
874 bool present;
875
877 CacheRegisterRelcacheCallback(invalidate_ancestry_cache_callback,
878 (Datum) 0);
880 }
881
882 pos = ancestry_cache_find(relid, &insert_at);
883
884 if(pos >= 0 && ancestry_cache[pos].valid) {
886 if(!e->present)
887 return false;
888 *ancestor_n_out = e->ancestor_n;
889 memcpy(ancestors_out, e->ancestors,
890 e->ancestor_n * sizeof(Oid));
891 return true;
892 }
893
894 present = provsql_fetch_ancestry(relid, &n, ancestors);
895
896 if(pos >= 0) {
898 e->valid = true;
899 e->present = present;
900 if(present) {
901 e->ancestor_n = n;
902 memcpy(e->ancestors, ancestors, n * sizeof(Oid));
903 }
904 } else {
905 ancestry_cache_insert(insert_at, relid, present, n, ancestors);
906 }
907
908 if(present) {
909 *ancestor_n_out = n;
910 memcpy(ancestors_out, ancestors, n * sizeof(Oid));
911 return true;
912 }
913 return false;
914}
915
916/* -------------------------------------------------------------------------
917 * Per-backend relation-keys cache (§2 PK-FD support)
918 *
919 * Sibling of the @c table_info_cache above; structurally identical
920 * (sorted-by-relid array, binary search, relcache-invalidated) but
921 * keyed on @c pg_class OIDs of @em arbitrary relations -- not just
922 * provenance-tracked ones -- because the §2 / §3 / §5 detector
923 * passes also need to reason about PRIMARY KEY constraints on
924 * deterministic dimension tables and self-joined relations.
925 *
926 * Each cached entry holds up to @c PROVSQL_KEY_CACHE_MAX_KEYS keys
927 * (PRIMARY KEY plus NOT-NULL UNIQUE constraints from
928 * @c pg_constraint, filtered by @c contype @c IN @c ('p','u')).
929 * The relcache callback uses a separate static flag from the
930 * table-info cache's, so a DROP CONSTRAINT / DROP TABLE invalidates
931 * both caches independently.
932 * ------------------------------------------------------------------------- */
933
941
943static unsigned key_cache_len = 0;
945
946static int key_cache_find(Oid relid, int *insert_at)
947{
948 int start = 0, end = (int) key_cache_len - 1;
949 while(end >= start) {
950 int mid = (start + end) / 2;
951 if(key_cache[mid].relid < relid)
952 start = mid + 1;
953 else if(key_cache[mid].relid > relid)
954 end = mid - 1;
955 else
956 return mid;
957 }
958 if(insert_at) *insert_at = start;
959 return -1;
960}
961
962static void key_cache_insert(int pos, Oid relid,
963 const ProvenanceRelationKeys *keys)
964{
965 key_cache_entry *new_buf = calloc(key_cache_len + 1,
966 sizeof(key_cache_entry));
967 for(int i = 0; i < pos; ++i)
968 new_buf[i] = key_cache[i];
969 new_buf[pos].relid = relid;
970 new_buf[pos].valid = true;
971 new_buf[pos].has_keys = keys->key_n > 0;
972 new_buf[pos].key_n = keys->key_n;
973 if(keys->key_n > 0)
974 memcpy(new_buf[pos].keys, keys->keys,
975 keys->key_n * sizeof(ProvenanceRelationKey));
976 for(unsigned i = (unsigned)pos; i < key_cache_len; ++i)
977 new_buf[i + 1] = key_cache[i];
978 free(key_cache);
979 key_cache = new_buf;
981}
982
983static void invalidate_key_cache_callback(Datum arg, Oid relid)
984{
985 int pos;
986 (void) arg;
987 if(relid == InvalidOid) {
988 for(unsigned i = 0; i < key_cache_len; ++i)
989 key_cache[i].valid = false;
990 return;
991 }
992 pos = key_cache_find(relid, NULL);
993 if(pos >= 0)
994 key_cache[pos].valid = false;
995}
996
997/**
998 * @brief Read the PRIMARY-KEY and NOT-NULL-UNIQUE keys of @p relid
999 * from the system catalogs.
1000 *
1001 * Scans @c pg_constraint for entries with @c conrelid @c = @p relid
1002 * and @c contype @c IN @c ('p','u'), then resolves each constraint's
1003 * column list via @c pg_index.indkey (the constraint's index is
1004 * recorded in @c pg_constraint.conindid). For UNIQUE constraints,
1005 * verifies every constituent column has @c pg_attribute.attnotnull
1006 * @c = @c true; UNIQUE-with-NULLABLE constraints are rejected
1007 * (UNIQUE allows multiple rows with NULL in PostgreSQL, so the
1008 * @c ∅ @c → @c attr FD does not hold without NOT NULL).
1009 *
1010 * Stores up to @c PROVSQL_KEY_CACHE_MAX_KEYS keys; subsequent keys
1011 * are silently dropped. Skips constraints whose column count
1012 * exceeds @c PROVSQL_KEY_CACHE_MAX_KEY_COLS. Both elisions are
1013 * conservatively safe (the §2 detector simply does not see the
1014 * dropped FDs).
1015 */
1017{
1018 Relation conrel;
1019 SysScanDesc scan;
1020 ScanKeyData skey;
1021 HeapTuple htup;
1022
1023 out->relid = relid;
1024 out->key_n = 0;
1025
1026 conrel = table_open(ConstraintRelationId, AccessShareLock);
1027 ScanKeyInit(&skey,
1028 Anum_pg_constraint_conrelid,
1029 BTEqualStrategyNumber, F_OIDEQ,
1030 ObjectIdGetDatum(relid));
1031 scan = systable_beginscan(conrel,
1032#if PG_VERSION_NUM >= 110000
1033 ConstraintRelidTypidNameIndexId,
1034#else
1035 ConstraintRelidIndexId, /* PG 10 name */
1036#endif
1037 true, NULL, 1, &skey);
1038
1039 while(HeapTupleIsValid(htup = systable_getnext(scan))) {
1040 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(htup);
1041 HeapTuple idxtup;
1042 Form_pg_index idx;
1043 Oid indexrelid;
1044 int k;
1046 bool ok_not_null = true;
1047
1048 if(con->contype != CONSTRAINT_PRIMARY && con->contype != CONSTRAINT_UNIQUE)
1049 continue;
1051 break;
1052
1053 indexrelid = con->conindid;
1054 if(!OidIsValid(indexrelid))
1055 continue;
1056 idxtup = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexrelid));
1057 if(!HeapTupleIsValid(idxtup))
1058 continue;
1059 idx = (Form_pg_index) GETSTRUCT(idxtup);
1060
1061 if(idx->indnatts <= 0 || idx->indnatts > PROVSQL_KEY_CACHE_MAX_KEY_COLS) {
1062 ReleaseSysCache(idxtup);
1063 continue;
1064 }
1065
1066 key = &out->keys[out->key_n];
1067 key->col_n = (uint16) idx->indnatts;
1068 for(k = 0; k < idx->indnatts; ++k) {
1069 AttrNumber attno = idx->indkey.values[k];
1070 key->cols[k] = attno;
1071
1072 if(con->contype == CONSTRAINT_UNIQUE) {
1073 HeapTuple atttup =
1074 SearchSysCache2(ATTNUM,
1075 ObjectIdGetDatum(relid),
1076 Int16GetDatum(attno));
1077 if(!HeapTupleIsValid(atttup)) {
1078 ok_not_null = false;
1079 break;
1080 } else {
1081 Form_pg_attribute attform = (Form_pg_attribute) GETSTRUCT(atttup);
1082 if(!attform->attnotnull)
1083 ok_not_null = false;
1084 ReleaseSysCache(atttup);
1085 if(!ok_not_null)
1086 break;
1087 }
1088 }
1089 }
1090 ReleaseSysCache(idxtup);
1091
1092 if(!ok_not_null)
1093 continue; /* nullable UNIQUE -- skip */
1094
1095 ++out->key_n;
1096 }
1097
1098 systable_endscan(scan);
1099 table_close(conrel, AccessShareLock);
1100
1101 return out->key_n > 0;
1102}
1103
1105{
1106 int insert_at = 0;
1107 int pos;
1109 bool has_keys;
1110
1112 CacheRegisterRelcacheCallback(invalidate_key_cache_callback, (Datum) 0);
1114 }
1115
1116 pos = key_cache_find(relid, &insert_at);
1117 if(pos >= 0 && key_cache[pos].valid) {
1118 key_cache_entry *e = &key_cache[pos];
1119 out->relid = relid;
1120 out->key_n = e->key_n;
1121 if(e->key_n > 0)
1122 memcpy(out->keys, e->keys, e->key_n * sizeof(ProvenanceRelationKey));
1123 return e->has_keys;
1124 }
1125
1126 has_keys = fetch_relation_keys(relid, &fresh);
1127
1128 if(pos >= 0) {
1129 key_cache_entry *e = &key_cache[pos];
1130 e->valid = true;
1131 e->has_keys = has_keys;
1132 e->key_n = fresh.key_n;
1133 if(fresh.key_n > 0)
1134 memcpy(e->keys, fresh.keys, fresh.key_n * sizeof(ProvenanceRelationKey));
1135 } else {
1136 key_cache_insert(insert_at, relid, &fresh);
1137 }
1138
1139 *out = fresh;
1140 return has_keys;
1141}
1142
1143PG_FUNCTION_INFO_V1(reset_constants_cache);
1144/**
1145 * @brief SQL function to invalidate the OID constants cache.
1146 *
1147 * Forces a fresh OID lookup for the current database on the next call to
1148 * @c get_constants(). Must be called after @c ALTER EXTENSION provsql
1149 * UPDATE to ensure cached OIDs are refreshed.
1150 * @return Void datum.
1151 */
1152Datum reset_constants_cache(PG_FUNCTION_ARGS)
1153{
1154 int start=0, end=constants_cache_len-1;
1155
1156 while(end>=start) {
1157 unsigned mid=(start+end)/2;
1158 if(constants_cache[mid].database<MyDatabaseId)
1159 start=mid+1;
1160 else if(constants_cache[mid].database>MyDatabaseId)
1161 end=mid-1;
1162 else {
1163 constants_cache[mid].constants = initialize_constants(true);
1164 break;
1165 }
1166 }
1167
1168 PG_RETURN_VOID();
1169}
#define PROVSQL_TABLE_INFO_MAX_BLOCK_KEY
Cap on the number of block-key columns recorded per relation.
#define PROVSQL_TABLE_INFO_MAX_ANCESTORS
Cap on the number of base ancestors recorded per relation.
bool provsql_fetch_table_info(Oid relid, ProvenanceTableInfo *out)
C-callable IPC fetch for per-table provenance metadata.
bool provsql_fetch_ancestry(Oid relid, uint16 *ancestor_n_out, Oid *ancestors_out)
C-callable IPC fetch for the ancestor half of a per-table metadata record.
static bool key_cache_callback_registered
static unsigned table_info_cache_len
static bool table_info_callback_registered
const char * gate_type_name[]
Names of gate types.
static Oid get_enum_oid(Oid enumtypoid, const char *label)
Return the OID of a specific enum label within an enum type.
static int key_cache_find(Oid relid, int *insert_at)
static void invalidate_ancestry_cache_callback(Datum arg, Oid relid)
static void OperatorGet(const char *operatorName, Oid operatorNamespace, Oid leftObjectId, Oid rightObjectId, Oid *operatorObjectId, Oid *functionObjectId)
Retrieve operator and function OIDs for a named operator.
Datum reset_constants_cache(PG_FUNCTION_ARGS)
SQL function to invalidate the OID constants cache.
Oid find_equality_operator(Oid ltypeId, Oid rtypeId)
Find the equality operator OID for two given types.
static void ancestry_cache_insert(int pos, Oid relid, bool present, uint16 ancestor_n, const Oid *ancestors)
#define GET_GATE_TYPE_OID_OPTIONAL(x)
bool provsql_lookup_ancestry(Oid relid, uint16 *ancestor_n_out, Oid *ancestors_out)
Look up the base-ancestor set of a tracked relation.
static Oid get_provsql_func_oid(char *s)
Return the OID of a provsql-schema function named s.
static database_constants_t * constants_cache
Per-database OID constants cache (sorted by database OID).
static void invalidate_table_info_cache_callback(Datum arg, Oid relid)
Relcache callback: PostgreSQL fires this whenever a relation's relcache entry is invalidated (locally...
bool provsql_lookup_table_info(Oid relid, ProvenanceTableInfo *out)
Look up per-table provenance metadata with a backend-local cache.
#define table_close(r, l)
static bool ancestry_callback_registered
static void table_info_cache_insert(int pos, Oid relid, bool present, const ProvenanceTableInfo *info)
Insert a fresh entry at pos (which must be the value returned by the most recent table_info_cache_fin...
static table_info_cache_entry * table_info_cache
Sorted by relid.
constants_t get_constants(bool failure_if_not_possible)
Retrieve the cached OID constants for the current database.
static unsigned constants_cache_len
Number of valid entries in constants_cache.
static int table_info_cache_find(Oid relid, int *insert_at)
Find relid in the cache.
static int ancestry_cache_find(Oid relid, int *insert_at)
static void invalidate_key_cache_callback(Datum arg, Oid relid)
bool provsql_lookup_relation_keys(Oid relid, ProvenanceRelationKeys *out)
Look up the PRIMARY-KEY and NOT-NULL-UNIQUE keys of a relation with a backend-local cache.
static unsigned ancestry_cache_len
static bool fetch_relation_keys(Oid relid, ProvenanceRelationKeys *out)
Read the PRIMARY-KEY and NOT-NULL-UNIQUE keys of relid from the system catalogs.
static Oid get_func_oid(char *s)
Return the OID of a globally qualified function named s.
static unsigned key_cache_len
static Oid binary_oper_exact(List *opname, Oid arg1, Oid arg2)
Look up an exactly matching binary operator OID.
#define GET_GATE_TYPE_OID(x)
#define CheckOid(o)
static void key_cache_insert(int pos, Oid relid, const ProvenanceRelationKeys *keys)
static key_cache_entry * key_cache
#define table_open(r, l)
static constants_t initialize_constants(bool failure_if_not_possible)
Query the system catalogs to populate a fresh constants_t.
static ancestry_cache_entry * ancestry_cache
Core types, constants, and utilities shared across ProvSQL.
#define PROVSQL_KEY_CACHE_MAX_KEY_COLS
@ gate_assumed
Structural marker over a single child whose sub-circuit was computed under a Boolean-provenance assum...
#define PROVSQL_KEY_CACHE_MAX_KEYS
Upper bounds for the relation-key cache.
One PRIMARY-KEY or NOT-NULL-UNIQUE key on a relation.
AttrNumber cols[PROVSQL_KEY_CACHE_MAX_KEY_COLS]
Per-relation set of PRIMARY-KEY and NOT-NULL-UNIQUE keys.
ProvenanceRelationKey keys[PROVSQL_KEY_CACHE_MAX_KEYS]
Per-relation metadata for the safe-query optimisation.
Oid relid
pg_class OID of the relation (primary key)
AttrNumber block_key[PROVSQL_TABLE_INFO_MAX_BLOCK_KEY]
Block-key column numbers.
uint16_t block_key_n
Number of valid entries in block_key.
uint8_t kind
One of provsql_table_kind.
uint16 ancestor_n
when present: count
Oid ancestors[PROVSQL_TABLE_INFO_MAX_ANCESTORS]
when present: ancestor OIDs
Oid relid
pg_class OID (sort key)
bool present
when valid: did the worker return any ancestors?
bool valid
false => refresh on next access
Structure to store the value of various constants.
Oid OID_FUNCTION_REGULAR_INDICATOR
OID of provsql.regular_indicator(boolean): the deterministic gate_one/gate_zero indicator the planner...
Oid OID_FUNCTION_PROVENANCE_EQ
OID of the provenance_eq FUNCTION.
Oid OID_FUNCTION_PROVENANCE_AGGREGATE
OID of the provenance_aggregate FUNCTION.
Oid OID_FUNCTION_PROVENANCE_SEMIMOD
OID of the provenance_semimod FUNCTION.
Oid OID_FUNCTION_CHOOSE
OID of the choose(anyelement) aggregate (keeps the first non-NULL value); used to decorrelate scalar ...
Oid OID_FUNCTION_ANNOTATE
OID of provsql.annotate(uuid,text)->uuid.
Oid OID_FUNCTION_PROVENANCE
OID of the provenance FUNCTION.
Oid OID_FUNCTION_INVERSION_FREE_KEY
OID of provsql.inversion_free_key(text,text,int)->text.
Oid OID_FUNCTION_AGG_TOKEN_UUID
OID of the agg_token_uuid FUNCTION.
Oid OID_FUNCTION_RV_AGGREGATE_SEMIMOD
OID of rv_aggregate_semimod helper (uuid, rv -> rv) used to wrap each per-row argument of an RV-retur...
Oid OID_TYPE_VARCHAR
OID of the VARCHAR TYPE.
Oid OID_FUNCTION_GATE_ZERO
OID of the provenance_zero FUNCTION.
Oid OID_SCHEMA_PROVSQL
OID of the provsql SCHEMA.
Oid OID_FUNCTION_COND
OID of provsql.cond(uuid,uuid)->uuid.
Oid OID_TYPE_GATE_TYPE
OID of the provenance_gate TYPE.
Oid OID_FUNCTION_PROVENANCE_PROJECT
OID of the provenance_project FUNCTION.
Oid OID_FUNCTION_GET_CHILDREN
OID of the get_children FUNCTION.
Oid OID_UNNEST
OID of the unnest(anyarray) FUNCTION.
Oid OID_FUNCTION_COND_PREDICATE
cond_predicate(uuid,boolean)
Oid OID_TYPE_FLOAT
OID of the FLOAT TYPE.
Oid OID_TYPE_AGG_TOKEN
OID of the agg_token TYPE.
Oid OID_FUNCTION_ARRAY_AGG
OID of the array_agg FUNCTION.
Oid OID_TYPE_INT
OID of the INT TYPE.
Oid GATE_TYPE_TO_OID[nb_gate_types]
Array of the OID of each provenance_gate ENUM value.
Oid OID_FUNCTION_PROVENANCE_PLUS
OID of the provenance_plus FUNCTION.
Oid OID_OPERATOR_NOT_EQUAL_UUID
OID of the <> operator on UUIDs FUNCTION.
Oid OID_TYPE_UUID
OID of the uuid TYPE.
Oid OID_TYPE_TSTZMULTIRANGE
OID of the tstzmultirange TYPE (PG14+, InvalidOid otherwise).
bool ok
true if constants were loaded
Oid OID_TYPE_INT_ARRAY
OID of the INT[] TYPE.
Oid OID_FUNCTION_PROVENANCE_DELTA
OID of the provenance_delta FUNCTION.
Oid OID_FUNCTION_ASSUME_BOOLEAN
OID of provsql.assume_boolean(uuid)->uuid.
Oid OID_FUNCTION_PROVENANCE_TIMES
OID of the provenance_times FUNCTION.
Oid OID_FUNCTION_PROVENANCE_MONUS
OID of the provenance_monus FUNCTION.
Oid OID_FUNCTION_AGG_COND_PREDICATE
agg_token_cond_predicate(agg_token,boolean)
Oid OID_FUNCTION_GIVEN_PREDICATE
given_predicate(boolean) – prefix whole-tuple
Oid OID_TYPE_BOOL
OID of the BOOL TYPE.
Oid OID_FUNCTION_NOT_EQUAL_UUID
OID of the = operator on UUIDs FUNCTION.
Oid OID_FUNCTION_GIVEN
OID of provsql.given(uuid)->uuid.
Oid OID_FUNCTION_GATE_ONE
OID of the provenance_one FUNCTION.
Oid OID_TYPE_NUMMULTIRANGE
OID of the nummultirange TYPE (PG14+, InvalidOid otherwise).
Oid OID_FUNCTION_RV_COND
OID of provsql.random_variable_cond(random_variable,uuid).
Oid OID_FUNCTION_RV_COND_PREDICATE
random_variable_cond_predicate(random_variable,boolean)
Oid OID_TYPE_UUID_ARRAY
OID of the uuid[] TYPE.
Oid OID_FUNCTION_AGG_COND
OID of provsql.agg_token_cond(agg_token,uuid): the conditioning constructor for the agg_token carrier...
Oid OID_TYPE_RANDOM_VARIABLE
OID of the random_variable TYPE.
Oid OID_FUNCTION_PROVENANCE_CMP
OID of the provenance_cmp FUNCTION.
Oid OID_TYPE_INT4MULTIRANGE
OID of the int4multirange TYPE (PG14+, InvalidOid otherwise).
Oid OID_FUNCTION_GET_EXTRA
OID of the get_extra FUNCTION.
Oid OID_FUNCTION_RV_CMP[6]
OIDs of the random_variable_{eq,ne,le,lt,ge,gt} comparison procedure functions, indexed by the Compar...
Structure to store the value of various constants for a specific database.
Oid database
OID of the database these constants belong to.
constants_t constants
Cached OID constants for this database.
uint16 key_n
bool valid
Oid relid
bool has_keys
ProvenanceRelationKey keys[PROVSQL_KEY_CACHE_MAX_KEYS]
uint8 kind
when present: provsql_table_kind value
Oid relid
pg_class OID (sort key)
uint16 block_key_n
when present: number of block-key columns
AttrNumber block_key[PROVSQL_TABLE_INFO_MAX_BLOCK_KEY]
when present: block-key column numbers
bool present
when valid: was a record found at the worker?
bool valid
false => refresh on next access