52#include "access/htup_details.h"
53#include "catalog/pg_database.h"
54#include "commands/dbcommands.h"
55#include "executor/spi.h"
59#include "storage/lmgr.h"
60#include "storage/procarray.h"
61#include "utils/builtins.h"
63#include "utils/lsyscache.h"
64#include "utils/uuid.h"
81 int64 newcap = rs->
cap ? rs->
cap * 2 : 1024;
96 int rc = SPI_execute(query,
true, 0);
97 if(rc != SPI_OK_SELECT)
98 provsql_error(
"circuit_cleanup: cannot collect roots (SPI code %d)", rc);
100 for(uint64 i = 0; i < SPI_processed; ++i) {
102 Datum d = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc,
120 int save_nestlevel = NewGUCNestLevel();
123 SetConfigOption(
"provsql.active",
"off",
124 PGC_USERSET, PGC_S_SESSION);
134 for(
int i = 0; i < 3; ++i)
136 DirectFunctionCall1(uuid_in,
137 CStringGetDatum(constants[i]))));
141 "SELECT c.oid::regclass::text AS rel, quote_ident(a.attname) AS col, "
142 " t.typelem <> 0 AND t.typlen = -1 AS is_array "
143 " FROM pg_attribute a "
144 " JOIN pg_class c ON c.oid = a.attrelid "
145 " JOIN pg_namespace n ON n.oid = c.relnamespace "
146 " JOIN pg_type t ON t.oid = a.atttypid "
147 " WHERE c.relkind IN ('r', 'm', 'p') "
148 " AND a.attnum > 0 AND NOT a.attisdropped "
149 " AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') "
150 " AND (CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN t.typelem "
151 " ELSE t.oid END) IN ("
152 " 'uuid'::regtype, 'provsql.agg_token'::regtype, "
153 " 'provsql.random_variable'::regtype) "
154 " ORDER BY 1, 2",
true, 0);
155 if(rc != SPI_OK_SELECT)
156 provsql_error(
"circuit_cleanup: cannot enumerate token columns "
157 "(SPI code %d)", rc);
160 uint64 n = SPI_processed;
161 char **rels = palloc(n *
sizeof(
char *));
162 char **cols = palloc(n *
sizeof(
char *));
163 bool *arrs = palloc(n *
sizeof(
bool));
165 for(uint64 i = 0; i < n; ++i) {
166 rels[i] = SPI_getvalue(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1);
167 cols[i] = SPI_getvalue(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2);
168 arrs[i] = (strcmp(SPI_getvalue(SPI_tuptable->vals[i],
169 SPI_tuptable->tupdesc, 3),
"t") == 0);
172 for(uint64 i = 0; i < n; ++i) {
174 initStringInfo(&buf);
176 appendStringInfo(&buf,
177 "SELECT DISTINCT u::uuid FROM %s, "
178 "LATERAL unnest(%s) AS u WHERE u IS NOT NULL",
181 appendStringInfo(&buf,
182 "SELECT DISTINCT %s::uuid FROM %s WHERE %s IS NOT NULL",
183 cols[i], rels[i], cols[i]);
193 "SELECT count(*) FROM pg_attribute a "
194 " JOIN pg_class c ON c.oid = a.attrelid "
195 " JOIN pg_type t ON t.oid = a.atttypid "
196 " WHERE c.relkind = 'f' AND a.attnum > 0 AND NOT a.attisdropped "
197 " AND (CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN t.typelem "
198 " ELSE t.oid END) IN ("
199 " 'uuid'::regtype, 'provsql.agg_token'::regtype, "
200 " 'provsql.random_variable'::regtype)",
true, 0);
201 if(rc == SPI_OK_SELECT && SPI_processed == 1) {
202 char *cnt = SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1);
203 if(cnt && strcmp(cnt,
"0") != 0)
204 provsql_notice(
"circuit_cleanup: %s token-typed column(s) live on "
205 "foreign tables and were not scanned; gates only they "
206 "reference are removed", cnt);
211 AtEOXact_GUC(
false, save_nestlevel);
216 AtEOXact_GUC(
false, save_nestlevel);
230 bool dry_run = PG_ARGISNULL(0) ? false : PG_GETARG_BOOL(0);
236 bool nulls[6] = {
false,
false,
false,
false,
false,
false};
238 if(RecoveryInProgress())
240 (errmsg(
"provsql.circuit_cleanup() cannot run on a standby"),
241 errdetail(
"The circuit store is not replicated, and a standby "
242 "must not write to it.")));
246 (errmsg(
"provsql.circuit_cleanup() cannot run in a transaction "
247 "that has already written to the circuit store"),
248 errhint(
"Run it as the first statement of its own transaction.")));
253 LockSharedObject(DatabaseRelationId, MyDatabaseId, 0, AccessExclusiveLock);
260 if(CountOtherDBBackends(MyDatabaseId, &nbackends, &nprepared))
262 (errcode(ERRCODE_OBJECT_IN_USE),
263 errmsg(
"database \"%s\" is being accessed by other users",
264 get_database_name(MyDatabaseId)),
265 errdetail(
"There %s %d other session%s and %d prepared "
266 "transaction%s using the database.",
267 nbackends == 1 ?
"is" :
"are", nbackends,
268 nbackends == 1 ?
"" :
"s", nprepared,
269 nprepared == 1 ?
"" :
"s")));
280 rs.
tokens = MemoryContextAlloc(CurrentMemoryContext,
284 if(SPI_connect() != SPI_OK_CONNECT)
291 if(get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
292 provsql_error(
"circuit_cleanup: expected composite return type");
293 tupdesc = BlessTupleDesc(tupdesc);
296 values[1] = Int64GetDatum((int64) res.
gates_after);
298 values[3] = Int64GetDatum((int64) res.
wires_after);
300 values[5] = Int64GetDatum((int64) res.
extra_after);
308 PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
C-linkage interface to the in-process provenance circuit cache.
void circuit_cache_reset(void)
Forget every cached gate.
static void collect_roots(root_set *rs)
Gather every token stored in the database.
Datum circuit_cleanup(PG_FUNCTION_ARGS)
Rebuild this database's circuit store, keeping only what the tokens stored in the database reach.
static void root_set_add(root_set *rs, const pg_uuid_t *token)
static void collect_from(root_set *rs, const char *query)
Run query and append every UUID it returns to rs.
void provsql_gate_builders_forget(void)
Forget what gate_builders.c remembers of the store (planted gates, value gates written).
#define provsql_error(fmt,...)
Report a fatal ProvSQL error and abort the current transaction.
#define provsql_notice(fmt,...)
Emit a ProvSQL informational notice (execution continues).
bool provsql_store_written(void)
Whether this transaction has written to the circuit store.
void provsql_circuit_cleanup_request(bool dry_run, const pg_uuid_t *roots, int64 nb_roots, provsql_cleanup_result *out)
Ask the worker to rebuild this database's store, keeping only what roots reach.
Background worker and IPC primitives for mmap-backed circuit storage.
Shared-memory segment and inter-process pipe management.
Core types, constants, and utilities shared across ProvSQL.
#define PROVSQL_GATE_ZERO_UUID
UUID of the semiring zero gate: the result of the SQL function gate_zero(), created with the extensio...
#define PROVSQL_GATE_ONE_UUID
UUID of the semiring one gate, the result of gate_one().
#define PROVSQL_GATE_NULL_UUID
UUID of the constant value gate standing for the NULL value, the result of gate_null().
What provsql.circuit_cleanup() reports.
uint64 wires_before
Child wires before.
uint64 wires_after
Child wires kept.
uint64 extra_before
Annotation bytes before.
uint64 gates_after
Gate records kept.
uint64 extra_after
Annotation bytes kept.
uint64 gates_before
Gate records before.
Growable array of root tokens gathered from the database.