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);
91 BackgroundWorkerUnblockSignals();
95 provsql_log(
"%s initialized", MyBgworkerEntry->bgw_name);
104 BackgroundWorker worker;
106 snprintf(worker.bgw_name, BGW_MAXLEN,
"ProvSQL MMap Worker");
107#if PG_VERSION_NUM >= 110000
108 snprintf(worker.bgw_type, BGW_MAXLEN,
"ProvSQL MMap");
111 worker.bgw_flags = BGWORKER_SHMEM_ACCESS;
112 worker.bgw_start_time = BgWorkerStart_PostmasterStart;
113 worker.bgw_restart_time = 1;
115 snprintf(worker.bgw_library_name, BGW_MAXLEN,
"provsql");
116 snprintf(worker.bgw_function_name, BGW_MAXLEN,
"provsql_mmap_worker");
117#if PG_VERSION_NUM < 100000
118 worker.bgw_main = NULL;
121 worker.bgw_main_arg = (Datum) 0;
122 worker.bgw_notify_pid = 0;
124 RegisterBackgroundWorker(&worker);
143 pg_uuid_t *token = DatumGetUUIDP(PG_GETARG_DATUM(0));
146 unsigned nb_children = 0;
166 provsql_error(
"Cannot communicate on pipe (message type t)");
180 provsql_error(
"Cannot communicate on pipe (message type c during get_gate_type)");
183 if(nb_children > 0) {
184 children = calloc(nb_children,
sizeof(
pg_uuid_t));
187 provsql_error(
"Cannot read children from pipe (during get_gate_type)");
202 if(children) free(children);
215 unsigned nb_children,
234#ifdef PROVSQL_INPROCESS_STORE
244 for(
unsigned i=0; i<nb_children; ++i)
254#ifndef PROVSQL_INPROCESS_STORE
258 unsigned children_per_batch = PIPE_BUF/
sizeof(
pg_uuid_t);
267 for(
unsigned j=0; j<1+(nb_children-1)/children_per_batch; ++j) {
270 for(
unsigned i=j*children_per_batch; i<(j+1)*children_per_batch && i<nb_children; ++i) {
330 pg_uuid_t *token = DatumGetUUIDP(PG_GETARG_DATUM(0));
331 Oid oid_type = PG_GETARG_INT32(1);
332 ArrayType *children = PG_ARGISNULL(2)?NULL:PG_GETARG_ARRAYTYPE_P(2);
333 unsigned nb_children = 0;
338 if(PG_ARGISNULL(0) || PG_ARGISNULL(1))
342 if(ARR_NDIM(children) > 1)
343 provsql_error(
"Invalid multi-dimensional array passed to create_gate");
344 else if(ARR_NDIM(children) == 1)
345 nb_children = *ARR_DIMS(children);
361 children_data = (
pg_uuid_t*) ARR_DATA_PTR(children);
363 children_data = NULL;
374 pg_uuid_t *token = DatumGetUUIDP(PG_GETARG_DATUM(0));
375 double prob = PG_GETARG_FLOAT8(1);
377 if(PG_ARGISNULL(0) || PG_ARGISNULL(1))
390 pg_uuid_t *token = DatumGetUUIDP(PG_GETARG_DATUM(0));
391 unsigned info1 = PG_GETARG_INT32(1);
392 unsigned info2 = PG_GETARG_INT32(2);
408 unsigned len=strlen(str);
416#ifdef PROVSQL_INPROCESS_STORE
435 pg_uuid_t *token = DatumGetUUIDP(PG_GETARG_DATUM(0));
436 text *data = PG_GETARG_TEXT_P(1);
437 char *str=text_to_cstring(data);
449 pg_uuid_t *token = DatumGetUUIDP(PG_GETARG_DATUM(0));
465 provsql_error(
"Cannot communicate with pipe (message type e)");
468 result = palloc(len + VARHDRSZ);
469 SET_VARSIZE(result, VARHDRSZ + len);
473 provsql_error(
"Cannot communicate with pipe (message type e)");
478 PG_RETURN_TEXT_P(result);
495 provsql_error(
"Cannot communicate with pipe (message type n)");
500 PG_RETURN_INT64((
long) nb);
507 pg_uuid_t *token = DatumGetUUIDP(PG_GETARG_DATUM(0));
508 ArrayType *result = NULL;
509 unsigned nb_children;
532 if(!
READB(nb_children,
unsigned)) {
534 provsql_error(
"Cannot read response from pipe (message type c)");
537 children=calloc(nb_children,
sizeof(
pg_uuid_t));
553 children_ptr = palloc(nb_children *
sizeof(Datum));
554 for(
unsigned i=0; i<nb_children; ++i)
555 children_ptr[i] = UUIDPGetDatum(&children[i]);
558 result = construct_array(
568 PG_RETURN_ARRAYTYPE_P(result);
575 pg_uuid_t *token = DatumGetUUIDP(PG_GETARG_DATUM(0));
590 provsql_error(
"Cannot communicate with pipe (message type p)");
598 PG_RETURN_FLOAT8(result);
607 provsql_error(
"set_table_info: unknown table kind '%s' (expected "
608 "'tid', 'bid', or 'opaque')", label);
620 provsql_error(
"get_table_info: unknown table kind value %u", kind);
641 ArrayType *block_key;
642 uint16 block_key_n = 0;
643 int16 *block_key_data = NULL;
646 if(PG_ARGISNULL(0) || PG_ARGISNULL(1))
647 provsql_error(
"Invalid NULL value passed to set_table_info");
649 relid = PG_GETARG_OID(0);
650 kind_text = PG_GETARG_TEXT_PP(1);
651 kind_str = text_to_cstring(kind_text);
654 block_key = PG_ARGISNULL(2) ? NULL : PG_GETARG_ARRAYTYPE_P(2);
657 if(ARR_NDIM(block_key) > 1)
658 provsql_error(
"Invalid multi-dimensional array passed to set_table_info");
659 else if(ARR_NDIM(block_key) == 1)
660 block_key_n = *ARR_DIMS(block_key);
662 block_key_data = (int16 *) ARR_DATA_PTR(block_key);
666 provsql_error(
"set_table_info: block key wider than %d columns "
667 "(%u given) is not supported",
670 payload_size =
sizeof(char) +
sizeof(Oid) +
sizeof(Oid) +
sizeof(uint8)
671 +
sizeof(uint16) + block_key_n *
sizeof(int16);
672 if(payload_size > PIPE_BUF)
673 provsql_error(
"set_table_info: IPC payload exceeds PIPE_BUF");
681 for(uint16 i = 0; i < block_key_n; ++i)
700 if(SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid)))
701 CacheInvalidateRelcacheByRelid(relid);
713 provsql_error(
"Invalid NULL value passed to remove_table_info");
715 relid = PG_GETARG_OID(0);
732 if(SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid)))
733 CacheInvalidateRelcacheByRelid(relid);
763 provsql_error(
"Cannot communicate with pipe (message type s)");
768 provsql_error(
"Cannot communicate with pipe (message type s)");
772 provsql_error(
"provsql_fetch_table_info: server returned an unexpectedly wide block key");
777 provsql_error(
"Cannot communicate with pipe (message type s)");
802 bool nulls[2] = {
false,
false};
809 relid = PG_GETARG_OID(0);
814 if(get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
815 provsql_error(
"get_table_info: expected composite return type");
816 tupdesc = BlessTupleDesc(tupdesc);
822 elems[i] = Int16GetDatum(info.
block_key[i]);
823 arr = construct_array(elems, info.
block_key_n, INT2OID, 2,
true,
's');
825 values[1] = PointerGetDatum(arr);
827 PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
850 ArrayType *ancestors;
851 uint16 ancestor_n = 0;
852 Oid *ancestor_data = NULL;
858 relid = PG_GETARG_OID(0);
859 ancestors = PG_ARGISNULL(1) ? NULL : PG_GETARG_ARRAYTYPE_P(1);
862 if(ARR_NDIM(ancestors) > 1)
863 provsql_error(
"Invalid multi-dimensional array passed to set_ancestors");
864 else if(ARR_NDIM(ancestors) == 1)
865 ancestor_n = *ARR_DIMS(ancestors);
867 ancestor_data = (Oid *) ARR_DATA_PTR(ancestors);
871 provsql_error(
"set_ancestors: ancestor set wider than %d entries "
872 "(%u given) is not supported",
875 payload_size =
sizeof(char) +
sizeof(Oid) +
sizeof(Oid)
876 +
sizeof(uint16) + ancestor_n *
sizeof(Oid);
877 if(payload_size > PIPE_BUF)
878 provsql_error(
"set_ancestors: IPC payload exceeds PIPE_BUF");
885 for(uint16 i = 0; i < ancestor_n; ++i)
895 if(SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid)))
896 CacheInvalidateRelcacheByRelid(relid);
915 provsql_error(
"Invalid NULL value passed to remove_ancestors");
917 relid = PG_GETARG_OID(0);
931 if(SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid)))
932 CacheInvalidateRelcacheByRelid(relid);
969 provsql_error(
"Cannot communicate with pipe (message type a)");
972 if(!
READB(n, uint16)) {
974 provsql_error(
"Cannot communicate with pipe (message type a)");
979 "unexpectedly wide ancestor set");
981 for(uint16 i = 0; i < n; ++i)
982 if(!
READB(ancestors_out[i], Oid)) {
984 provsql_error(
"Cannot communicate with pipe (message type a)");
992 return found != 0 && n > 0;
1015 relid = PG_GETARG_OID(0);
1020 elems = palloc(ancestor_n *
sizeof(Datum));
1021 for(uint16 i = 0; i < ancestor_n; ++i)
1022 elems[i] = ObjectIdGetDatum(ancestors[i]);
1023 arr = construct_array(elems, ancestor_n, OIDOID,
1024 sizeof(Oid),
true,
'i');
1027 PG_RETURN_ARRAYTYPE_P(arr);
1034 pg_uuid_t *token = DatumGetUUIDP(PG_GETARG_DATUM(0));
1035 unsigned info1 =0, info2 = 0;
1049 provsql_error(
"Cannot communicate with pipe (message type i)");
1057 bool nulls[2] = {
false,
false};
1059 get_call_result_type(fcinfo,NULL,&tupdesc);
1060 tupdesc = BlessTupleDesc(tupdesc);
1062 values[0] = Int32GetDatum(info1);
1063 values[1] = Int32GetDatum(info2);
1065 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)
PostgreSQL-callable wrapper for get_gate_type().
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)
PostgreSQL-callable wrapper for get_gate_type().
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().
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.
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.