36#include "postmaster/bgworker.h"
37#include "catalog/pg_type.h"
41#include "utils/array.h"
42#include "access/htup_details.h"
43#include "utils/builtins.h"
44#include "utils/inval.h"
45#include "utils/syscache.h"
49#ifdef PROVSQL_INPROCESS_STORE
55void provsql_buffer_ensure(
size_t need)
57 if(need > buffercap) {
58 size_t newcap = buffercap ? buffercap * 2 : 4096;
63 provsql_error(
"ProvSQL: out of memory growing the IPC buffer");
79 while(remaining > 0) {
80 ssize_t r = read(fd, p, remaining);
89#if PG_VERSION_NUM >= 190000
98static void provsql_worker_die(SIGNAL_ARGS)
101 (errcode(ERRCODE_ADMIN_SHUTDOWN),
102 errmsg(
"terminating background worker \"%s\" due to administrator command",
103 MyBgworkerEntry->bgw_type)));
109#if PG_VERSION_NUM >= 190000
110 pqsignal(SIGTERM, provsql_worker_die);
112 BackgroundWorkerUnblockSignals();
116 provsql_log(
"%s initialized", MyBgworkerEntry->bgw_name);
125 BackgroundWorker worker;
127 snprintf(worker.bgw_name, BGW_MAXLEN,
"ProvSQL MMap Worker");
128#if PG_VERSION_NUM >= 110000
129 snprintf(worker.bgw_type, BGW_MAXLEN,
"ProvSQL MMap");
132 worker.bgw_flags = BGWORKER_SHMEM_ACCESS;
133 worker.bgw_start_time = BgWorkerStart_PostmasterStart;
134 worker.bgw_restart_time = 1;
136 snprintf(worker.bgw_library_name, BGW_MAXLEN,
"provsql");
137 snprintf(worker.bgw_function_name, BGW_MAXLEN,
"provsql_mmap_worker");
138#if PG_VERSION_NUM < 100000
139 worker.bgw_main = NULL;
142 worker.bgw_main_arg = (Datum) 0;
143 worker.bgw_notify_pid = 0;
145 RegisterBackgroundWorker(&worker);
169 unsigned *nb_children_out,
173 unsigned nb_children = 0;
192 provsql_error(
"Cannot communicate on pipe (message type t)");
206 provsql_error(
"Cannot communicate on pipe (message type c during get_gate_type)");
209 if(nb_children > 0) {
210 children = calloc(nb_children,
sizeof(
pg_uuid_t));
213 provsql_error(
"Cannot read children from pipe (during get_gate_type)");
228 *nb_children_out = nb_children;
229 *children_out = children;
235 pg_uuid_t *token = DatumGetUUIDP(PG_GETARG_DATUM(0));
238 unsigned nb_children = 0;
245 if(children) free(children);
258 unsigned nb_children,
277#ifdef PROVSQL_INPROCESS_STORE
287 for(
unsigned i=0; i<nb_children; ++i)
297#ifndef PROVSQL_INPROCESS_STORE
301 unsigned children_per_batch = PIPE_BUF/
sizeof(
pg_uuid_t);
310 for(
unsigned j=0; j<1+(nb_children-1)/children_per_batch; ++j) {
313 for(
unsigned i=j*children_per_batch; i<(j+1)*children_per_batch && i<nb_children; ++i) {
373 pg_uuid_t *token = DatumGetUUIDP(PG_GETARG_DATUM(0));
374 Oid oid_type = PG_GETARG_INT32(1);
375 ArrayType *children = PG_ARGISNULL(2)?NULL:PG_GETARG_ARRAYTYPE_P(2);
376 unsigned nb_children = 0;
381 if(PG_ARGISNULL(0) || PG_ARGISNULL(1))
385 if(ARR_NDIM(children) > 1)
386 provsql_error(
"Invalid multi-dimensional array passed to create_gate");
387 if(array_contains_nulls(children))
388 provsql_error(
"create_gate: children array must not contain NULL "
389 "elements (filter them out before calling)");
390 if(ARR_NDIM(children) == 1)
391 nb_children = *ARR_DIMS(children);
407 children_data = (
pg_uuid_t*) ARR_DATA_PTR(children);
409 children_data = NULL;
427 pg_uuid_t *token = DatumGetUUIDP(PG_GETARG_DATUM(0));
428 double prob = PG_GETARG_FLOAT8(1);
431 if(PG_ARGISNULL(0) || PG_ARGISNULL(1))
435 unsigned nb_children = 0;
439 if(children) free(children);
442 peeled = children[0];
457 pg_uuid_t *token = DatumGetUUIDP(PG_GETARG_DATUM(0));
458 unsigned info1 = PG_GETARG_INT32(1);
459 unsigned info2 = PG_GETARG_INT32(2);
475 unsigned len=strlen(str);
483#ifdef PROVSQL_INPROCESS_STORE
502 pg_uuid_t *token = DatumGetUUIDP(PG_GETARG_DATUM(0));
503 text *data = PG_GETARG_TEXT_P(1);
504 char *str=text_to_cstring(data);
516 pg_uuid_t *token = DatumGetUUIDP(PG_GETARG_DATUM(0));
532 provsql_error(
"Cannot communicate with pipe (message type e)");
535 result = palloc(len + VARHDRSZ);
536 SET_VARSIZE(result, VARHDRSZ + len);
540 provsql_error(
"Cannot communicate with pipe (message type e)");
545 PG_RETURN_TEXT_P(result);
562 provsql_error(
"Cannot communicate with pipe (message type n)");
567 PG_RETURN_INT64((
long) nb);
574 pg_uuid_t *token = DatumGetUUIDP(PG_GETARG_DATUM(0));
575 ArrayType *result = NULL;
576 unsigned nb_children;
599 if(!
READB(nb_children,
unsigned)) {
601 provsql_error(
"Cannot read response from pipe (message type c)");
604 children=calloc(nb_children,
sizeof(
pg_uuid_t));
620 children_ptr = palloc(nb_children *
sizeof(Datum));
621 for(
unsigned i=0; i<nb_children; ++i)
622 children_ptr[i] = UUIDPGetDatum(&children[i]);
625 result = construct_array(
635 PG_RETURN_ARRAYTYPE_P(result);
642 pg_uuid_t *token = DatumGetUUIDP(PG_GETARG_DATUM(0));
657 provsql_error(
"Cannot communicate with pipe (message type p)");
665 PG_RETURN_FLOAT8(result);
674 provsql_error(
"set_table_info: unknown table kind '%s' (expected "
675 "'tid', 'bid', or 'opaque')", label);
687 provsql_error(
"get_table_info: unknown table kind value %u", kind);
708 ArrayType *block_key;
709 uint16 block_key_n = 0;
710 int16 *block_key_data = NULL;
713 if(PG_ARGISNULL(0) || PG_ARGISNULL(1))
714 provsql_error(
"Invalid NULL value passed to set_table_info");
716 relid = PG_GETARG_OID(0);
717 kind_text = PG_GETARG_TEXT_PP(1);
718 kind_str = text_to_cstring(kind_text);
721 block_key = PG_ARGISNULL(2) ? NULL : PG_GETARG_ARRAYTYPE_P(2);
724 if(ARR_NDIM(block_key) > 1)
725 provsql_error(
"Invalid multi-dimensional array passed to set_table_info");
726 else if(ARR_NDIM(block_key) == 1)
727 block_key_n = *ARR_DIMS(block_key);
729 block_key_data = (int16 *) ARR_DATA_PTR(block_key);
733 provsql_error(
"set_table_info: block key wider than %d columns "
734 "(%u given) is not supported",
737 payload_size =
sizeof(char) +
sizeof(Oid) +
sizeof(Oid) +
sizeof(uint8)
738 +
sizeof(uint16) + block_key_n *
sizeof(int16);
739 if(payload_size > PIPE_BUF)
740 provsql_error(
"set_table_info: IPC payload exceeds PIPE_BUF");
748 for(uint16 i = 0; i < block_key_n; ++i)
767 if(SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid)))
768 CacheInvalidateRelcacheByRelid(relid);
780 provsql_error(
"Invalid NULL value passed to remove_table_info");
782 relid = PG_GETARG_OID(0);
799 if(SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid)))
800 CacheInvalidateRelcacheByRelid(relid);
830 provsql_error(
"Cannot communicate with pipe (message type s)");
835 provsql_error(
"Cannot communicate with pipe (message type s)");
839 provsql_error(
"provsql_fetch_table_info: server returned an unexpectedly wide block key");
844 provsql_error(
"Cannot communicate with pipe (message type s)");
869 bool nulls[2] = {
false,
false};
876 relid = PG_GETARG_OID(0);
881 if(get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
882 provsql_error(
"get_table_info: expected composite return type");
883 tupdesc = BlessTupleDesc(tupdesc);
889 elems[i] = Int16GetDatum(info.
block_key[i]);
890 arr = construct_array(elems, info.
block_key_n, INT2OID, 2,
true,
's');
892 values[1] = PointerGetDatum(arr);
894 PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
917 ArrayType *ancestors;
918 uint16 ancestor_n = 0;
919 Oid *ancestor_data = NULL;
925 relid = PG_GETARG_OID(0);
926 ancestors = PG_ARGISNULL(1) ? NULL : PG_GETARG_ARRAYTYPE_P(1);
929 if(ARR_NDIM(ancestors) > 1)
930 provsql_error(
"Invalid multi-dimensional array passed to set_ancestors");
931 else if(ARR_NDIM(ancestors) == 1)
932 ancestor_n = *ARR_DIMS(ancestors);
934 ancestor_data = (Oid *) ARR_DATA_PTR(ancestors);
938 provsql_error(
"set_ancestors: ancestor set wider than %d entries "
939 "(%u given) is not supported",
942 payload_size =
sizeof(char) +
sizeof(Oid) +
sizeof(Oid)
943 +
sizeof(uint16) + ancestor_n *
sizeof(Oid);
944 if(payload_size > PIPE_BUF)
945 provsql_error(
"set_ancestors: IPC payload exceeds PIPE_BUF");
952 for(uint16 i = 0; i < ancestor_n; ++i)
962 if(SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid)))
963 CacheInvalidateRelcacheByRelid(relid);
982 provsql_error(
"Invalid NULL value passed to remove_ancestors");
984 relid = PG_GETARG_OID(0);
998 if(SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid)))
999 CacheInvalidateRelcacheByRelid(relid);
1036 provsql_error(
"Cannot communicate with pipe (message type a)");
1039 if(!
READB(n, uint16)) {
1041 provsql_error(
"Cannot communicate with pipe (message type a)");
1046 "unexpectedly wide ancestor set");
1048 for(uint16 i = 0; i < n; ++i)
1049 if(!
READB(ancestors_out[i], Oid)) {
1051 provsql_error(
"Cannot communicate with pipe (message type a)");
1056 *ancestor_n_out = n;
1059 return found != 0 && n > 0;
1082 relid = PG_GETARG_OID(0);
1087 elems = palloc(ancestor_n *
sizeof(Datum));
1088 for(uint16 i = 0; i < ancestor_n; ++i)
1089 elems[i] = ObjectIdGetDatum(ancestors[i]);
1090 arr = construct_array(elems, ancestor_n, OIDOID,
1091 sizeof(Oid),
true,
'i');
1094 PG_RETURN_ARRAYTYPE_P(arr);
1101 pg_uuid_t *token = DatumGetUUIDP(PG_GETARG_DATUM(0));
1102 unsigned info1 =0, info2 = 0;
1116 provsql_error(
"Cannot communicate with pipe (message type i)");
1124 bool nulls[2] = {
false,
false};
1126 get_call_result_type(fcinfo,NULL,&tupdesc);
1127 tupdesc = BlessTupleDesc(tupdesc);
1129 values[0] = Int32GetDatum(info1);
1130 values[1] = Int32GetDatum(info2);
1132 PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
void destroy_provsql_mmap()
Unmap and close the mmap files.
void provsql_mmap_main_loop()
Main processing loop of the mmap background worker.
void initialize_provsql_mmap()
Open (or create) the mmap files and initialise the circuit store.
Per-table provenance metadata persisted alongside the circuit store.
#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.
C-linkage interface to the in-process provenance circuit cache.
gate_type circuit_cache_get_type(pg_uuid_t token)
Retrieve the type of a cached gate.
unsigned circuit_cache_get_children(pg_uuid_t token, pg_uuid_t **children)
Retrieve the children of a cached gate.
bool circuit_cache_create_gate(pg_uuid_t token, gate_type type, unsigned nb_children, const pg_uuid_t *children)
Insert a new gate into the circuit cache.
Datum get_gate_type(PG_FUNCTION_ARGS)
Datum set_ancestors(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for setTableAncestry() over the IPC pipe.
Datum set_table_info(PG_FUNCTION_ARGS)
Forward declaration of the C SQL entry points.
#define provsql_error(fmt,...)
Report a fatal ProvSQL error and abort the current transaction.
#define provsql_log(fmt,...)
Write a ProvSQL message to the server log only.
void provsql_internal_set_extra(const pg_uuid_t *token, const char *str)
Internal entry point behind set_extra(): worker IPC only.
Datum get_infos(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for get_infos().
void provsql_mmap_worker(Datum ignored)
Entry point for the ProvSQL mmap background worker.
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.
Datum set_ancestors(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for setTableAncestry() over the IPC pipe.
bool provsql_read_all(int fd, void *dst, size_t n)
Read exactly n bytes from fd into dst; false on EOF/error.
static const char * table_kind_label(uint8_t kind)
Inverse of parse_table_kind for use by get_table_info.
Datum set_table_info(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for setTableInfo() over the IPC pipe.
Datum get_table_info(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper around the cached table-info lookup.
Datum remove_ancestors(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for removeTableAncestry() over the IPC pipe.
Datum set_extra(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for set_extra().
Datum get_nb_gates(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for get_nb_gates().
Datum create_gate(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for create_gate().
Datum get_ancestors(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper around the cached ancestry lookup.
static uint8_t parse_table_kind(const char *label)
Translate a SQL-side kind label into the persisted enum value.
Datum remove_table_info(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for removeTableInfo() over the IPC pipe.
Datum set_infos(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for set_infos().
bool provsql_fetch_table_info(Oid relid, ProvenanceTableInfo *out)
C-callable IPC fetch for per-table provenance metadata.
bool provsql_internal_set_prob(const pg_uuid_t *token, double prob)
Internal entry point behind set_prob(): worker IPC only.
Datum get_gate_type(PG_FUNCTION_ARGS)
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.
char buffer[PIPE_BUF]
Shared write buffer used with STARTWRITEM / ADDWRITEM / SENDWRITEM.
void provsql_internal_set_infos(const pg_uuid_t *token, unsigned info1, unsigned info2)
Internal entry point behind set_infos(): worker IPC only.
Datum get_children(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for get_children().
Datum get_extra(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for get_extra().
static 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().
Datum get_prob(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for get_prob().
unsigned bufferpos
Current write position within buffer.
void RegisterProvSQLMMapWorker(void)
Register the ProvSQL mmap background worker with PostgreSQL.
Datum set_prob(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for set_prob().
Background worker and IPC primitives for mmap-backed circuit storage.
#define READB_BYTES(ptr, n)
Read exactly n bytes of a reply from the main-to-background pipe.
#define READB(var, type)
Read one value of type from the main-to-background pipe.
#define STARTWRITEM()
Reset the shared write buffer for a new batched write.
#define ADDWRITEM(pvar, type)
Append one value of type to the shared write buffer.
#define SENDWRITEM()
Flush the shared write buffer to the background-to-main pipe atomically.
void provsql_shmem_unlock(void)
Release the ProvSQL LWLock.
void provsql_shmem_lock_exclusive(void)
Acquire the ProvSQL LWLock in exclusive mode.
provsqlSharedState * provsql_shared_state
Pointer to the ProvSQL shared-memory segment (set in provsql_shmem_startup).
void provsql_shmem_lock_shared(void)
Acquire the ProvSQL LWLock in shared mode.
Shared-memory segment and inter-process pipe management.
bool provsql_lookup_ancestry(Oid relid, uint16 *ancestor_n_out, Oid *ancestors_out)
Look up the base-ancestor set of a tracked relation.
bool provsql_lookup_table_info(Oid relid, ProvenanceTableInfo *out)
Look up per-table provenance metadata with a backend-local cache.
constants_t get_constants(bool failure_if_not_possible)
Retrieve the cached OID constants for the current database.
Core types, constants, and utilities shared across ProvSQL.
@ gate_annotation
Transparent single-child wrapper carrying a query-level annotation in extra (inversion-free certifica...
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.
Structure to store the value of various constants.
Oid GATE_TYPE_TO_OID[nb_gate_types]
Array of the OID of each provenance_gate ENUM value.
Oid OID_TYPE_UUID
OID of the uuid TYPE.