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#include "miscadmin.h"
24#include "catalog/namespace.h"
25#include "catalog/pg_type.h"
26#include "catalog/pg_enum.h"
27#include "catalog/pg_namespace.h"
28#include "catalog/pg_operator.h"
29#include "catalog/pg_type.h"
30#include "fmgr.h"
31#include "nodes/value.h"
32#include "parser/parse_func.h"
33#include "utils/syscache.h"
34#include "utils/lsyscache.h"
35
36#include "provsql_utils.h"
37
38const char *gate_type_name[] = {
39 "input",
40 "plus",
41 "times",
42 "monus",
43 "project",
44 "zero",
45 "one",
46 "eq",
47 "agg",
48 "semimod",
49 "cmp",
50 "delta",
51 "value",
52 "mulinput",
53 "update",
54 "invalid"
55};
56
57/**
58 * @brief Look up an exactly matching binary operator OID.
59 *
60 * Copied and adapted from @c parse_oper.c (PostgreSQL internals, not
61 * exported). Returns @c InvalidOid if no exact match exists.
62 *
63 * @param opname Qualified operator name (a @c List of @c String nodes).
64 * @param arg1 OID of the left operand type.
65 * @param arg2 OID of the right operand type.
66 * @return OID of the matching operator, or @c InvalidOid.
67 */
68static Oid
69binary_oper_exact(List *opname, Oid arg1, Oid arg2)
70{
71 Oid result;
72 bool was_unknown = false;
73
74 /* Unspecified type for one of the arguments? then use the other */
75 if ((arg1 == UNKNOWNOID) && (arg2 != InvalidOid))
76 {
77 arg1 = arg2;
78 was_unknown = true;
79 }
80 else if ((arg2 == UNKNOWNOID) && (arg1 != InvalidOid))
81 {
82 arg2 = arg1;
83 was_unknown = true;
84 }
85
86 result = OpernameGetOprid(opname, arg1, arg2);
87 if (OidIsValid(result))
88 return result;
89
90 if (was_unknown)
91 {
92 /* arg1 and arg2 are the same here, need only look at arg1 */
93 Oid basetype = getBaseType(arg1);
94
95 if (basetype != arg1)
96 {
97 result = OpernameGetOprid(opname, basetype, basetype);
98 if (OidIsValid(result))
99 return result;
100 }
101 }
102
103 return InvalidOid;
104}
105
106/* Adapted from PostgreSQL code that is not exported (see parse_oper.c
107 * and the static function oper_select_candidate therein).
108 */
109Oid find_equality_operator(Oid ltypeId, Oid rtypeId)
110{
111 List * const equals=list_make1(makeString("="));
112
113 FuncCandidateList clist;
114 Oid inputOids[2] = {ltypeId,rtypeId};
115 int ncandidates;
116
117 Oid result = binary_oper_exact(equals, ltypeId, rtypeId);
118
119 if(result!=InvalidOid)
120 return result;
121
122 clist = OpernameGetCandidates(equals, 'b', false);
123
124 ncandidates = func_match_argtypes(2, inputOids,
125 clist, &clist);
126
127 if (ncandidates == 0)
128 return InvalidOid;
129 else if (ncandidates == 1)
130 return clist->oid;
131
132 clist = func_select_candidate(2, inputOids, clist);
133
134 if(clist)
135 return clist->oid;
136 else
137 return InvalidOid;
138}
139
140/**
141 * @brief Return the OID of a globally qualified function named @p s.
142 *
143 * Looks up the function in the default search path. Returns 0 if no
144 * matching function is found.
145 *
146 * @param s Function name (unqualified).
147 * @return OID of the function, or 0 if not found.
148 */
149static Oid get_func_oid(char *s)
150{
151 FuncCandidateList fcl=FuncnameGetCandidates(
152 list_make1(makeString(s)),
153 -1,
154 NIL,
155 false,
156 false,
157#if PG_VERSION_NUM >= 140000
158 false,
159#endif
160 false);
161 if(fcl)
162 return fcl->oid;
163 else
164 return 0;
165}
166
167/**
168 * @brief Return the OID of a @c provsql-schema function named @p s.
169 *
170 * Looks up the function in the @c provsql schema. Returns 0 if not found.
171 *
172 * @param s Function name (without schema prefix).
173 * @return OID of the function, or 0 if not found.
174 */
175static Oid get_provsql_func_oid(char *s)
176{
177 FuncCandidateList fcl=FuncnameGetCandidates(
178 list_make2(makeString("provsql"),makeString(s)),
179 -1,
180 NIL,
181 false,
182 false,
183#if PG_VERSION_NUM >= 140000
184 false,
185#endif
186 false);
187 if(fcl)
188 return fcl->oid;
189 else
190 return 0;
191}
192
193/**
194 * @brief Retrieve operator and function OIDs for a named operator.
195 *
196 * Copied and adapted from @c pg_operator.c (PostgreSQL internals, not
197 * exported). Looks up the operator by name, namespace, and operand types
198 * in the system cache.
199 *
200 * @param operatorName Operator symbol string (e.g. @c "<>").
201 * @param operatorNamespace OID of the schema containing the operator.
202 * @param leftObjectId OID of the left operand type.
203 * @param rightObjectId OID of the right operand type.
204 * @param operatorObjectId Output: OID of the operator, or 0 if not found.
205 * @param functionObjectId Output: OID of the underlying function, or 0.
206 */
207static void OperatorGet(
208 const char *operatorName,
209 Oid operatorNamespace,
210 Oid leftObjectId,
211 Oid rightObjectId,
212 Oid *operatorObjectId,
213 Oid *functionObjectId)
214{
215 HeapTuple tup;
216 bool defined;
217
218 tup = SearchSysCache4(OPERNAMENSP,
219 PointerGetDatum(operatorName),
220 ObjectIdGetDatum(leftObjectId),
221 ObjectIdGetDatum(rightObjectId),
222 ObjectIdGetDatum(operatorNamespace));
223 if (HeapTupleIsValid(tup))
224 {
225 Form_pg_operator oprform = (Form_pg_operator) GETSTRUCT(tup);
226#if PG_VERSION_NUM >= 120000
227 *operatorObjectId = oprform->oid;
228#else
229 *operatorObjectId = HeapTupleGetOid(tup);
230#endif
231 *functionObjectId = oprform->oprcode;
232 defined = RegProcedureIsValid(oprform->oprcode);
233 ReleaseSysCache(tup);
234 }
235 else
236 {
237 defined = false;
238 }
239
240 if(!defined) {
241 *operatorObjectId = 0;
242 *functionObjectId = 0;
243 }
244}
245
246/**
247 * @brief Return the OID of a specific enum label within an enum type.
248 *
249 * @param enumtypoid OID of the enum type (e.g. @c provenance_gate).
250 * @param label C-string label of the enum value to look up.
251 * @return OID of the enum label's @c pg_enum row.
252 */
253static Oid get_enum_oid(Oid enumtypoid, const char *label)
254{
255 HeapTuple tup;
256 Oid ret;
257
258 tup = SearchSysCache2(ENUMTYPOIDNAME,
259 ObjectIdGetDatum(enumtypoid),
260 CStringGetDatum(label));
261 Assert(HeapTupleIsValid(tup));
262
263#if PG_VERSION_NUM >= 120000
264 ret = ((Form_pg_enum) GETSTRUCT(tup))->oid;
265#else
266 ret = HeapTupleGetOid(tup);
267#endif
268
269 ReleaseSysCache(tup);
270
271 return ret;
272}
273
274/**
275 * @brief Query the system catalogs to populate a fresh @c constants_t.
276 *
277 * Performs all OID lookups required by ProvSQL in a single pass through
278 * the system caches. The @c CheckOid() macro aborts (or returns early,
279 * depending on @p failure_if_not_possible) if any OID resolves to
280 * @c InvalidOid.
281 *
282 * @param failure_if_not_possible If @c true, raise a @c provsql_error when
283 * any OID cannot be resolved. If @c false, return a @c constants_t
284 * with @c ok==false instead.
285 * @return Fully populated @c constants_t on success, or @c ok==false on
286 * failure when @p failure_if_not_possible is @c false.
287 */
288static constants_t initialize_constants(bool failure_if_not_possible)
289{
290 constants_t constants;
291 constants.ok = false;
292
293 /** @brief Abort or return early if OID field @p o of @p constants is invalid. */
294 #define CheckOid(o) if(constants.o==InvalidOid) { \
295 if(failure_if_not_possible) \
296 provsql_error("Could not initialize provsql constants"); \
297 else \
298 return constants; }
299
300 constants.OID_SCHEMA_PROVSQL = get_namespace_oid("provsql", true);
301 CheckOid(OID_SCHEMA_PROVSQL);
302
303 constants.OID_TYPE_UUID = TypenameGetTypid("uuid");
304 CheckOid(OID_TYPE_UUID);
305
306 constants.OID_TYPE_GATE_TYPE = GetSysCacheOid2(
307 TYPENAMENSP,
308#if PG_VERSION_NUM >= 120000
309 Anum_pg_type_oid,
310#endif
311 CStringGetDatum("provenance_gate"),
312 ObjectIdGetDatum(constants.OID_SCHEMA_PROVSQL)
313 );
314 CheckOid(OID_TYPE_GATE_TYPE);
315
316 constants.OID_TYPE_AGG_TOKEN = GetSysCacheOid2(
317 TYPENAMENSP,
318#if PG_VERSION_NUM >= 120000
319 Anum_pg_type_oid,
320#endif
321 CStringGetDatum("agg_token"),
322 ObjectIdGetDatum(constants.OID_SCHEMA_PROVSQL)
323 );
324 CheckOid(OID_TYPE_AGG_TOKEN);
325
326 constants.OID_TYPE_UUID = TypenameGetTypid("uuid");
327 CheckOid(OID_TYPE_UUID);
328
329 constants.OID_TYPE_UUID_ARRAY = TypenameGetTypid("_uuid");
330 CheckOid(OID_TYPE_UUID_ARRAY);
331
332 constants.OID_TYPE_INT = TypenameGetTypid("int4");
333 CheckOid(OID_TYPE_INT);
334
335 constants.OID_TYPE_BOOL = TypenameGetTypid("bool");
336 CheckOid(OID_TYPE_BOOL);
337
338 constants.OID_TYPE_FLOAT = TypenameGetTypid("float8");
339 CheckOid(OID_TYPE_FLOAT);
340
341 constants.OID_TYPE_INT_ARRAY = TypenameGetTypid("_int4");
342 CheckOid(OID_TYPE_INT_ARRAY);
343
344 constants.OID_TYPE_VARCHAR = TypenameGetTypid("varchar");
345 CheckOid(OID_TYPE_VARCHAR);
346
347 constants.OID_FUNCTION_ARRAY_AGG = get_func_oid("array_agg");
348 CheckOid(OID_FUNCTION_ARRAY_AGG);
349
350 constants.OID_FUNCTION_PROVENANCE_PLUS = get_provsql_func_oid("provenance_plus");
351 CheckOid(OID_FUNCTION_PROVENANCE_PLUS);
352
353 constants.OID_FUNCTION_PROVENANCE_TIMES = get_provsql_func_oid("provenance_times");
354 CheckOid(OID_FUNCTION_PROVENANCE_TIMES);
355
356 constants.OID_FUNCTION_PROVENANCE_MONUS = get_provsql_func_oid("provenance_monus");
357 CheckOid(OID_FUNCTION_PROVENANCE_MONUS);
358
359 constants.OID_FUNCTION_PROVENANCE_PROJECT = get_provsql_func_oid("provenance_project");
360 CheckOid(OID_FUNCTION_PROVENANCE_PROJECT);
361
362 constants.OID_FUNCTION_PROVENANCE_EQ = get_provsql_func_oid("provenance_eq");
363 CheckOid(OID_FUNCTION_PROVENANCE_EQ);
364
365 constants.OID_FUNCTION_PROVENANCE = get_provsql_func_oid("provenance");
366 CheckOid(OID_FUNCTION_PROVENANCE);
367
368 constants.OID_FUNCTION_PROVENANCE_DELTA = get_provsql_func_oid("provenance_delta");
369 CheckOid(OID_FUNCTION_PROVENANCE_DELTA);
370
371 constants.OID_FUNCTION_PROVENANCE_AGGREGATE = get_provsql_func_oid("provenance_aggregate");
372 CheckOid(OID_FUNCTION_PROVENANCE_AGGREGATE);
373
374 constants.OID_FUNCTION_PROVENANCE_SEMIMOD = get_provsql_func_oid("provenance_semimod");
375 CheckOid(OID_FUNCTION_PROVENANCE_SEMIMOD);
376
377 constants.OID_FUNCTION_GATE_ZERO = get_provsql_func_oid("gate_zero");
378 CheckOid(OID_FUNCTION_GATE_ZERO);
379
380 constants.OID_FUNCTION_GATE_ONE = get_provsql_func_oid("gate_one");
381 CheckOid(OID_FUNCTION_GATE_ONE);
382
383 constants.OID_FUNCTION_PROVENANCE_CMP = get_provsql_func_oid("provenance_cmp");
384 CheckOid(OID_FUNCTION_PROVENANCE_CMP);
385
386 constants.OID_FUNCTION_AGG_TOKEN_UUID = get_provsql_func_oid("agg_token_uuid");
387 CheckOid(OID_FUNCTION_AGG_TOKEN_UUID);
388
389 OperatorGet("<>", PG_CATALOG_NAMESPACE, constants.OID_TYPE_UUID, constants.OID_TYPE_UUID, &constants.OID_OPERATOR_NOT_EQUAL_UUID, &constants.OID_FUNCTION_NOT_EQUAL_UUID);
390 CheckOid(OID_OPERATOR_NOT_EQUAL_UUID);
391 CheckOid(OID_FUNCTION_NOT_EQUAL_UUID);
392
393 /** @brief Look up the OID of provenance_gate enum value @p x and store it in constants. */
394 #define GET_GATE_TYPE_OID(x) { \
395 constants.GATE_TYPE_TO_OID[gate_ ## x] = get_enum_oid( \
396 constants.OID_TYPE_GATE_TYPE, \
397 #x); \
398 if(constants.GATE_TYPE_TO_OID[gate_ ## x]==InvalidOid) \
399 provsql_error("Could not initialize provsql gate type " #x); }
400
401 GET_GATE_TYPE_OID(input);
402 GET_GATE_TYPE_OID(plus);
403 GET_GATE_TYPE_OID(times);
404 GET_GATE_TYPE_OID(monus);
405 GET_GATE_TYPE_OID(project);
406 GET_GATE_TYPE_OID(zero);
410 GET_GATE_TYPE_OID(semimod);
412 GET_GATE_TYPE_OID(delta);
413 GET_GATE_TYPE_OID(value);
414 GET_GATE_TYPE_OID(mulinput);
415 GET_GATE_TYPE_OID(update);
416
417 constants.ok=true;
418
419 return constants;
420}
421
422database_constants_t *constants_cache; ///< Per-database OID constants cache (sorted by database OID)
423unsigned constants_cache_len=0; ///< Number of valid entries in @c constants_cache
424
425constants_t get_constants(bool failure_if_not_possible)
426{
427 int start=0, end=constants_cache_len-1;
428 database_constants_t *constants_cache2;
429
430
431 while(end>=start) {
432 unsigned mid=(start+end)/2;
433 if(constants_cache[mid].database<MyDatabaseId)
434 start=mid+1;
435 else if(constants_cache[mid].database>MyDatabaseId)
436 end=mid-1;
437 else
438 return constants_cache[mid].constants;
439 }
440
441 constants_cache2=calloc(constants_cache_len+1, sizeof(database_constants_t));
442 for(unsigned i=0; i<start; ++i)
443 constants_cache2[i]=constants_cache[i];
444
445 constants_cache2[start].database=MyDatabaseId;
446 constants_cache2[start].constants=initialize_constants(failure_if_not_possible);
447
448 for(unsigned i=start; i<constants_cache_len; ++i)
449 constants_cache2[i+1]=constants_cache[i];
450 free(constants_cache);
451 constants_cache=constants_cache2;
453
454 return constants_cache[start].constants;
455}
456
457PG_FUNCTION_INFO_V1(reset_constants_cache);
458/**
459 * @brief SQL function to invalidate the OID constants cache.
460 *
461 * Forces a fresh OID lookup for the current database on the next call to
462 * @c get_constants(). Must be called after @c ALTER EXTENSION provsql
463 * UPDATE to ensure cached OIDs are refreshed.
464 * @return Void datum.
465 */
466Datum reset_constants_cache(PG_FUNCTION_ARGS)
467{
468 int start=0, end=constants_cache_len-1;
469
470 while(end>=start) {
471 unsigned mid=(start+end)/2;
472 if(constants_cache[mid].database<MyDatabaseId)
473 start=mid+1;
474 else if(constants_cache[mid].database>MyDatabaseId)
475 end=mid-1;
476 else {
478 break;
479 }
480 }
481
482 PG_RETURN_VOID();
483}
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 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 Oid get_provsql_func_oid(char *s)
Return the OID of a provsql-schema function named s.
database_constants_t * constants_cache
Per-database OID constants cache (sorted by database OID)
constants_t get_constants(bool failure_if_not_possible)
Retrieve the cached OID constants for the current database.
unsigned constants_cache_len
Number of valid entries in constants_cache.
static Oid get_func_oid(char *s)
Return the OID of a globally qualified function named s.
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 constants_t initialize_constants(bool failure_if_not_possible)
Query the system catalogs to populate a fresh constants_t.
Core types, constants, and utilities shared across ProvSQL.
Structure to store the value of various constants.
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_PROVENANCE
OID of the provenance FUNCTION.
Oid OID_FUNCTION_AGG_TOKEN_UUID
OID of the agg_token_uuid FUNCTION.
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_TYPE_GATE_TYPE
OID of the provenance_gate TYPE.
Oid OID_FUNCTION_PROVENANCE_PROJECT
OID of the provenance_project FUNCTION.
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 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.
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_PROVENANCE_TIMES
OID of the provenance_times FUNCTION.
Oid OID_FUNCTION_PROVENANCE_MONUS
OID of the provenance_monus FUNCTION.
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_GATE_ONE
OID of the provenance_one FUNCTION.
Oid OID_TYPE_UUID_ARRAY
OID of the uuid[] TYPE.
Oid OID_FUNCTION_PROVENANCE_CMP
OID of the provenance_cmp FUNCTION.
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.