19SET search_path
TO provsql;
27CREATE TYPE PROVENANCE_GATE
AS
112CREATE OR REPLACE
FUNCTION create_gate(
114 type PROVENANCE_GATE,
115 children UUID[]
DEFAULT NULL)
117 'provsql',
'create_gate' LANGUAGE C PARALLEL SAFE;
124CREATE OR REPLACE
FUNCTION get_gate_type(
126 RETURNS PROVENANCE_GATE
AS
127 'provsql',
'get_gate_type' LANGUAGE C IMMUTABLE PARALLEL SAFE;
129CREATE OR REPLACE
FUNCTION get_children(
132 'provsql',
'get_children' LANGUAGE C IMMUTABLE PARALLEL SAFE;
139CREATE OR REPLACE
FUNCTION set_prob(
140 token UUID, p
DOUBLE PRECISION)
142 'provsql',
'set_prob' LANGUAGE C PARALLEL SAFE;
144CREATE OR REPLACE
FUNCTION get_prob(
146 RETURNS DOUBLE PRECISION AS
147 'provsql',
'get_prob' LANGUAGE C STABLE PARALLEL SAFE;
165CREATE OR REPLACE
FUNCTION set_infos(
166 token UUID, info1
INT, info2
INT DEFAULT NULL)
168 'provsql',
'set_infos' LANGUAGE C PARALLEL SAFE;
171CREATE OR REPLACE
FUNCTION get_infos(
172 token UUID,
OUT info1
INT,
OUT info2
INT)
174 'provsql',
'get_infos' LANGUAGE C STABLE PARALLEL SAFE;
200CREATE OR REPLACE
FUNCTION provenance_assume(token UUID, assumption TEXT)
206 IF token
IS NULL THEN
209 IF assumption
NOT IN (
'BOOLEAN',
'absorptive')
THEN
210 RAISE EXCEPTION
'provenance_assume: unknown assumption %', assumption;
212 wrapped := public.uuid_generate_v5(uuid_ns_provsql(),
213 concat(
'assumed', assumption, token));
214 PERFORM create_gate(wrapped,
'assumed',
ARRAY[token]);
215 PERFORM set_extra(wrapped, assumption);
218$$
LANGUAGE plpgsql
SET search_path=provsql,pg_temp,public
219 SECURITY DEFINER PARALLEL SAFE;
233CREATE OR REPLACE
FUNCTION assume_boolean(token UUID)
RETURNS UUID
AS
238 wrapped := provenance_assume(token,
'BOOLEAN');
239 IF wrapped
IS NOT NULL THEN
241 PERFORM set_infos(wrapped,
1,
0);
245$$
LANGUAGE plpgsql
SET search_path=provsql,pg_temp,public
246 SECURITY DEFINER PARALLEL SAFE;
261CREATE OR REPLACE
FUNCTION annotate(token UUID, extra TEXT)
RETURNS UUID
AS
266 IF token
IS NULL THEN
269 annotated := public.uuid_generate_v5(uuid_ns_provsql(),
270 concat(
'annotation', token, extra));
271 PERFORM create_gate(annotated,
'annotation',
ARRAY[token]);
272 PERFORM set_extra(annotated, extra);
275$$
LANGUAGE plpgsql
SET search_path=provsql,pg_temp,public
276 SECURITY DEFINER PARALLEL SAFE;
289CREATE OR REPLACE
FUNCTION strip_annotations(token UUID)
RETURNS UUID
AS
291WITH RECURSIVE peel(g)
AS (
294 SELECT (provsql.get_children(p.g))[
1]
FROM peel p
295 WHERE provsql.get_gate_type(p.g) =
'annotation'
297SELECT g
FROM peel
WHERE provsql.get_gate_type(g) <>
'annotation' LIMIT
1;
298$$
LANGUAGE sql STABLE PARALLEL SAFE;
327CREATE OR REPLACE
FUNCTION cond(target UUID, evidence UUID)
RETURNS UUID
AS
337 IF evidence
IS NULL OR evidence = gate_one()
THEN
342 tgt := coalesce(target, gate_one());
344 IF get_gate_type(tgt) =
'conditioned' THEN
348 ch := get_children(tgt);
350 ev := provenance_times(ch[
2], evidence);
351 jnt := provenance_times(ch[
3], evidence);
354 jnt := provenance_times(tgt, evidence);
357 result := public.uuid_generate_v5(uuid_ns_provsql(),
358 concat(
'conditioned', tgt, ev, jnt));
359 PERFORM create_gate(
result,
'conditioned',
ARRAY[tgt, ev, jnt]);
362$$
LANGUAGE plpgsql
SET search_path=provsql,pg_temp,public
363 SECURITY DEFINER PARALLEL SAFE;
372CREATE OPERATOR | (LEFTARG=UUID, RIGHTARG=UUID,
PROCEDURE=cond);
382CREATE OR REPLACE
FUNCTION cond_predicate(target UUID, predicate
BOOLEAN)
386 RAISE EXCEPTION
'UUID | (predicate) must be rewritten by the ProvSQL '
387 'planner hook: the right operand must be a Boolean combination of '
388 'random_variable / aggregate comparisons (is provsql.active off?)';
390$$
LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
392CREATE OPERATOR | (LEFTARG=UUID, RIGHTARG=
BOOLEAN,
PROCEDURE=cond_predicate);
408CREATE OR REPLACE
FUNCTION predicate_cond_predicate(target
BOOLEAN, evidence
BOOLEAN)
412 RAISE EXCEPTION
'(predicate) | (predicate) must be rewritten by the ProvSQL '
413 'planner hook: both operands must be Boolean combinations of '
414 'random_variable / aggregate comparisons (is provsql.active off?)';
416$$
LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
418CREATE OPERATOR | (LEFTARG=
BOOLEAN, RIGHTARG=
BOOLEAN,
PROCEDURE=predicate_cond_predicate);
432CREATE OR REPLACE
FUNCTION regular_indicator(cond
BOOLEAN)
RETURNS UUID
AS
434 SELECT CASE WHEN cond
THEN provsql.gate_one()
ELSE provsql.gate_zero()
END;
435$$
LANGUAGE sql IMMUTABLE PARALLEL SAFE
SET search_path=provsql,pg_temp,public;
465CREATE OR REPLACE
FUNCTION given(evidence UUID)
RETURNS UUID
AS
468 RETURN provsql.evidence_as_observation(evidence);
470$$
LANGUAGE plpgsql VOLATILE PARALLEL SAFE
471 SET search_path=provsql,pg_temp,public SECURITY DEFINER;
481CREATE OPERATOR | (RIGHTARG=UUID,
PROCEDURE=given);
499CREATE OR REPLACE
FUNCTION given(predicate
BOOLEAN)
RETURNS UUID
AS
502 RAISE EXCEPTION
'given(predicate) / prefix | (predicate) must be rewritten '
503 'by the ProvSQL planner hook: the operand must be a Boolean combination '
504 'of random_variable / aggregate comparisons (is provsql.active off?)';
506$$
LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
508CREATE OPERATOR | (RIGHTARG=
BOOLEAN,
PROCEDURE=given);
540CREATE OR REPLACE
FUNCTION provenance_not(event UUID)
RETURNS UUID
AS
542 SELECT provsql.provenance_monus(provsql.gate_one(), event);
543$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
544 SET search_path=provsql,pg_temp,public;
553CREATE OPERATOR ! (RIGHTARG=UUID,
PROCEDURE=provenance_not);
567CREATE OR REPLACE
FUNCTION inversion_free_key(root TEXT, sec TEXT, factor
INT)
569$$
SELECT 'K' || factor::TEXT ||
' '
570 || octet_length(root) ||
':' || root
571 || octet_length(sec) ||
':' || sec $$
572 LANGUAGE sql IMMUTABLE PARALLEL SAFE;
588CREATE OR REPLACE
FUNCTION set_extra(
589 token UUID, data TEXT)
591 'provsql',
'set_extra' LANGUAGE C PARALLEL SAFE STRICT;
593CREATE OR REPLACE
FUNCTION get_extra(token UUID)
595 'provsql',
'get_extra' LANGUAGE C STABLE PARALLEL SAFE
RETURNS NULL ON NULL INPUT;
604CREATE OR REPLACE
FUNCTION get_nb_gates()
RETURNS BIGINT AS
605 'provsql',
'get_nb_gates' LANGUAGE C PARALLEL SAFE;
622CREATE OR REPLACE
FUNCTION delete_statement_trigger()
632 delete_token := public.uuid_generate_v4();
634 PERFORM create_gate(delete_token,
'input');
638 FROM pg_stat_activity
639 WHERE pid = pg_backend_pid();
641 INSERT INTO delete_provenance (delete_token, query, deleted_by, deleted_at)
642 VALUES (delete_token, query_text,
current_user,
CURRENT_TIMESTAMP);
644 EXECUTE format(
'INSERT INTO %I.%I SELECT * FROM OLD_TABLE;', TG_TABLE_SCHEMA, TG_TABLE_NAME);
646 FOR r
IN (
SELECT *
FROM OLD_TABLE) LOOP
647 old_token := r.provsql;
648 new_token := provenance_monus(old_token, delete_token);
650 EXECUTE format(
'UPDATE %I.%I SET provsql = $1 WHERE provsql = $2;', TG_TABLE_SCHEMA, TG_TABLE_NAME)
651 USING new_token, old_token;
656$$
LANGUAGE plpgsql
SET search_path=provsql,pg_temp SECURITY DEFINER;
680CREATE OR REPLACE
FUNCTION set_table_info(
681 relid OID, kind TEXT, block_key INT2[]
DEFAULT ARRAY[]::INT2[])
683 'provsql',
'set_table_info' LANGUAGE C PARALLEL SAFE;
686CREATE OR REPLACE
FUNCTION remove_table_info(relid OID)
688 'provsql',
'remove_table_info' LANGUAGE C PARALLEL SAFE;
699CREATE OR REPLACE
FUNCTION get_table_info(
700 relid OID,
OUT kind TEXT,
OUT block_key INT2[])
702 'provsql',
'get_table_info' LANGUAGE C STABLE PARALLEL SAFE;
722CREATE OR REPLACE
FUNCTION set_ancestors(
723 relid OID, ancestors OID[]
DEFAULT ARRAY[]::OID[])
725 'provsql',
'set_ancestors' LANGUAGE C PARALLEL SAFE;
729CREATE OR REPLACE
FUNCTION remove_ancestors(relid OID)
731 'provsql',
'remove_ancestors' LANGUAGE C PARALLEL SAFE;
740CREATE OR REPLACE
FUNCTION get_ancestors(relid OID)
742 'provsql',
'get_ancestors' LANGUAGE C STABLE PARALLEL SAFE;
762CREATE OR REPLACE
FUNCTION provenance_guard()
763 RETURNS TRIGGER AS $$
767 IF TG_OP =
'INSERT' THEN
768 IF NEW.provsql
IS NULL THEN
776 NEW.provsql := public.uuid_generate_v4();
777 FOR _m
IN SELECT mapping, attribute
778 FROM provsql.provenance_mapping_registry
WHERE source = TG_RELID
781 'INSERT INTO %s(value, provenance) SELECT ($1).%I, $2',
782 _m.mapping::REGCLASS, _m.attribute)
783 USING NEW,
NEW.provsql;
786 PERFORM provsql.set_table_info(TG_RELID,
'opaque');
788 ELSIF TG_OP =
'UPDATE' THEN
789 IF NEW.provsql
IS DISTINCT FROM OLD.provsql
THEN
790 PERFORM provsql.set_table_info(TG_RELID,
'opaque');
795$$
LANGUAGE plpgsql
SET search_path=provsql,pg_temp,public
811CREATE OR REPLACE
FUNCTION add_provenance(_tbl REGCLASS)
819 SELECT 1 FROM pg_attribute
820 WHERE attrelid = _tbl
AND attname =
'provsql' AND NOT attisdropped
822 RAISE NOTICE
'table % already has provenance tracking', _tbl;
831 EXECUTE format(
'ALTER TABLE %s ADD COLUMN provsql UUID', _tbl);
833 'UPDATE %s SET provsql = public.uuid_generate_v4() WHERE provsql IS NULL',
835 EXECUTE format(
'CREATE INDEX ON %s(provsql)', _tbl);
837 'CREATE TRIGGER provenance_guard BEFORE INSERT OR UPDATE OF provsql '
838 'ON %s FOR EACH ROW EXECUTE PROCEDURE provsql.provenance_guard()',
840 PERFORM provsql.set_table_info(_tbl::oid,
'tid');
845 PERFORM provsql.set_ancestors(_tbl::oid,
ARRAY[_tbl::oid]);
847$$
LANGUAGE plpgsql SECURITY DEFINER;
856CREATE OR REPLACE
FUNCTION remove_provenance(_tbl REGCLASS)
861 PERFORM provsql.remove_table_info(_tbl::oid);
867 SELECT 1 FROM pg_attribute
868 WHERE attrelid = _tbl
AND attname =
'provsql' AND NOT attisdropped
870 RAISE NOTICE
'table % does not have provenance tracking', _tbl;
877 EXECUTE format(
'DROP TRIGGER provenance_guard on %s', _tbl);
878 EXCEPTION
WHEN undefined_object
THEN
880 EXECUTE format(
'ALTER TABLE %s DROP COLUMN provsql', _tbl);
882 EXECUTE format(
'DROP TRIGGER add_gate on %s', _tbl);
883 EXCEPTION
WHEN undefined_object
THEN
886 EXECUTE format(
'DROP TRIGGER insert_statement on %s', _tbl);
887 EXECUTE format(
'DROP TRIGGER update_statement on %s', _tbl);
888 EXECUTE format(
'DROP TRIGGER delete_statement on %s', _tbl);
889 EXCEPTION
WHEN undefined_object
THEN
905CREATE OR REPLACE
FUNCTION repair_key(_tbl REGCLASS, key_att TEXT)
911 block_key_cols INT2[];
918 block_key_cols :=
ARRAY[]::INT2[];
920 SELECT array_agg(a.attnum
ORDER BY t.ord)::INT2[]
922 FROM unnest(string_to[](key_att,
','))
WITH ORDINALITY
AS t(name, ord)
925 AND a.attname = trim(t.name)
927 AND NOT a.attisdropped;
928 IF block_key_cols
IS NULL OR array_length(block_key_cols,
1)
IS NULL THEN
929 RAISE EXCEPTION
'repair_key: could not resolve key columns from "%"', key_att;
931 IF array_length(block_key_cols,
1) >
16 THEN
932 RAISE EXCEPTION
'repair_key: block key wider than 16 columns is not supported';
942 EXECUTE format(
'ALTER TABLE %s ADD COLUMN provsql_temp UUID DEFAULT public.uuid_generate_v4()', _tbl);
955 'CREATE TEMP TABLE provsql_repair_key_tmp ON COMMIT DROP AS
956 SELECT public.uuid_generate_v4() AS provsql_key_token,
957 COUNT(*) AS provsql_group_size
959 rows_query := format(
960 'SELECT t.provsql_temp,
961 k.provsql_key_token AS key_token,
962 ROW_NUMBER() OVER (ORDER BY t.ctid) AS within_group,
963 k.provsql_group_size AS group_size
964 FROM %s t CROSS JOIN provsql_repair_key_tmp k', _tbl);
967 'CREATE TEMP TABLE provsql_repair_key_tmp ON COMMIT DROP AS
969 public.uuid_generate_v4() AS provsql_key_token,
970 COUNT(*) AS provsql_group_size
972 GROUP BY %1$s', key_att, _tbl);
973 rows_query := format(
974 'SELECT t.provsql_temp,
975 k.provsql_key_token AS key_token,
976 ROW_NUMBER() OVER (PARTITION BY k.provsql_key_token
977 ORDER BY t.ctid) AS within_group,
978 k.provsql_group_size AS group_size
980 JOIN provsql_repair_key_tmp k USING (%1$s)', key_att, _tbl);
984 FOR r
IN SELECT provsql_key_token
FROM provsql_repair_key_tmp LOOP
985 PERFORM provsql.create_gate(r.provsql_key_token,
'input');
989 FOR r
IN EXECUTE rows_query LOOP
990 PERFORM provsql.create_gate(r.provsql_temp,
'mulinput',
ARRAY[r.key_token]);
991 PERFORM provsql.set_prob(r.provsql_temp,
1./r.group_size);
992 PERFORM provsql.set_infos(r.provsql_temp, r.within_group::
INT);
995 DROP TABLE provsql_repair_key_tmp;
997 EXECUTE format(
'ALTER TABLE %s ALTER COLUMN provsql_temp DROP DEFAULT', _tbl);
998 EXECUTE format(
'ALTER TABLE %s RENAME COLUMN provsql_temp TO provsql', _tbl);
999 EXECUTE format(
'CREATE INDEX ON %s(provsql)', _tbl);
1001 'CREATE TRIGGER provenance_guard BEFORE INSERT OR UPDATE OF provsql '
1002 'ON %s FOR EACH ROW EXECUTE PROCEDURE provsql.provenance_guard()',
1004 PERFORM provsql.set_table_info(_tbl::oid,
'bid', block_key_cols);
1007 PERFORM provsql.set_ancestors(_tbl::oid,
ARRAY[_tbl::oid]);
1022CREATE OR REPLACE
FUNCTION cleanup_table_info()
1023 RETURNS event_trigger
AS
1029 SELECT objid
FROM pg_event_trigger_dropped_objects()
1030 WHERE object_type
IN (
'table',
'foreign table',
'materialized view')
1032 PERFORM provsql.remove_table_info(r.objid);
1034 DELETE FROM provsql.provenance_mapping_registry
1035 WHERE source = r.objid
OR mapping = r.objid;
1040DROP EVENT
TRIGGER IF EXISTS provsql_cleanup_table_info;
1043CREATE EVENT
TRIGGER provsql_cleanup_table_info
ON sql_drop
1044 EXECUTE PROCEDURE provsql.cleanup_table_info();
1056CREATE TABLE IF NOT EXISTS provsql.provenance_mapping_registry(
1057 mapping oid
PRIMARY KEY,
1058 source oid
NOT NULL,
1059 attribute name
NOT NULL
1061CREATE INDEX
IF NOT EXISTS provenance_mapping_registry_source_idx
1062 ON provsql.provenance_mapping_registry(source);
1083CREATE OR REPLACE
FUNCTION create_provenance_mapping(
1087 preserve_case BOOL
DEFAULT 'f',
1088 maintained BOOL
DEFAULT false
1096 IF (
CASE WHEN preserve_case
THEN to_regclass(format(
'%I', newtbl))
1097 ELSE to_regclass(newtbl)
END)
IS NOT NULL THEN
1098 RAISE NOTICE
'mapping table % already exists', newtbl;
1105 IF to_regclass(
'pg_temp.tmp_provsql')
IS NOT NULL THEN
1106 DROP TABLE tmp_provsql;
1108 EXECUTE format(
'CREATE TEMP TABLE tmp_provsql ON COMMIT DROP AS TABLE %s', oldtbl);
1109 ALTER TABLE tmp_provsql RENAME provsql
TO provenance;
1113 UPDATE tmp_provsql
SET provenance = provsql.strip_annotations(provenance)
1114 WHERE provsql.get_gate_type(provenance) =
'annotation';
1115 IF preserve_case
THEN
1116 EXECUTE format(
'CREATE TABLE %I AS SELECT %s AS value, provenance FROM tmp_provsql', newtbl, att);
1117 EXECUTE format(
'CREATE INDEX ON %I(provenance)', newtbl);
1119 EXECUTE format(
'CREATE TABLE %s AS SELECT %s AS value, provenance FROM tmp_provsql', newtbl, att);
1120 EXECUTE format(
'CREATE INDEX ON %s(provenance)', newtbl);
1126 INSERT INTO provsql.provenance_mapping_registry(mapping, source, attribute)
1128 (
CASE WHEN preserve_case
THEN to_regclass(format(
'%I', newtbl))
1129 ELSE to_regclass(newtbl)
END)::oid,
1131 ON CONFLICT (mapping)
1132 DO
UPDATE SET source = EXCLUDED.source, attribute = EXCLUDED.attribute;
1146CREATE OR REPLACE
FUNCTION uuid_ns_provsql()
RETURNS UUID
AS
1149 SELECT '920d4f02-8718-5319-9532-d4ab83a64489'::UUID
1150$$
LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
1153CREATE OR REPLACE
FUNCTION gate_zero()
RETURNS UUID
AS
1155 SELECT public.uuid_generate_v5(provsql.uuid_ns_provsql(),
'zero');
1156$$
LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
1159CREATE OR REPLACE
FUNCTION gate_one()
RETURNS UUID
AS
1161 SELECT public.uuid_generate_v5(provsql.uuid_ns_provsql(),
'one');
1162$$
LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
1165CREATE OR REPLACE
FUNCTION epsilon()
RETURNS DOUBLE PRECISION AS
1167 SELECT CAST(
0.
001 AS DOUBLE PRECISION)
1168$$
LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
1205CREATE OR REPLACE
FUNCTION provenance_times(VARIADIC tokens UUID[])
1210 filtered_tokens UUID[];
1220 SELECT array_agg(t)
FROM unnest(tokens) t
WHERE t
IS NOT NULL AND t <> gate_one()
INTO filtered_tokens;
1227 CASE coalesce(array_length(filtered_tokens,
1),
0)
1229 times_token:=gate_one();
1231 times_token:=filtered_tokens[
1];
1236 SELECT uuid_generate_v5(uuid_ns_provsql(),
1237 concat(
'times-canonical', array_agg(t
ORDER BY t)))
1238 FROM unnest(filtered_tokens) t
1240 IF get_gate_type(canonical) =
'times' THEN
1243 times_token := canonical;
1245 times_token := uuid_generate_v5(uuid_ns_provsql(),concat(
'times',filtered_tokens));
1247 PERFORM create_gate(times_token,
'times', ARRAY_AGG(t))
FROM UNNEST(filtered_tokens)
AS t
WHERE t
IS NOT NULL;
1253$$
LANGUAGE plpgsql
SET search_path=provsql,pg_temp,public SECURITY DEFINER PARALLEL SAFE IMMUTABLE;
1261CREATE OR REPLACE
FUNCTION provenance_monus(token1 UUID, token2 UUID)
1267 IF token1
IS NULL THEN
1268 RAISE EXCEPTION
USING MESSAGE=
'provenance_monus is called with first argument NULL';
1271 IF token2
IS NULL THEN
1279 IF token1 = token2
THEN
1281 monus_token:=gate_zero();
1282 ELSIF token1 = gate_zero()
THEN
1284 monus_token:=gate_zero();
1285 ELSIF token2 = gate_zero()
THEN
1287 monus_token:=token1;
1289 monus_token:=uuid_generate_v5(uuid_ns_provsql(),concat(
'monus',token1,token2));
1290 PERFORM create_gate(monus_token,
'monus',
ARRAY[token1::UUID, token2::UUID]);
1295$$
LANGUAGE plpgsql
SET search_path=provsql,pg_temp,public SECURITY DEFINER PARALLEL SAFE IMMUTABLE;
1305CREATE OR REPLACE
FUNCTION provenance_project(token UUID, VARIADIC positions
INT[])
1312 project_token:=uuid_generate_v5(uuid_ns_provsql(),concat(
'project', token, positions));
1313 PERFORM create_gate(project_token,
'project',
ARRAY[token]);
1314 PERFORM set_extra(project_token, ARRAY_AGG(pair)::TEXT)
1316 SELECT ARRAY[(
CASE WHEN info=
0 THEN NULL ELSE info
END), idx]
AS pair
1317 FROM unnest(positions)
WITH ORDINALITY
AS a(info, idx)
1321 RETURN project_token;
1323$$
LANGUAGE plpgsql
SET search_path=provsql,pg_temp,public SECURITY DEFINER PARALLEL SAFE IMMUTABLE;
1332CREATE OR REPLACE
FUNCTION provenance_eq(token UUID, pos1
INT, pos2
INT)
1339 eq_token:=uuid_generate_v5(uuid_ns_provsql(),concat(
'eq',token,pos1,
',',pos2));
1341 PERFORM create_gate(eq_token,
'eq',
ARRAY[token::UUID]);
1342 PERFORM set_infos(eq_token, pos1, pos2);
1345$$
LANGUAGE plpgsql
SET search_path=provsql,pg_temp,public SECURITY DEFINER PARALLEL SAFE IMMUTABLE;
1363CREATE OR REPLACE
FUNCTION provenance_plus(tokens UUID[])
1369 filtered_tokens UUID[];
1377 SELECT array_agg(t)
FROM unnest(tokens) t
1378 WHERE t
IS NOT NULL AND t <> gate_zero()
1379 INTO filtered_tokens;
1381 c:=array_length(filtered_tokens,
1);
1384 plus_token := gate_zero();
1386 plus_token := filtered_tokens[
1];
1391 SELECT uuid_generate_v5(uuid_ns_provsql(),
1392 concat(
'plus-canonical', array_agg(t
ORDER BY t)))
1393 FROM unnest(filtered_tokens) t
1395 IF get_gate_type(canonical) =
'plus' THEN
1398 plus_token := canonical;
1400 plus_token := uuid_generate_v5(
1402 concat(
'plus', filtered_tokens));
1404 PERFORM create_gate(plus_token,
'plus', filtered_tokens);
1410$$
LANGUAGE plpgsql STRICT
SET search_path=provsql,pg_temp,public SECURITY DEFINER PARALLEL SAFE IMMUTABLE;
1457CREATE OR REPLACE
FUNCTION eval_recursive(
1462 max_iter
INT DEFAULT 1000)
1479 absorptive_mode
BOOLEAN :=
1480 coalesce(current_setting(
'provsql.provenance',
true),
'semiring')
1481 IN (
'absorptive',
'BOOLEAN');
1482 truncated
BOOLEAN :=
false;
1483 ntuples
INT :=
NULL;
1485 EXECUTE format(
'DROP TABLE IF EXISTS %I', work_name);
1486 DROP TABLE IF EXISTS _new;
1490 EXECUTE format(
'CREATE TEMP TABLE %I (%s, provsql UUID)', work_name, coldef);
1491 EXECUTE format(
'CREATE TEMP TABLE _new (LIKE %I)', work_name);
1497 IF iters > max_iter
THEN
1498 RAISE EXCEPTION
'eval_recursive: no fixpoint after % rounds (cyclic data?)', max_iter;
1505 EXECUTE 'TRUNCATE _new';
1506 EXECUTE format(
'INSERT INTO _new(%s) %s', colnames, body_sql);
1507 GET DIAGNOSTICS new_count = ROW_COUNT;
1511 'SELECT EXISTS((TABLE _new EXCEPT TABLE %1$I) UNION ALL (TABLE %1$I EXCEPT TABLE _new))',
1512 work_name)
INTO changed;
1517 IF absorptive_mode
AND ntuples
IS NULL THEN
1519 'SELECT NOT EXISTS('
1520 ||
'(SELECT %2$s FROM _new EXCEPT SELECT %2$s FROM %1$I) UNION ALL '
1521 ||
'(SELECT %2$s FROM %1$I EXCEPT SELECT %2$s FROM _new))',
1522 work_name, colnames)
INTO set_stable;
1524 ntuples := new_count;
1529 EXECUTE format(
'TRUNCATE %I', work_name);
1530 EXECUTE format(
'INSERT INTO %1$I(%2$s) SELECT %2$s FROM _new', work_name, colnames);
1534 EXIT
WHEN NOT changed;
1541 IF absorptive_mode
AND ntuples
IS NOT NULL AND iters >= ntuples +
1 THEN
1551 'UPDATE %I SET provsql = provsql.provenance_assume(provsql, ''absorptive'')',
1555$$
LANGUAGE plpgsql
SET client_min_messages = warning;
1564CREATE OR REPLACE
FUNCTION provenance_cmp(
1580 IF left_token
IS NULL OR right_token
IS NULL OR comparison_op
IS NULL THEN
1584 cmp_token := public.uuid_generate_v5(
1586 concat(
'cmp', left_token::TEXT, comparison_op::TEXT, right_token::TEXT)
1589 PERFORM create_gate(cmp_token,
'cmp',
ARRAY[left_token, right_token]);
1590 PERFORM set_infos(cmp_token, comparison_op::
INTEGER);
1594 SET search_path=provsql,pg_temp,public
1609CREATE FUNCTION cmp_surviving_factors(tokens UUID[], cmp UUID)
1611 'provsql',
'cmp_surviving_factors' LANGUAGE C PARALLEL SAFE STABLE;
1621CREATE OR REPLACE
FUNCTION provenance_cmp_times(cmp UUID, tokens UUID[])
1627 kept := provsql.cmp_surviving_factors(tokens, cmp);
1628 IF kept
IS NULL OR array_length(kept,
1)
IS NULL THEN
1631 RETURN provsql.provenance_times(VARIADIC kept || cmp);
1633$$
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE;
1651CREATE OR REPLACE
FUNCTION provenance_arith(
1660 arith_token := public.uuid_generate_v5(
1662 concat(
'arith', op::TEXT, children::TEXT)
1664 PERFORM create_gate(arith_token,
'arith', children);
1665 PERFORM set_infos(arith_token, op);
1669 SET search_path=provsql,pg_temp,public
1691CREATE OR REPLACE
FUNCTION provenance_case(
1699 IF array_length(children,
1)
IS NULL OR array_length(children,
1) %
2 =
0 THEN
1700 RAISE EXCEPTION
'provenance_case expects an odd number of children '
1701 '(guard/value pairs followed by a default), got %',
1702 coalesce(array_length(children,
1),
0);
1704 case_token := public.uuid_generate_v5(
1706 concat(
'case', children::TEXT)
1708 PERFORM create_gate(case_token,
'case', children);
1712 SET search_path=provsql,pg_temp,public
1737CREATE OR REPLACE
FUNCTION provenance_evaluate_compiled(
1739 token2value REGCLASS,
1741 element_one ANYELEMENT)
1742RETURNS ANYELEMENT
AS
1743 'provsql',
'provenance_evaluate_compiled' LANGUAGE C PARALLEL SAFE STABLE;
1762CREATE OR REPLACE
FUNCTION provenance_evaluate(
1764 token2value REGCLASS,
1765 element_one ANYELEMENT,
1767 plus_function REGPROC,
1768 times_function REGPROC,
1769 monus_function REGPROC,
1770 delta_function REGPROC)
1771 RETURNS ANYELEMENT
AS
1774 gate_type PROVENANCE_GATE;
1775 result ALIAS
FOR $
0;
1781 SELECT get_gate_type(token)
INTO gate_type;
1783 IF gate_type
IS NULL THEN
1786 ELSIF gate_type =
'input' THEN
1787 EXECUTE format(
'SELECT value FROM %s WHERE provenance=%L', token2value, token)
1789 IF result IS NULL THEN
1790 result := element_one;
1792 ELSIF gate_type =
'mulinput' THEN
1793 SELECT concat(
'{',(get_children(token))[
1]::TEXT,
'=',(get_infos(token)).info1,
'}')
1795 ELSIF gate_type=
'update' THEN
1796 EXECUTE format(
'SELECT value FROM %s WHERE provenance=%L',token2value,token)
INTO result;
1797 IF result IS NULL THEN
1798 result:=element_one;
1800 ELSIF gate_type =
'plus' THEN
1801 EXECUTE format(
'SELECT %s(provsql.provenance_evaluate(t,%L,%L::%s,%L,%L,%L,%L,%L)) FROM unnest(get_children(%L)) AS t',
1802 plus_function, token2value, element_one, value_type, value_type, plus_function, times_function, monus_function, delta_function, token)
1805 ELSIF gate_type =
'times' THEN
1806 EXECUTE format(
'SELECT %s(provsql.provenance_evaluate(t,%L,%L::%s,%L,%L,%L,%L,%L)) FROM unnest(get_children(%L)) AS t',
1807 times_function, token2value, element_one, value_type, value_type, plus_function, times_function, monus_function, delta_function, token)
1810 ELSIF gate_type =
'monus' THEN
1811 IF monus_function
IS NULL THEN
1812 RAISE EXCEPTION
USING MESSAGE=
'Provenance with negation evaluated over a semiring without monus function';
1814 EXECUTE format(
'SELECT %s(a1,a2) FROM (SELECT provsql.provenance_evaluate(c[1],%L,%L::%s,%L,%L,%L,%L,%L) AS a1, ' ||
1815 'provsql.provenance_evaluate(c[2],%L,%L::%s,%L,%L,%L,%L,%L) AS a2 FROM get_children(%L) c) tmp',
1816 monus_function, token2value, element_one, value_type, value_type, plus_function, times_function, monus_function, delta_function,
1817 token2value, element_one, value_type, value_type, plus_function, times_function, monus_function, delta_function, token)
1821 ELSIF gate_type =
'eq' THEN
1822 EXECUTE format(
'SELECT provsql.provenance_evaluate((get_children(%L))[1],%L,%L::%s,%L,%L,%L,%L,%L)',
1823 token, token2value, element_one, value_type, value_type, plus_function, times_function, monus_function, delta_function)
1844 ELSIF gate_type =
'delta' THEN
1845 IF delta_function
IS NULL THEN
1846 RAISE EXCEPTION
USING MESSAGE=
'Provenance with aggregation evaluated over a semiring without delta function';
1848 EXECUTE format(
'SELECT %I(a) FROM (SELECT provsql.provenance_evaluate((get_children(%L))[1],%L,%L::%s,%L,%L,%L,%L,%L) AS a) tmp',
1849 delta_function, token, token2value, element_one, value_type, value_type, plus_function, times_function, monus_function, delta_function)
1853 ELSIF gate_type =
'zero' THEN
1854 EXECUTE format(
'SELECT %I(a) FROM (SELECT %L::%I AS a WHERE FALSE) temp', plus_function, element_one, value_type)
1857 ELSIF gate_type =
'one' THEN
1858 EXECUTE format(
'SELECT %L::%I', element_one, value_type)
1861 ELSIF gate_type =
'project' THEN
1862 EXECUTE format(
'SELECT provsql.provenance_evaluate((get_children(%L))[1],%L,%L::%s,%L,%L,%L,%L,%L)',
1863 token, token2value, element_one, value_type, value_type, plus_function, times_function, monus_function, delta_function)
1866 ELSIF gate_type =
'annotation' THEN
1870 EXECUTE format(
'SELECT provsql.provenance_evaluate((get_children(%L))[1],%L,%L::%s,%L,%L,%L,%L,%L)',
1871 token, token2value, element_one, value_type, value_type, plus_function, times_function, monus_function, delta_function)
1875 RAISE EXCEPTION
USING MESSAGE=
'provenance_evaluate cannot be called on formulas using ' || gate_type ||
' gates; use compiled semirings instead';
1880$$
LANGUAGE plpgsql PARALLEL SAFE STABLE;
1897CREATE OR REPLACE
FUNCTION provenance_evaluate(
1899 token2value REGCLASS,
1900 element_one ANYELEMENT,
1901 plus_function REGPROC,
1902 times_function REGPROC,
1903 monus_function REGPROC =
NULL,
1904 delta_function REGPROC =
NULL)
1905 RETURNS ANYELEMENT
AS
1906 'provsql',
'provenance_evaluate' LANGUAGE C STABLE;
1917CREATE TYPE GATE_WITH_DESC
AS (f UUID, t UUID, gate_type PROVENANCE_GATE, desc_str
CHARACTER VARYING, infos
INTEGER[], extra TEXT);
1929CREATE OR REPLACE
FUNCTION sub_circuit_with_desc(
1931 token2desc REGCLASS)
RETURNS SETOF GATE_WITH_DESC
AS
1934 RETURN QUERY
EXECUTE
1935 'WITH RECURSIVE transitive_closure(f,t,gate_type) AS (
1936 SELECT $1,t,provsql.get_gate_type($1) FROM unnest(provsql.get_children($1)) AS t
1938 SELECT p1.t,u,provsql.get_gate_type(p1.t) FROM transitive_closure p1, unnest(provsql.get_children(p1.t)) AS u)
1939 SELECT *, ARRAY[(get_infos(f)).info1, (get_infos(f)).info2], get_extra(f) FROM (
1940 SELECT f::UUID,t::UUID,gate_type,NULL FROM transitive_closure
1942 SELECT p2.provenance::UUID as f, NULL::UUID, ''input'', CAST (p2.value AS varchar) FROM transitive_closure p1 JOIN ' || token2desc ||
' AS p2
1945 SELECT provenance::UUID as f, NULL::UUID, ''input'', CAST (value AS varchar) FROM ' || token2desc ||
' WHERE provenance=$1
1950$$
LANGUAGE plpgsql PARALLEL SAFE;
1962CREATE OR REPLACE
FUNCTION identify_token(
1963 token UUID,
OUT table_name REGCLASS,
OUT nb_columns
INTEGER)
AS
1973 (
SELECT count(*)
FROM pg_attribute a2
WHERE a2.attrelid=a1.attrelid
AND attnum>
0 AND atttypid<>
0)-1 c
1974 FROM pg_attribute a1
JOIN pg_type
ON atttypid=pg_type.oid
1975 JOIN pg_class
ON attrelid=pg_class.oid
1976 JOIN pg_namespace
ON relnamespace=pg_namespace.oid
1977 WHERE typname=
'UUID' AND relkind=
'r'
1978 AND nspname<>
'provsql'
1979 AND attname=
'provsql'
1981 EXECUTE format(
'SELECT * FROM %I WHERE provsql=%L',t.relname,token)
INTO result;
1986 IF result.provsql
IS NOT NULL THEN
1987 table_name:=t.relname;
1993$$
LANGUAGE plpgsql STRICT;
2001CREATE OR REPLACE
FUNCTION sub_circuit_for_where(token UUID)
2002 RETURNS TABLE(f UUID, t UUID, gate_type PROVENANCE_GATE, table_name REGCLASS, nb_columns
INTEGER, infos
INTEGER[], extra TEXT)
AS
2004 WITH RECURSIVE transitive_closure(f,t,idx,gate_type)
AS (
2005 SELECT $
1,t,id,provsql.get_gate_type($
1)
FROM unnest(provsql.get_children($
1))
WITH ORDINALITY
AS a(t,id)
2007 SELECT p1.t,u,id,provsql.get_gate_type(p1.t)
FROM transitive_closure p1,
unnest(provsql.get_children(p1.t))
WITH ORDINALITY
AS a(u, id)
2008 )
SELECT f, t, gate_type, table_name, nb_columns,
ARRAY[(get_infos(f)).info1, (get_infos(f)).info2], get_extra(f)
FROM (
2018 SELECT DISTINCT f, t::UUID, idx, gate_type,
NULL::REGCLASS
AS table_name,
NULL::
INTEGER AS nb_columns
FROM transitive_closure
2020 SELECT DISTINCT t,
NULL::UUID,
NULL::
INT,
'input'::PROVENANCE_GATE, (id).table_name, (id).nb_columns
FROM transitive_closure
JOIN (
SELECT t
AS prov, provsql.identify_token(t)
as id
FROM transitive_closure
WHERE t
NOT IN (
SELECT f
FROM transitive_closure)) temp
ON t=prov
2022 SELECT DISTINCT $
1,
NULL::UUID,
NULL::
INT,
'input'::PROVENANCE_GATE, (id).table_name, (id).nb_columns
FROM (
SELECT provsql.identify_token($
1)
AS id
WHERE $
1 NOT IN (
SELECT f
FROM transitive_closure)) temp
2057CREATE OR REPLACE
FUNCTION circuit_subgraph(root UUID, max_depth
INT DEFAULT 8)
2058 RETURNS TABLE(node UUID, parent UUID, child_pos
INT, gate_type TEXT, info1 TEXT, info2 TEXT, depth
INT)
AS
2060 WITH RECURSIVE bfs(node, parent, child_pos, depth)
AS (
2061 SELECT root,
NULL::UUID,
NULL::
INT,
0
2063 SELECT c.t, b.node, c.idx::
INT, b.depth +
1
2065 CROSS JOIN LATERAL unnest(provsql.get_children(b.node))
2066 WITH ORDINALITY
AS c(t, idx)
2067 WHERE b.depth < max_depth
2075 SELECT node, MAX(depth)
AS depth
FROM bfs
GROUP BY node
2081 SELECT DISTINCT parent, node
AS child, child_pos
2082 FROM bfs
WHERE parent
IS NOT NULL
2088 provsql.get_gate_type(d.node)::TEXT,
2093 LEFT JOIN edges e
ON e.child = d.node
2094 LEFT JOIN LATERAL provsql.get_infos(d.node) i
ON TRUE
2095 ORDER BY d.depth, d.node, e.parent;
2096$$
LANGUAGE sql STABLE PARALLEL SAFE;
2117CREATE OR REPLACE
FUNCTION simplified_circuit_subgraph(
2118 root UUID, max_depth
INT DEFAULT 8)
RETURNS jsonb
2119 AS 'provsql',
'simplified_circuit_subgraph'
2120 LANGUAGE C STABLE PARALLEL SAFE;
2144CREATE OR REPLACE
FUNCTION rv_histogram(
2145 token UUID, bins
INT DEFAULT 30, prov UUID
DEFAULT gate_one())
2147 AS 'provsql',
'rv_histogram'
2148 LANGUAGE C VOLATILE PARALLEL SAFE;
2175CREATE OR REPLACE
FUNCTION rv_analytical_curves(
2176 token UUID, samples
INT DEFAULT 100, prov UUID
DEFAULT gate_one())
2178 AS 'provsql',
'rv_analytical_curves'
2179 LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
2200CREATE OR REPLACE
FUNCTION rv_sample(
2201 token UUID, n
INTEGER, prov UUID
DEFAULT gate_one())
2202 RETURNS SETOF float8
2203 AS 'provsql',
'rv_sample'
2204 LANGUAGE C VOLATILE PARALLEL SAFE;
2222CREATE OR REPLACE
FUNCTION resolve_input(UUID UUID)
2223 RETURNS TABLE(relation REGCLASS, row_data JSONB)
AS
2235 SELECT c.oid::REGCLASS
AS regc
2237 JOIN pg_class c
ON a.attrelid = c.oid
2238 JOIN pg_namespace ns
ON c.relnamespace = ns.oid
2239 JOIN pg_type ty
ON a.atttypid = ty.oid
2240 WHERE a.attname =
'provsql'
2241 AND ty.typname =
'UUID'
2243 AND ns.nspname <>
'provsql'
2248 'SELECT %L::REGCLASS, to_jsonb(t) - ''provsql'', t.provsql FROM %s AS t WHERE provsql = $1',
2258$$
LANGUAGE plpgsql STABLE;
2281CREATE TYPE AGG_TOKEN;
2284CREATE OR REPLACE
FUNCTION agg_token_in(CSTRING)
2286 AS 'provsql',
'agg_token_in' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
2304CREATE OR REPLACE
FUNCTION agg_token_out(AGG_TOKEN)
2306 AS 'provsql',
'agg_token_out' LANGUAGE C STABLE STRICT PARALLEL SAFE;
2309CREATE OR REPLACE
FUNCTION agg_token_cast(AGG_TOKEN)
2311 AS 'provsql',
'agg_token_cast' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
2313CREATE TYPE AGG_TOKEN (
2314 internallength =
117,
2315 input = agg_token_in,
2316 output = agg_token_out,
2321CREATE OR REPLACE
FUNCTION agg_token_uuid(aggtok AGG_TOKEN)
2325 RETURN agg_token_cast(aggtok)::UUID;
2327$$
LANGUAGE plpgsql STRICT
SET search_path=provsql,pg_temp,public SECURITY DEFINER IMMUTABLE PARALLEL SAFE;
2330CREATE CAST (AGG_TOKEN
AS UUID)
WITH FUNCTION agg_token_uuid(AGG_TOKEN)
AS IMPLICIT;
2344CREATE OR REPLACE
FUNCTION agg_guard_holds(token UUID)
2348 gt PROVENANCE_GATE := get_gate_type(token);
2359 ELSIF gt =
'zero' THEN
2361 ELSIF gt
IN (
'times',
'plus')
THEN
2362 SELECT bool_and(h), bool_or(h), bool_or(h
IS NULL)
2363 INTO all_true, any_true, any_null
2364 FROM (
SELECT provsql.agg_guard_holds(c)
AS h
2365 FROM unnest(get_children(token))
AS c)
AS s;
2366 IF gt =
'times' THEN
2369 RETURN CASE WHEN NOT all_true
THEN false
2370 WHEN any_null
THEN NULL
2374 RETURN CASE WHEN any_true
THEN true
2375 WHEN any_null
THEN NULL
2378 ELSIF gt =
'cmp' THEN
2379 ch := get_children(token);
2380 l := agg_gate_value(ch[
1]);
2381 r := agg_gate_value(ch[
2]);
2382 IF l
IS NULL OR r
IS NULL THEN
2385 SELECT oprname
INTO opname
2386 FROM pg_catalog.pg_operator
WHERE oid = (get_infos(token)).info1;
2389 WHEN '<=' THEN l <= r
2391 WHEN '<>' THEN l <> r
2392 WHEN '>=' THEN l >= r
2398$$
LANGUAGE plpgsql STABLE STRICT PARALLEL SAFE
2399 SET search_path=provsql,pg_temp,public;
2415CREATE OR REPLACE
FUNCTION agg_gate_value(token UUID)
2419 gt PROVENANCE_GATE := get_gate_type(token);
2424 IF gt
IN (
'agg',
'arith',
'value')
THEN
2426 RETURN get_extra(token)::
NUMERIC;
2427 EXCEPTION
WHEN others
THEN
2430 ELSIF gt =
'semimod' THEN
2431 RETURN agg_gate_value((get_children(token))[
2]);
2432 ELSIF gt =
'conditioned' THEN
2433 RETURN agg_gate_value((get_children(token))[
1]);
2434 ELSIF gt =
'case' THEN
2435 ch := get_children(token);
2436 n := array_length(ch,
1);
2437 FOR i
IN 1 .. (n -
1) /
2 LOOP
2438 holds := agg_guard_holds(ch[
2 * i -
1]);
2439 IF holds
IS NULL THEN
2442 RETURN agg_gate_value(ch[
2 * i]);
2445 RETURN agg_gate_value(ch[n]);
2449$$
LANGUAGE plpgsql STABLE STRICT PARALLEL SAFE
2450 SET search_path=provsql,pg_temp,public;
2471CREATE OR REPLACE
FUNCTION agg_token_value_text(token UUID)
2477 WHEN provsql.get_gate_type(token)
IN (
'agg',
'arith')
2478 THEN provsql.get_extra(token) ||
' (*)'
2481 WHEN provsql.get_gate_type(token)
IN (
'case',
'conditioned',
'semimod',
'value')
2482 THEN provsql.agg_gate_value(token)::TEXT ||
' (*)'
2485$$
LANGUAGE sql STABLE STRICT PARALLEL SAFE;
2488CREATE OR REPLACE
FUNCTION agg_token_to_numeric(AGG_TOKEN)
2490 AS 'provsql',
'agg_token_to_numeric' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
2493CREATE OR REPLACE
FUNCTION agg_token_to_float8(AGG_TOKEN)
2494 RETURNS double precision
2495 AS 'provsql',
'agg_token_to_float8' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
2498CREATE OR REPLACE
FUNCTION agg_token_to_int4(AGG_TOKEN)
2500 AS 'provsql',
'agg_token_to_int4' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
2503CREATE OR REPLACE
FUNCTION agg_token_to_int8(AGG_TOKEN)
2505 AS 'provsql',
'agg_token_to_int8' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
2508CREATE OR REPLACE
FUNCTION agg_token_to_text(AGG_TOKEN)
2510 AS 'provsql',
'agg_token_to_text' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
2519CREATE CAST (AGG_TOKEN
AS NUMERIC)
WITH FUNCTION agg_token_to_numeric(AGG_TOKEN)
AS ASSIGNMENT;
2536CREATE OR REPLACE
FUNCTION agg_token_value(AGG_TOKEN)
2538 AS 'provsql',
'agg_token_value' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
2543CREATE OR REPLACE
FUNCTION agg_token_make(tok UUID, val
NUMERIC)
2544 RETURNS AGG_TOKEN
AS
2546 SELECT format(
'( %s , %s )', tok::TEXT, val::TEXT)::provsql.AGG_TOKEN;
2547$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
2548 SET search_path=provsql,pg_temp,public;
2553CREATE OR REPLACE
FUNCTION agg_value_gate(v
NUMERIC)
2557 token UUID := public.uuid_generate_v5(
2558 provsql.uuid_ns_provsql(), concat(
'value', v::TEXT));
2560 PERFORM provsql.create_gate(token,
'value');
2561 PERFORM provsql.set_extra(token, v::TEXT);
2564$$
LANGUAGE plpgsql STRICT IMMUTABLE PARALLEL SAFE
2565 SET search_path=provsql,pg_temp,public SECURITY DEFINER;
2576CREATE OR REPLACE
FUNCTION agg_arith_make(op
INT, children UUID[], val
NUMERIC)
2577 RETURNS AGG_TOKEN
AS
2580 token UUID := provsql.provenance_arith(op, children);
2582 PERFORM provsql.set_extra(token, val::TEXT);
2583 RETURN provsql.agg_token_make(token, val);
2585$$
LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE
2586 SET search_path=provsql,pg_temp,public SECURITY DEFINER;
2590CREATE OR REPLACE
FUNCTION agg_token_plus(a AGG_TOKEN, b AGG_TOKEN)
2591 RETURNS AGG_TOKEN
AS
2592$$
SELECT provsql.agg_arith_make(
0,
ARRAY[(a)::UUID, (b)::UUID],
2593 provsql.agg_token_value(a) + provsql.agg_token_value(b)); $$
2594 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
SET search_path=provsql,pg_temp,public;
2597CREATE OR REPLACE
FUNCTION agg_token_minus(a AGG_TOKEN, b AGG_TOKEN)
2598 RETURNS AGG_TOKEN
AS
2599$$
SELECT provsql.agg_arith_make(
2,
ARRAY[(a)::UUID, (b)::UUID],
2600 provsql.agg_token_value(a) - provsql.agg_token_value(b)); $$
2601 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
SET search_path=provsql,pg_temp,public;
2604CREATE OR REPLACE
FUNCTION agg_token_times(a AGG_TOKEN, b AGG_TOKEN)
2605 RETURNS AGG_TOKEN
AS
2606$$
SELECT provsql.agg_arith_make(
1,
ARRAY[(a)::UUID, (b)::UUID],
2607 provsql.agg_token_value(a) * provsql.agg_token_value(b)); $$
2608 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
SET search_path=provsql,pg_temp,public;
2611CREATE OR REPLACE
FUNCTION agg_token_div(a AGG_TOKEN, b AGG_TOKEN)
2612 RETURNS AGG_TOKEN
AS
2613$$
SELECT provsql.agg_arith_make(
3,
ARRAY[(a)::UUID, (b)::UUID],
2614 provsql.agg_token_value(a) / provsql.agg_token_value(b)); $$
2615 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
SET search_path=provsql,pg_temp,public;
2618CREATE OR REPLACE
FUNCTION agg_token_neg(a AGG_TOKEN)
2619 RETURNS AGG_TOKEN
AS
2620$$
SELECT provsql.agg_arith_make(
4,
ARRAY[(a)::UUID],
2621 - provsql.agg_token_value(a)); $$
2622 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
SET search_path=provsql,pg_temp,public;
2626CREATE OR REPLACE
FUNCTION agg_token_plus_numeric(a AGG_TOKEN, b
NUMERIC)
2627 RETURNS AGG_TOKEN
AS
2628$$
SELECT provsql.agg_arith_make(
0,
ARRAY[(a)::UUID, provsql.agg_value_gate(b)],
2629 provsql.agg_token_value(a) + b); $$
2630 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
SET search_path=provsql,pg_temp,public;
2633CREATE OR REPLACE
FUNCTION agg_token_minus_numeric(a AGG_TOKEN, b
NUMERIC)
2634 RETURNS AGG_TOKEN
AS
2635$$
SELECT provsql.agg_arith_make(
2,
ARRAY[(a)::UUID, provsql.agg_value_gate(b)],
2636 provsql.agg_token_value(a) - b); $$
2637 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
SET search_path=provsql,pg_temp,public;
2640CREATE OR REPLACE
FUNCTION agg_token_times_numeric(a AGG_TOKEN, b
NUMERIC)
2641 RETURNS AGG_TOKEN
AS
2642$$
SELECT provsql.agg_arith_make(
1,
ARRAY[(a)::UUID, provsql.agg_value_gate(b)],
2643 provsql.agg_token_value(a) * b); $$
2644 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
SET search_path=provsql,pg_temp,public;
2647CREATE OR REPLACE
FUNCTION agg_token_div_numeric(a AGG_TOKEN, b
NUMERIC)
2648 RETURNS AGG_TOKEN
AS
2649$$
SELECT provsql.agg_arith_make(
3,
ARRAY[(a)::UUID, provsql.agg_value_gate(b)],
2650 provsql.agg_token_value(a) / b); $$
2651 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
SET search_path=provsql,pg_temp,public;
2655CREATE OR REPLACE
FUNCTION numeric_plus_agg_token(a
NUMERIC, b AGG_TOKEN)
2656 RETURNS AGG_TOKEN
AS
2657$$
SELECT provsql.agg_arith_make(
0,
ARRAY[provsql.agg_value_gate(a), (b)::UUID],
2658 a + provsql.agg_token_value(b)); $$
2659 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
SET search_path=provsql,pg_temp,public;
2662CREATE OR REPLACE
FUNCTION numeric_minus_agg_token(a
NUMERIC, b AGG_TOKEN)
2663 RETURNS AGG_TOKEN
AS
2664$$
SELECT provsql.agg_arith_make(
2,
ARRAY[provsql.agg_value_gate(a), (b)::UUID],
2665 a - provsql.agg_token_value(b)); $$
2666 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
SET search_path=provsql,pg_temp,public;
2669CREATE OR REPLACE
FUNCTION numeric_times_agg_token(a
NUMERIC, b AGG_TOKEN)
2670 RETURNS AGG_TOKEN
AS
2671$$
SELECT provsql.agg_arith_make(
1,
ARRAY[provsql.agg_value_gate(a), (b)::UUID],
2672 a * provsql.agg_token_value(b)); $$
2673 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
SET search_path=provsql,pg_temp,public;
2676CREATE OR REPLACE
FUNCTION numeric_div_agg_token(a
NUMERIC, b AGG_TOKEN)
2677 RETURNS AGG_TOKEN
AS
2678$$
SELECT provsql.agg_arith_make(
3,
ARRAY[provsql.agg_value_gate(a), (b)::UUID],
2679 a / provsql.agg_token_value(b)); $$
2680 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
SET search_path=provsql,pg_temp,public;
2683CREATE OPERATOR + (LEFTARG=AGG_TOKEN, RIGHTARG=AGG_TOKEN,
PROCEDURE=agg_token_plus, COMMUTATOR = +);
2684CREATE OPERATOR - (LEFTARG=AGG_TOKEN, RIGHTARG=AGG_TOKEN,
PROCEDURE=agg_token_minus);
2685CREATE OPERATOR * (LEFTARG=AGG_TOKEN, RIGHTARG=AGG_TOKEN,
PROCEDURE=agg_token_times, COMMUTATOR = *);
2686CREATE OPERATOR / (LEFTARG=AGG_TOKEN, RIGHTARG=AGG_TOKEN,
PROCEDURE=agg_token_div);
2687CREATE OPERATOR - (RIGHTARG=AGG_TOKEN,
PROCEDURE=agg_token_neg);
2689CREATE OPERATOR + (LEFTARG=AGG_TOKEN, RIGHTARG=
NUMERIC,
PROCEDURE=agg_token_plus_numeric, COMMUTATOR = +);
2690CREATE OPERATOR - (LEFTARG=AGG_TOKEN, RIGHTARG=
NUMERIC,
PROCEDURE=agg_token_minus_numeric);
2691CREATE OPERATOR * (LEFTARG=AGG_TOKEN, RIGHTARG=
NUMERIC,
PROCEDURE=agg_token_times_numeric, COMMUTATOR = *);
2692CREATE OPERATOR / (LEFTARG=AGG_TOKEN, RIGHTARG=
NUMERIC,
PROCEDURE=agg_token_div_numeric);
2694CREATE OPERATOR + (LEFTARG=
NUMERIC, RIGHTARG=AGG_TOKEN,
PROCEDURE=numeric_plus_agg_token, COMMUTATOR = +);
2695CREATE OPERATOR - (LEFTARG=
NUMERIC, RIGHTARG=AGG_TOKEN,
PROCEDURE=numeric_minus_agg_token);
2696CREATE OPERATOR * (LEFTARG=
NUMERIC, RIGHTARG=AGG_TOKEN,
PROCEDURE=numeric_times_agg_token, COMMUTATOR = *);
2697CREATE OPERATOR / (LEFTARG=
NUMERIC, RIGHTARG=AGG_TOKEN,
PROCEDURE=numeric_div_agg_token);
2700CREATE CAST (AGG_TOKEN
AS double precision)
WITH FUNCTION agg_token_to_float8(AGG_TOKEN)
AS ASSIGNMENT;
2702CREATE CAST (AGG_TOKEN
AS INTEGER)
WITH FUNCTION agg_token_to_int4(AGG_TOKEN)
AS ASSIGNMENT;
2704CREATE CAST (AGG_TOKEN
AS bigint)
WITH FUNCTION agg_token_to_int8(AGG_TOKEN)
AS ASSIGNMENT;
2706CREATE CAST (AGG_TOKEN
AS TEXT)
WITH FUNCTION agg_token_to_text(AGG_TOKEN)
AS ASSIGNMENT;
2721CREATE OR REPLACE
FUNCTION agg_token_cond(a AGG_TOKEN, cond UUID)
2722 RETURNS AGG_TOKEN
AS
2730 IF cond
IS NULL OR cond = gate_one()
THEN
2735 IF get_gate_type(tok) =
'conditioned'
2736 AND array_length(get_children(tok),
1) =
2 THEN
2737 ch := get_children(tok);
2739 ev := provenance_times(ch[
2], cond);
2744 result := public.uuid_generate_v5(uuid_ns_provsql(),
2745 concat(
'conditioned', tok, ev));
2746 PERFORM create_gate(
result,
'conditioned',
ARRAY[tok, ev]);
2747 RETURN agg_token_make(
result, agg_token_value(a));
2749$$
LANGUAGE plpgsql
SET search_path=provsql,pg_temp,public
2750 SECURITY DEFINER PARALLEL SAFE;
2753 LEFTARG = AGG_TOKEN,
2755 PROCEDURE = agg_token_cond
2766CREATE OR REPLACE
FUNCTION agg_token_cond_predicate(
2767 a AGG_TOKEN, predicate
BOOLEAN)
RETURNS AGG_TOKEN
AS
2770 RAISE EXCEPTION
'AGG_TOKEN | (predicate) must be rewritten by the ProvSQL '
2771 'planner hook: the right operand must be a Boolean combination of '
2772 'aggregate / random_variable comparisons (is provsql.active off?)';
2774$$
LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
2777 LEFTARG = AGG_TOKEN,
2779 PROCEDURE = agg_token_cond_predicate
2791CREATE OR REPLACE
FUNCTION agg_conditioned_target(a AGG_TOKEN)
2792 RETURNS AGG_TOKEN
AS
2795 WHEN provsql.get_gate_type((a)::UUID) =
'conditioned'
2796 AND array_length(provsql.get_children((a)::UUID),
1) =
2
2797 THEN provsql.agg_token_make(
2798 (provsql.get_children((a)::UUID))[
1], provsql.agg_token_value(a))
2801$$
LANGUAGE sql STABLE PARALLEL SAFE
SET search_path=provsql,pg_temp,public;
2810CREATE OR REPLACE
FUNCTION agg_token_comp_numeric(a AGG_TOKEN, b
NUMERIC)
2813IMMUTABLE STRICT PARALLEL SAFE
2816 RAISE EXCEPTION
'Comparison AGG_TOKEN-NUMERIC not implemented, should be replaced by ProvSQL behavior';
2826CREATE OR REPLACE
FUNCTION numeric_comp_agg_token(a
NUMERIC, b AGG_TOKEN)
2829IMMUTABLE STRICT PARALLEL SAFE
2832 RAISE EXCEPTION
'Comparison NUMERIC-AGG_TOKEN not implemented, should be replaced by ProvSQL behavior';
2838 LEFTARG = AGG_TOKEN,
2840 PROCEDURE = agg_token_comp_numeric,
2847 RIGHTARG = AGG_TOKEN,
2848 PROCEDURE = numeric_comp_agg_token,
2855 LEFTARG = AGG_TOKEN,
2857 PROCEDURE = agg_token_comp_numeric,
2864 RIGHTARG = AGG_TOKEN,
2865 PROCEDURE = numeric_comp_agg_token,
2872 LEFTARG = AGG_TOKEN,
2874 PROCEDURE = agg_token_comp_numeric,
2881 RIGHTARG = AGG_TOKEN,
2882 PROCEDURE = numeric_comp_agg_token,
2889 LEFTARG = AGG_TOKEN,
2891 PROCEDURE = agg_token_comp_numeric,
2898 RIGHTARG = AGG_TOKEN,
2899 PROCEDURE = numeric_comp_agg_token,
2906 LEFTARG = AGG_TOKEN,
2908 PROCEDURE = agg_token_comp_numeric,
2915 RIGHTARG = AGG_TOKEN,
2916 PROCEDURE = numeric_comp_agg_token,
2923 LEFTARG = AGG_TOKEN,
2925 PROCEDURE = agg_token_comp_numeric,
2932 RIGHTARG = AGG_TOKEN,
2933 PROCEDURE = numeric_comp_agg_token,
2947CREATE OR REPLACE
FUNCTION agg_token_comp_agg_token(a AGG_TOKEN, b AGG_TOKEN)
2950IMMUTABLE STRICT PARALLEL SAFE
2953 RAISE EXCEPTION
'Comparison AGG_TOKEN-AGG_TOKEN not implemented, should be replaced by ProvSQL behavior';
2959 LEFTARG=AGG_TOKEN, RIGHTARG=AGG_TOKEN,
PROCEDURE=agg_token_comp_agg_token,
2960 COMMUTATOR = >, NEGATOR = >=
2964 LEFTARG=AGG_TOKEN, RIGHTARG=AGG_TOKEN,
PROCEDURE=agg_token_comp_agg_token,
2965 COMMUTATOR = >=, NEGATOR = >
2969 LEFTARG=AGG_TOKEN, RIGHTARG=AGG_TOKEN,
PROCEDURE=agg_token_comp_agg_token,
2970 COMMUTATOR = <, NEGATOR = <=
2974 LEFTARG=AGG_TOKEN, RIGHTARG=AGG_TOKEN,
PROCEDURE=agg_token_comp_agg_token,
2975 COMMUTATOR = <=, NEGATOR = <
2979 LEFTARG=AGG_TOKEN, RIGHTARG=AGG_TOKEN,
PROCEDURE=agg_token_comp_agg_token,
2980 COMMUTATOR = =, NEGATOR = <>
2984 LEFTARG=AGG_TOKEN, RIGHTARG=AGG_TOKEN,
PROCEDURE=agg_token_comp_agg_token,
2985 COMMUTATOR = <>, NEGATOR = =
2995CREATE OR REPLACE
FUNCTION agg_token_comp_text(a AGG_TOKEN, b TEXT)
2998IMMUTABLE STRICT PARALLEL SAFE
3001 RAISE EXCEPTION
'Comparison AGG_TOKEN-TEXT not implemented, should be replaced by ProvSQL behavior';
3011CREATE OR REPLACE
FUNCTION text_comp_agg_token(a TEXT, b AGG_TOKEN)
3014IMMUTABLE STRICT PARALLEL SAFE
3017 RAISE EXCEPTION
'Comparison TEXT-AGG_TOKEN not implemented, should be replaced by ProvSQL behavior';
3023 LEFTARG = AGG_TOKEN,
3025 PROCEDURE = agg_token_comp_text,
3032 RIGHTARG = AGG_TOKEN,
3033 PROCEDURE = text_comp_agg_token,
3040 LEFTARG = AGG_TOKEN,
3042 PROCEDURE = agg_token_comp_text,
3049 RIGHTARG = AGG_TOKEN,
3050 PROCEDURE = text_comp_agg_token,
3088CREATE TYPE random_variable;
3091CREATE OR REPLACE
FUNCTION random_variable_in(CSTRING)
3092 RETURNS random_variable
3093 AS 'provsql',
'random_variable_in' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
3096CREATE OR REPLACE
FUNCTION random_variable_out(random_variable)
3098 AS 'provsql',
'random_variable_out' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
3100CREATE TYPE random_variable (
3101 internallength =
16,
3102 input = random_variable_in,
3103 output = random_variable_out,
3108CREATE OR REPLACE
FUNCTION random_variable_make(tok UUID)
3109 RETURNS random_variable
3110 AS 'provsql',
'random_variable_make' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
3123CREATE CAST (random_variable
AS UUID)
WITHOUT FUNCTION AS ASSIGNMENT;
3124CREATE CAST (UUID
AS random_variable)
WITHOUT FUNCTION;
3139CREATE OR REPLACE
FUNCTION agg_token_to_random_variable(a AGG_TOKEN)
3140 RETURNS random_variable
AS
3141$$
SELECT provsql.random_variable_make(provsql.agg_token_uuid($
1)); $$
3142 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
3143CREATE CAST (AGG_TOKEN
AS random_variable)
3144 WITH FUNCTION agg_token_to_random_variable(AGG_TOKEN)
AS IMPLICIT;
3154CREATE OR REPLACE
FUNCTION is_finite_float8(x
double precision)
3157 SELECT $
1 <>
'NaN'::float8
AND $
1 <>
'Infinity'::float8
AND $
1 <>
'-Infinity'::float8;
3158$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
3186CREATE OR REPLACE
FUNCTION rv_parametric2(
3188 p1_tok UUID, p1_lit
double precision,
3189 p2_tok UUID, p2_lit
double precision)
3190 RETURNS random_variable
AS
3194 wires UUID[] :=
ARRAY[]::UUID[];
3198 IF p1_tok
IS NOT NULL THEN
3199 wires := wires || p1_tok;
3200 s1 :=
'$' || (array_length(wires,
1) -
1);
3202 IF NOT provsql.is_finite_float8(p1_lit)
THEN
3203 RAISE EXCEPTION
'provsql.%: literal parameter must be finite (got %)',
3208 IF p2_tok
IS NOT NULL THEN
3209 wires := wires || p2_tok;
3210 s2 :=
'$' || (array_length(wires,
1) -
1);
3212 IF NOT provsql.is_finite_float8(p2_lit)
THEN
3213 RAISE EXCEPTION
'provsql.%: literal parameter must be finite (got %)',
3218 token := public.uuid_generate_v4();
3219 PERFORM provsql.create_gate(token,
'rv', wires);
3220 PERFORM provsql.set_extra(token, family ||
':' || s1 ||
',' || s2);
3221 RETURN provsql.random_variable_make(token);
3223$$
LANGUAGE plpgsql VOLATILE PARALLEL SAFE;
3228CREATE OR REPLACE
FUNCTION rv_parametric1(family TEXT, p_tok UUID)
3229 RETURNS random_variable
AS
3234 token := public.uuid_generate_v4();
3235 PERFORM provsql.create_gate(token,
'rv',
ARRAY[p_tok]);
3236 PERFORM provsql.set_extra(token, family ||
':$0');
3237 RETURN provsql.random_variable_make(token);
3239$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
3252CREATE OR REPLACE
FUNCTION normal(mu random_variable, sigma
double precision)
3253 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'normal', ($
1)::UUID,
NULL,
NULL, $
2); $$
3254 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3255CREATE OR REPLACE
FUNCTION normal(mu
double precision, sigma random_variable)
3256 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'normal',
NULL, $
1, ($
2)::UUID,
NULL); $$
3257 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3258CREATE OR REPLACE
FUNCTION normal(mu random_variable, sigma random_variable)
3259 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'normal', ($
1)::UUID,
NULL, ($
2)::UUID,
NULL); $$
3260 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3263CREATE OR REPLACE
FUNCTION logistic(mu random_variable, s
double precision)
3264 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'logistic', ($
1)::UUID,
NULL,
NULL, $
2); $$
3265 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3266CREATE OR REPLACE
FUNCTION logistic(mu
double precision, s random_variable)
3267 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'logistic',
NULL, $
1, ($
2)::UUID,
NULL); $$
3268 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3269CREATE OR REPLACE
FUNCTION logistic(mu random_variable, s random_variable)
3270 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'logistic', ($
1)::UUID,
NULL, ($
2)::UUID,
NULL); $$
3271 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3274CREATE OR REPLACE
FUNCTION uniform(a random_variable, b
double precision)
3275 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'uniform', ($
1)::UUID,
NULL,
NULL, $
2); $$
3276 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3277CREATE OR REPLACE
FUNCTION uniform(a
double precision, b random_variable)
3278 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'uniform',
NULL, $
1, ($
2)::UUID,
NULL); $$
3279 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3280CREATE OR REPLACE
FUNCTION uniform(a random_variable, b random_variable)
3281 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'uniform', ($
1)::UUID,
NULL, ($
2)::UUID,
NULL); $$
3282 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3285CREATE OR REPLACE
FUNCTION exponential(lambda random_variable)
3286 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric1(
'exponential', ($
1)::UUID); $$
3287 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3290CREATE OR REPLACE
FUNCTION gamma(k random_variable, lambda
double precision)
3291 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'gamma', ($
1)::UUID,
NULL,
NULL, $
2); $$
3292 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3293CREATE OR REPLACE
FUNCTION gamma(k
double precision, lambda random_variable)
3294 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'gamma',
NULL, $
1, ($
2)::UUID,
NULL); $$
3295 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3296CREATE OR REPLACE
FUNCTION gamma(k random_variable, lambda random_variable)
3297 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'gamma', ($
1)::UUID,
NULL, ($
2)::UUID,
NULL); $$
3298 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3301CREATE OR REPLACE
FUNCTION lognormal(mu random_variable, sigma
double precision)
3302 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'lognormal', ($
1)::UUID,
NULL,
NULL, $
2); $$
3303 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3304CREATE OR REPLACE
FUNCTION lognormal(mu
double precision, sigma random_variable)
3305 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'lognormal',
NULL, $
1, ($
2)::UUID,
NULL); $$
3306 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3307CREATE OR REPLACE
FUNCTION lognormal(mu random_variable, sigma random_variable)
3308 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'lognormal', ($
1)::UUID,
NULL, ($
2)::UUID,
NULL); $$
3309 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3312CREATE OR REPLACE
FUNCTION weibull(k random_variable, lambda
double precision)
3313 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'weibull', ($
1)::UUID,
NULL,
NULL, $
2); $$
3314 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3315CREATE OR REPLACE
FUNCTION weibull(k
double precision, lambda random_variable)
3316 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'weibull',
NULL, $
1, ($
2)::UUID,
NULL); $$
3317 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3318CREATE OR REPLACE
FUNCTION weibull(k random_variable, lambda random_variable)
3319 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'weibull', ($
1)::UUID,
NULL, ($
2)::UUID,
NULL); $$
3320 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3323CREATE OR REPLACE
FUNCTION pareto(xm random_variable, alpha
double precision)
3324 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'pareto', ($
1)::UUID,
NULL,
NULL, $
2); $$
3325 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3326CREATE OR REPLACE
FUNCTION pareto(xm
double precision, alpha random_variable)
3327 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'pareto',
NULL, $
1, ($
2)::UUID,
NULL); $$
3328 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3329CREATE OR REPLACE
FUNCTION pareto(xm random_variable, alpha random_variable)
3330 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'pareto', ($
1)::UUID,
NULL, ($
2)::UUID,
NULL); $$
3331 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3334CREATE OR REPLACE
FUNCTION beta(alpha random_variable, beta
double precision)
3335 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'beta', ($
1)::UUID,
NULL,
NULL, $
2); $$
3336 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3337CREATE OR REPLACE
FUNCTION beta(alpha
double precision, beta random_variable)
3338 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'beta',
NULL, $
1, ($
2)::UUID,
NULL); $$
3339 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3340CREATE OR REPLACE
FUNCTION beta(alpha random_variable, beta random_variable)
3341 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'beta', ($
1)::UUID,
NULL, ($
2)::UUID,
NULL); $$
3342 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3345CREATE OR REPLACE
FUNCTION inverse_gamma(alpha random_variable, beta
double precision)
3346 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'inverse_gamma', ($
1)::UUID,
NULL,
NULL, $
2); $$
3347 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3348CREATE OR REPLACE
FUNCTION inverse_gamma(alpha
double precision, beta random_variable)
3349 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'inverse_gamma',
NULL, $
1, ($
2)::UUID,
NULL); $$
3350 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3351CREATE OR REPLACE
FUNCTION inverse_gamma(alpha random_variable, beta random_variable)
3352 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'inverse_gamma', ($
1)::UUID,
NULL, ($
2)::UUID,
NULL); $$
3353 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3356CREATE OR REPLACE
FUNCTION inverse_gaussian(mu random_variable, lambda
double precision)
3357 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'inverse_gaussian', ($
1)::UUID,
NULL,
NULL, $
2); $$
3358 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3359CREATE OR REPLACE
FUNCTION inverse_gaussian(mu
double precision, lambda random_variable)
3360 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'inverse_gaussian',
NULL, $
1, ($
2)::UUID,
NULL); $$
3361 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3362CREATE OR REPLACE
FUNCTION inverse_gaussian(mu random_variable, lambda random_variable)
3363 RETURNS random_variable
AS $$
SELECT provsql.rv_parametric2(
'inverse_gaussian', ($
1)::UUID,
NULL, ($
2)::UUID,
NULL); $$
3364 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3394CREATE OR REPLACE
FUNCTION normal(mu
double precision, sigma
double precision)
3395 RETURNS random_variable
AS
3400 IF NOT provsql.is_finite_float8(mu)
OR NOT provsql.is_finite_float8(sigma)
THEN
3401 RAISE EXCEPTION
'provsql.normal: parameters must be finite (got mu=%, sigma=%)', mu, sigma;
3404 RAISE EXCEPTION
'provsql.normal: sigma must be non-negative (got %)', sigma;
3407 RETURN provsql.as_random(mu);
3409 token := public.uuid_generate_v4();
3410 PERFORM provsql.create_gate(token,
'rv');
3411 PERFORM provsql.set_extra(token,
'normal:' || mu ||
',' || sigma);
3412 RETURN provsql.random_variable_make(token);
3414$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
3435CREATE OR REPLACE
FUNCTION logistic(mu
double precision, s
double precision)
3436 RETURNS random_variable
AS
3441 IF NOT provsql.is_finite_float8(mu)
OR NOT provsql.is_finite_float8(s)
THEN
3442 RAISE EXCEPTION
'provsql.logistic: parameters must be finite (got mu=%, s=%)', mu, s;
3445 RAISE EXCEPTION
'provsql.logistic: scale s must be non-negative (got %)', s;
3448 RETURN provsql.as_random(mu);
3450 token := public.uuid_generate_v4();
3451 PERFORM provsql.create_gate(token,
'rv');
3452 PERFORM provsql.set_extra(token,
'logistic:' || mu ||
',' || s);
3453 RETURN provsql.random_variable_make(token);
3455$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
3472CREATE OR REPLACE
FUNCTION uniform(a
double precision, b
double precision)
3473 RETURNS random_variable
AS
3478 IF NOT provsql.is_finite_float8(a)
OR NOT provsql.is_finite_float8(b)
THEN
3479 RAISE EXCEPTION
'provsql.uniform: bounds must be finite (got a=%, b=%)', a, b;
3482 RAISE EXCEPTION
'provsql.uniform: a must be <= b (got a=%, b=%)', a, b;
3485 RETURN provsql.as_random(a);
3487 token := public.uuid_generate_v4();
3488 PERFORM provsql.create_gate(token,
'rv');
3489 PERFORM provsql.set_extra(token,
'uniform:' || a ||
',' || b);
3490 RETURN provsql.random_variable_make(token);
3492$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
3507CREATE OR REPLACE
FUNCTION exponential(lambda
double precision)
3508 RETURNS random_variable
AS
3513 IF NOT provsql.is_finite_float8(lambda)
THEN
3514 RAISE EXCEPTION
'provsql.exponential: lambda must be finite (got %)', lambda;
3517 RAISE EXCEPTION
'provsql.exponential: lambda must be strictly positive (got %)', lambda;
3519 token := public.uuid_generate_v4();
3520 PERFORM provsql.create_gate(token,
'rv');
3521 PERFORM provsql.set_extra(token,
'exponential:' || lambda);
3522 RETURN provsql.random_variable_make(token);
3524$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
3549CREATE OR REPLACE
FUNCTION erlang(k
INTEGER, lambda
double precision)
3550 RETURNS random_variable
AS
3556 RAISE EXCEPTION
'provsql.erlang: k must be >= 1 (got %)', k;
3558 IF NOT provsql.is_finite_float8(lambda)
THEN
3559 RAISE EXCEPTION
'provsql.erlang: lambda must be finite (got %)', lambda;
3562 RAISE EXCEPTION
'provsql.erlang: lambda must be strictly positive (got %)', lambda;
3565 RETURN provsql.exponential(lambda);
3567 token := public.uuid_generate_v4();
3568 PERFORM provsql.create_gate(token,
'rv');
3569 PERFORM provsql.set_extra(token,
'erlang:' || k ||
',' || lambda);
3570 RETURN provsql.random_variable_make(token);
3572$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
3595CREATE OR REPLACE
FUNCTION gamma(k
double precision, lambda
double precision)
3596 RETURNS random_variable
AS
3601 IF NOT provsql.is_finite_float8(k)
THEN
3602 RAISE EXCEPTION
'provsql.gamma: k must be finite (got %)', k;
3605 RAISE EXCEPTION
'provsql.gamma: k must be strictly positive (got %)', k;
3607 IF NOT provsql.is_finite_float8(lambda)
THEN
3608 RAISE EXCEPTION
'provsql.gamma: lambda must be finite (got %)', lambda;
3611 RAISE EXCEPTION
'provsql.gamma: lambda must be strictly positive (got %)', lambda;
3613 IF k = floor(k)
AND k <=
2147483647 THEN
3614 RETURN provsql.erlang(k::
INTEGER, lambda);
3616 token := public.uuid_generate_v4();
3617 PERFORM provsql.create_gate(token,
'rv');
3618 PERFORM provsql.set_extra(token,
'gamma:' || k ||
',' || lambda);
3619 RETURN provsql.random_variable_make(token);
3621$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
3636CREATE OR REPLACE
FUNCTION chi_squared(k
double precision)
3637 RETURNS random_variable
AS
3640 IF NOT provsql.is_finite_float8(k)
THEN
3641 RAISE EXCEPTION
'provsql.chi_squared: k must be finite (got %)', k;
3644 RAISE EXCEPTION
'provsql.chi_squared: k must be strictly positive (got %)', k;
3646 RETURN provsql.gamma(k /
2,
0.
5);
3648$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
3670CREATE OR REPLACE
FUNCTION lognormal(mu
double precision, sigma
double precision)
3671 RETURNS random_variable
AS
3676 IF NOT provsql.is_finite_float8(mu)
OR NOT provsql.is_finite_float8(sigma)
THEN
3677 RAISE EXCEPTION
'provsql.lognormal: parameters must be finite (got mu=%, sigma=%)', mu, sigma;
3680 RAISE EXCEPTION
'provsql.lognormal: sigma must be non-negative (got %)', sigma;
3683 RETURN provsql.as_random(exp(mu));
3685 token := public.uuid_generate_v4();
3686 PERFORM provsql.create_gate(token,
'rv');
3687 PERFORM provsql.set_extra(token,
'lognormal:' || mu ||
',' || sigma);
3688 RETURN provsql.random_variable_make(token);
3690$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
3711CREATE OR REPLACE
FUNCTION weibull(k
double precision, lambda
double precision)
3712 RETURNS random_variable
AS
3717 IF NOT provsql.is_finite_float8(k)
OR NOT provsql.is_finite_float8(lambda)
THEN
3718 RAISE EXCEPTION
'provsql.weibull: parameters must be finite (got k=%, lambda=%)', k, lambda;
3720 IF k <=
0 OR lambda <=
0 THEN
3721 RAISE EXCEPTION
'provsql.weibull: parameters must be strictly positive (got k=%, lambda=%)', k, lambda;
3724 RETURN provsql.exponential(
1 / lambda);
3726 token := public.uuid_generate_v4();
3727 PERFORM provsql.create_gate(token,
'rv');
3728 PERFORM provsql.set_extra(token,
'weibull:' || k ||
',' || lambda);
3729 RETURN provsql.random_variable_make(token);
3731$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
3751CREATE OR REPLACE
FUNCTION pareto(xm
double precision, alpha
double precision)
3752 RETURNS random_variable
AS
3757 IF NOT provsql.is_finite_float8(xm)
OR NOT provsql.is_finite_float8(alpha)
THEN
3758 RAISE EXCEPTION
'provsql.pareto: parameters must be finite (got xm=%, alpha=%)', xm, alpha;
3760 IF xm <=
0 OR alpha <=
0 THEN
3761 RAISE EXCEPTION
'provsql.pareto: parameters must be strictly positive (got xm=%, alpha=%)', xm, alpha;
3763 token := public.uuid_generate_v4();
3764 PERFORM provsql.create_gate(token,
'rv');
3765 PERFORM provsql.set_extra(token,
'pareto:' || xm ||
',' || alpha);
3766 RETURN provsql.random_variable_make(token);
3768$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
3789CREATE OR REPLACE
FUNCTION inverse_gamma(alpha
double precision, beta
double precision)
3790 RETURNS random_variable
AS
3795 IF NOT provsql.is_finite_float8(alpha)
OR NOT provsql.is_finite_float8(beta)
THEN
3796 RAISE EXCEPTION
'provsql.inverse_gamma: parameters must be finite (got alpha=%, beta=%)', alpha, beta;
3798 IF alpha <=
0 OR beta <=
0 THEN
3799 RAISE EXCEPTION
'provsql.inverse_gamma: parameters must be strictly positive (got alpha=%, beta=%)', alpha, beta;
3801 token := public.uuid_generate_v4();
3802 PERFORM provsql.create_gate(token,
'rv');
3803 PERFORM provsql.set_extra(token,
'inverse_gamma:' || alpha ||
',' || beta);
3804 RETURN provsql.random_variable_make(token);
3806$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
3827CREATE OR REPLACE
FUNCTION inverse_gaussian(mu
double precision, lambda
double precision)
3828 RETURNS random_variable
AS
3833 IF NOT provsql.is_finite_float8(mu)
OR NOT provsql.is_finite_float8(lambda)
THEN
3834 RAISE EXCEPTION
'provsql.inverse_gaussian: parameters must be finite (got mu=%, lambda=%)', mu, lambda;
3836 IF mu <=
0 OR lambda <=
0 THEN
3837 RAISE EXCEPTION
'provsql.inverse_gaussian: parameters must be strictly positive (got mu=%, lambda=%)', mu, lambda;
3839 token := public.uuid_generate_v4();
3840 PERFORM provsql.create_gate(token,
'rv');
3841 PERFORM provsql.set_extra(token,
'inverse_gaussian:' || mu ||
',' || lambda);
3842 RETURN provsql.random_variable_make(token);
3844$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
3851CREATE OR REPLACE
FUNCTION wald(mu
double precision, lambda
double precision)
3852 RETURNS random_variable
AS
3854 SELECT provsql.inverse_gaussian(mu, lambda);
3855$$
LANGUAGE sql VOLATILE PARALLEL SAFE;
3875CREATE OR REPLACE
FUNCTION categorical_from_log_pmf(
3876 outcomes
double precision[], log_pmf
double precision[])
3877 RETURNS random_variable
AS
3880 n
INT := array_length(outcomes,
1);
3881 max_lp
double precision :=
'-Infinity';
3882 kept_o
double precision[] :=
'{}';
3883 kept_p
double precision[] :=
'{}';
3884 total
double precision :=
0;
3888 IF n
IS NULL OR n =
0 OR n <> coalesce(array_length(log_pmf,
1),
0)
THEN
3889 RAISE EXCEPTION
'provsql.categorical_from_log_pmf: outcomes and log_pmf must be non-empty arrays of the same length';
3892 IF log_pmf[i] > max_lp
THEN max_lp := log_pmf[i];
END IF;
3894 IF max_lp =
'-Infinity' THEN
3895 RAISE EXCEPTION
'provsql.categorical_from_log_pmf: all masses are zero';
3898 v := exp(log_pmf[i] - max_lp);
3900 kept_o := array_append(kept_o, outcomes[i]);
3901 kept_p := array_append(kept_p, v);
3905 FOR i
IN 1..array_length(kept_p,
1) LOOP
3906 kept_p[i] := kept_p[i] / total;
3908 RETURN provsql.categorical(kept_p, kept_o);
3910$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
3927CREATE OR REPLACE
FUNCTION poisson(lambda
double precision)
3928 RETURNS random_variable
AS
3933 outcomes
double precision[] :=
'{}';
3934 lps
double precision[] :=
'{}';
3935 lp
double precision :=
0;
3938 IF NOT provsql.is_finite_float8(lambda)
OR lambda <
0 THEN
3939 RAISE EXCEPTION
'provsql.poisson: lambda must be finite and non-negative (got %)', lambda;
3942 RETURN provsql.as_random(
0);
3944 lo := greatest(
0, floor(lambda -
12 * sqrt(lambda)))::
INT;
3945 hi := ceil(lambda +
12 * sqrt(lambda))::
INT +
30;
3946 IF hi - lo +
1 >
10000 THEN
3947 RAISE EXCEPTION
'provsql.poisson: support window of % outcomes exceeds 10000; approximate with normal(%, sqrt(%))', hi - lo +
1, lambda, lambda;
3952 lp := lp + ln(lambda) - ln(k::
double precision);
3954 outcomes := array_append(outcomes, k::
double precision);
3955 lps := array_append(lps, lp);
3959 outcomes := array_prepend(
0::
double precision, outcomes);
3960 lps := array_prepend(-lambda, lps);
3962 RETURN provsql.categorical_from_log_pmf(outcomes, lps);
3964$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
3977CREATE OR REPLACE
FUNCTION poisson(lambda random_variable)
3978 RETURNS random_variable
AS
3979$$
SELECT provsql.rv_parametric1(
'poisson', ($
1)::UUID); $$
3980 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
3999CREATE OR REPLACE
FUNCTION beta(alpha
double precision, beta
double precision)
4000 RETURNS random_variable
AS
4005 IF NOT provsql.is_finite_float8(alpha)
OR NOT provsql.is_finite_float8(beta)
THEN
4006 RAISE EXCEPTION
'provsql.beta: parameters must be finite (got alpha=%, beta=%)', alpha, beta;
4008 IF alpha <=
0 OR beta <=
0 THEN
4009 RAISE EXCEPTION
'provsql.beta: parameters must be strictly positive (got alpha=%, beta=%)', alpha, beta;
4011 IF alpha =
1 AND beta =
1 THEN
4012 RETURN provsql.uniform(
0,
1);
4014 token := public.uuid_generate_v4();
4015 PERFORM provsql.create_gate(token,
'rv');
4016 PERFORM provsql.set_extra(token,
'beta:' || alpha ||
',' || beta);
4017 RETURN provsql.random_variable_make(token);
4019$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
4032CREATE OR REPLACE
FUNCTION binomial(n
INTEGER, p
double precision)
4033 RETURNS random_variable
AS
4036 outcomes
double precision[] :=
'{}';
4037 lps
double precision[] :=
'{}';
4038 lp
double precision;
4041 IF n
IS NULL OR n <
0 THEN
4042 RAISE EXCEPTION
'provsql.binomial: n must be non-negative (got %)', n;
4044 IF NOT provsql.is_finite_float8(p)
OR p <
0 OR p >
1 THEN
4045 RAISE EXCEPTION
'provsql.binomial: p must be in [0, 1] (got %)', p;
4048 RAISE EXCEPTION
'provsql.binomial: n = % exceeds 10000; approximate with normal(n*p, sqrt(n*p*(1-p)))', n;
4050 IF n =
0 OR p =
0 THEN
4051 RETURN provsql.as_random(
0);
4054 RETURN provsql.as_random(n);
4056 lp := n * ln(
1 - p);
4057 outcomes := array_append(outcomes,
0::
double precision);
4058 lps := array_append(lps, lp);
4059 FOR k
IN 0..(n -
1) LOOP
4060 lp := lp + ln((n - k)::
double precision / (k +
1)) + ln(p / (
1 - p));
4061 outcomes := array_append(outcomes, (k +
1)::
double precision);
4062 lps := array_append(lps, lp);
4064 RETURN provsql.categorical_from_log_pmf(outcomes, lps);
4066$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
4079CREATE OR REPLACE
FUNCTION binomial(n
INTEGER, p random_variable)
4080 RETURNS random_variable
AS
4081$$
SELECT provsql.rv_parametric2(
'binomial',
NULL, $
1::
double precision,
4082 ($
2)::UUID,
NULL); $$
4083 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
4095CREATE OR REPLACE
FUNCTION geometric(p
double precision)
4096 RETURNS random_variable
AS
4100 outcomes
double precision[] :=
'{}';
4101 lps
double precision[] :=
'{}';
4104 IF NOT provsql.is_finite_float8(p)
OR p <=
0 OR p >
1 THEN
4105 RAISE EXCEPTION
'provsql.geometric: p must be in (0, 1] (got %)', p;
4108 RETURN provsql.as_random(
1);
4110 k_max :=
1 + ceil(ln(1e-15) / ln(
1 - p))::
INT;
4111 IF k_max >
10000 THEN
4112 RAISE EXCEPTION
'provsql.geometric: support window of % outcomes exceeds 10000 (p = % is too small); approximate with exponential(%)', k_max, p, p;
4114 FOR k
IN 1..k_max LOOP
4115 outcomes := array_append(outcomes, k::
double precision);
4116 lps := array_append(lps, (k -
1) * ln(
1 - p) + ln(p));
4118 RETURN provsql.categorical_from_log_pmf(outcomes, lps);
4120$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
4130CREATE OR REPLACE
FUNCTION geometric(p random_variable)
4131 RETURNS random_variable
AS
4132$$
SELECT provsql.rv_parametric1(
'geometric', ($
1)::UUID); $$
4133 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
4149CREATE OR REPLACE
FUNCTION hypergeometric(pop_n
INTEGER, k_marked
INTEGER, n
INTEGER)
4150 RETURNS random_variable
AS
4155 outcomes
double precision[] :=
'{}';
4156 lps
double precision[] :=
'{}';
4157 lp
double precision :=
0;
4160 IF pop_n
IS NULL OR k_marked
IS NULL OR n
IS NULL
4161 OR pop_n <
0 OR k_marked <
0 OR n <
0
4162 OR k_marked > pop_n
OR n > pop_n
THEN
4163 RAISE EXCEPTION
'provsql.hypergeometric: need 0 <= k_marked, n <= pop_n (got pop_n=%, k_marked=%, n=%)', pop_n, k_marked, n;
4165 lo := greatest(
0, n - (pop_n - k_marked));
4166 hi := least(n, k_marked);
4167 IF hi - lo +
1 >
10000 THEN
4168 RAISE EXCEPTION
'provsql.hypergeometric: support window of % outcomes exceeds 10000', hi - lo +
1;
4170 outcomes := array_append(outcomes, lo::
double precision);
4171 lps := array_append(lps, lp);
4172 FOR k
IN lo..(hi -
1) LOOP
4174 lp := lp + ln((k_marked - k)::
double precision * (n - k))
4175 - ln((k +
1)::
double precision * (pop_n - k_marked - n + k +
1));
4176 outcomes := array_append(outcomes, (k +
1)::
double precision);
4177 lps := array_append(lps, lp);
4179 RETURN provsql.categorical_from_log_pmf(outcomes, lps);
4181$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
4196CREATE OR REPLACE
FUNCTION negative_binomial(r
double precision, p
double precision)
4197 RETURNS random_variable
AS
4200 outcomes
double precision[] :=
'{}';
4201 lps
double precision[] :=
'{}';
4202 lp
double precision;
4203 max_lp
double precision;
4204 mean
double precision;
4207 IF NOT provsql.is_finite_float8(r)
OR r <=
0 THEN
4208 RAISE EXCEPTION
'provsql.negative_binomial: r must be finite and strictly positive (got %)', r;
4210 IF NOT provsql.is_finite_float8(p)
OR p <=
0 OR p >
1 THEN
4211 RAISE EXCEPTION
'provsql.negative_binomial: p must be in (0, 1] (got %)', p;
4214 RETURN provsql.as_random(
0);
4216 mean := r * (
1 - p) / p;
4219 outcomes := array_append(outcomes,
0::
double precision);
4220 lps := array_append(lps, lp);
4222 lp := lp + ln((k + r) / (k +
1)) + ln(
1 - p);
4224 IF lp > max_lp
THEN max_lp := lp;
END IF;
4225 outcomes := array_append(outcomes, k::
double precision);
4226 lps := array_append(lps, lp);
4227 EXIT
WHEN k > mean
AND lp < max_lp + ln(1e-15);
4229 RAISE EXCEPTION
'provsql.negative_binomial: support window exceeds 10000 outcomes (r=%, p=%)', r, p;
4232 RETURN provsql.categorical_from_log_pmf(outcomes, lps);
4234$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
4243CREATE OR REPLACE
FUNCTION negative_binomial(r
double precision, p random_variable)
4244 RETURNS random_variable
AS
4245$$
SELECT provsql.rv_parametric2(
'negative_binomial',
NULL, $
1, ($
2)::UUID,
NULL); $$
4246 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
4247CREATE OR REPLACE
FUNCTION negative_binomial(r random_variable, p
double precision)
4248 RETURNS random_variable
AS
4249$$
SELECT provsql.rv_parametric2(
'negative_binomial', ($
1)::UUID,
NULL,
NULL, $
2); $$
4250 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
4251CREATE OR REPLACE
FUNCTION negative_binomial(r random_variable, p random_variable)
4252 RETURNS random_variable
AS
4253$$
SELECT provsql.rv_parametric2(
'negative_binomial', ($
1)::UUID,
NULL, ($
2)::UUID,
NULL); $$
4254 LANGUAGE sql STRICT VOLATILE PARALLEL SAFE;
4268CREATE OR REPLACE
FUNCTION rv_families()
4269 RETURNS TABLE(name TEXT, nparams
INT, param_names TEXT[], label TEXT)
AS
4270 'provsql',
'rv_families' LANGUAGE C STABLE PARALLEL SAFE;
4312CREATE OR REPLACE
FUNCTION mixture(
4313 p UUID, x random_variable, y random_variable)
4314 RETURNS random_variable
AS
4318 p_kind provsql.PROVENANCE_GATE;
4321 x_kind provsql.PROVENANCE_GATE;
4322 y_kind provsql.PROVENANCE_GATE;
4324 p_kind := provsql.get_gate_type(p);
4325 IF p_kind
NOT IN (
'input',
'mulinput',
'update',
4326 'plus',
'times',
'monus',
4327 'project',
'eq',
'cmp',
4329 RAISE EXCEPTION
'provsql.mixture: p must be a Boolean gate '
4330 '(input/mulinput/update/plus/times/monus/project/eq/cmp/zero/one), got %', p_kind;
4333 x_uuid := (x)::UUID;
4334 y_uuid := (y)::UUID;
4335 x_kind := provsql.get_gate_type(x_uuid);
4336 y_kind := provsql.get_gate_type(y_uuid);
4337 IF x_kind
NOT IN (
'rv',
'value',
'arith',
'mixture')
THEN
4338 RAISE EXCEPTION
'provsql.mixture: x must be a scalar RV root (rv / value / arith / mixture), got %', x_kind;
4340 IF y_kind
NOT IN (
'rv',
'value',
'arith',
'mixture')
THEN
4341 RAISE EXCEPTION
'provsql.mixture: y must be a scalar RV root (rv / value / arith / mixture), got %', y_kind;
4344 token := public.uuid_generate_v5(
4345 provsql.uuid_ns_provsql(),
4346 concat(
'mixture', p, x_uuid, y_uuid));
4347 PERFORM provsql.create_gate(token,
'mixture',
ARRAY[p, x_uuid, y_uuid]);
4348 RETURN provsql.random_variable_make(token);
4350$$
LANGUAGE plpgsql STRICT IMMUTABLE PARALLEL SAFE;
4377CREATE OR REPLACE
FUNCTION mixture(
4378 p_value
double precision,
4381 RETURNS random_variable
AS
4386 IF p_value
IS NULL OR p_value <> p_value
OR p_value <
0 OR p_value >
1 THEN
4387 RAISE EXCEPTION
'provsql.mixture: probability must be in [0,1] (got %)', p_value;
4389 p_token := public.uuid_generate_v4();
4390 PERFORM provsql.create_gate(p_token,
'input');
4391 PERFORM provsql.set_prob(p_token, p_value);
4392 RETURN provsql.mixture(p_token, x, y);
4394$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
4428CREATE OR REPLACE
FUNCTION categorical(
4429 probs
double precision[],
4430 outcomes
double precision[])
4431 RETURNS random_variable
AS
4435 p_sum
double precision :=
0.
0;
4440 mul_tokens UUID[] :=
ARRAY[]::UUID[];
4442 pi_i
double precision;
4443 vi_i
double precision;
4445 IF probs
IS NULL OR outcomes
IS NULL THEN
4446 RAISE EXCEPTION
'provsql.categorical: probs and outcomes must be non-null';
4448 n := array_length(probs,
1);
4449 IF n
IS NULL OR n <
1 THEN
4450 RAISE EXCEPTION
'provsql.categorical: probs must be non-empty';
4452 IF array_length(outcomes,
1) <> n
THEN
4453 RAISE EXCEPTION
'provsql.categorical: probs and outcomes must have the same length (got % and %)',
4454 n, array_length(outcomes,
1);
4459 vi_i := outcomes[i];
4464 IF pi_i
IS NULL OR pi_i =
'NaN'::float8
OR pi_i <
0 OR pi_i >
1 THEN
4465 RAISE EXCEPTION
'provsql.categorical: probs[%] must be in [0,1] (got %)', i, pi_i;
4467 IF vi_i
IS NULL OR vi_i =
'NaN'::float8
4468 OR vi_i =
'Infinity'::float8
OR vi_i =
'-Infinity'::float8
THEN
4469 RAISE EXCEPTION
'provsql.categorical: outcomes[%] must be finite (got %)', i, vi_i;
4471 p_sum := p_sum + pi_i;
4473 IF abs(p_sum -
1.
0) > 1e-9
THEN
4474 RAISE EXCEPTION
'provsql.categorical: probs must sum to 1 within 1e-9 (got %)', p_sum;
4486 nb_positive
INTEGER :=
0;
4487 only_idx
INTEGER :=
0;
4490 IF probs[i] >
0.
0 THEN
4491 nb_positive := nb_positive +
1;
4495 IF nb_positive =
1 THEN
4496 RETURN provsql.as_random(outcomes[only_idx]);
4503 key_token := public.uuid_generate_v4();
4504 PERFORM provsql.create_gate(key_token,
'input');
4505 PERFORM provsql.set_prob(key_token,
1.
0);
4512 IF pi_i <=
0.
0 THEN CONTINUE;
END IF;
4513 mul_token := public.uuid_generate_v4();
4514 PERFORM provsql.create_gate(mul_token,
'mulinput',
ARRAY[key_token]);
4515 PERFORM provsql.set_prob(mul_token, pi_i);
4516 PERFORM provsql.set_infos(mul_token, (i -
1));
4517 PERFORM provsql.set_extra(mul_token, outcomes[i]::TEXT);
4518 mul_tokens := mul_tokens || mul_token;
4521 mix_wires :=
ARRAY[key_token] || mul_tokens;
4522 mix_token := public.uuid_generate_v4();
4523 PERFORM provsql.create_gate(mix_token,
'mixture', mix_wires);
4524 RETURN provsql.random_variable_make(mix_token);
4526$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
4559CREATE OR REPLACE
FUNCTION gmm(
4560 weights
double precision[],
4561 means
double precision[],
4562 stddevs
double precision[])
4563 RETURNS random_variable
AS
4567 w_sum
double precision :=
0.
0;
4569 acc random_variable :=
NULL;
4570 remaining
double precision :=
0.
0;
4572 IF weights
IS NULL OR means
IS NULL OR stddevs
IS NULL THEN
4573 RAISE EXCEPTION
'provsql.gmm: weights, means, and stddevs must be non-null';
4575 n := array_length(weights,
1);
4576 IF n
IS NULL OR n <
1 THEN
4577 RAISE EXCEPTION
'provsql.gmm: weights must be non-empty';
4579 IF array_length(means,
1) <> n
OR array_length(stddevs,
1) <> n
THEN
4580 RAISE EXCEPTION
'provsql.gmm: weights, means, and stddevs must have the same length (got %, %, %)',
4581 n, array_length(means,
1), array_length(stddevs,
1);
4584 IF weights[i]
IS NULL OR weights[i] =
'NaN'::float8
4585 OR weights[i] <
0 OR weights[i] >
1 THEN
4586 RAISE EXCEPTION
'provsql.gmm: weights[%] must be in [0,1] (got %)',
4589 w_sum := w_sum + weights[i];
4591 IF abs(w_sum -
1.
0) > 1e-9
THEN
4592 RAISE EXCEPTION
'provsql.gmm: weights must sum to 1 within 1e-9 (got %)', w_sum;
4598 FOR i
IN REVERSE n..
1 LOOP
4599 IF weights[i] <=
0.
0 THEN
4603 acc := provsql.normal(means[i], stddevs[i]);
4604 remaining := weights[i];
4606 remaining := remaining + weights[i];
4607 acc := provsql.mixture(least(
1.
0, weights[i] / remaining),
4608 provsql.normal(means[i], stddevs[i]), acc);
4613$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
4640CREATE OR REPLACE
FUNCTION empirical_samples(samples
double precision[])
4641 RETURNS random_variable
AS
4645 sorted
double precision[];
4646 outcomes
double precision[] :=
'{}';
4647 probs
double precision[] :=
'{}';
4649 prev
double precision;
4651 started
BOOLEAN :=
false;
4653 n := array_length(samples,
1);
4654 IF n
IS NULL OR n <
1 THEN
4655 RAISE EXCEPTION
'provsql.empirical_samples: samples must be non-empty';
4657 sorted :=
ARRAY(
SELECT s
FROM unnest(samples)
AS s
ORDER BY 1);
4658 FOREACH v
IN ARRAY sorted LOOP
4659 IF v
IS NULL OR v =
'NaN'::float8
4660 OR v =
'Infinity'::float8
OR v =
'-Infinity'::float8
THEN
4662 'provsql.empirical_samples: samples must be finite (got %)', v;
4664 IF started
AND v = prev
THEN
4668 outcomes := outcomes || prev;
4669 probs := probs || (run::
double precision / n);
4676 outcomes := outcomes || prev;
4677 probs := probs || (run::
double precision / n);
4678 IF array_length(outcomes,
1) >
10000 THEN
4680 'provsql.empirical_samples: at most 10000 distinct values are '
4681 'supported (got %); thin the chain or bin the samples (e.g. with '
4682 'width_bucket)', array_length(outcomes,
1);
4684 RETURN provsql.categorical(probs, outcomes);
4686$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
4716CREATE OR REPLACE
FUNCTION empirical_cdf(grid
double precision[],
4717 cdf
double precision[])
4718 RETURNS random_variable
AS
4723 acc random_variable :=
NULL;
4724 remaining
double precision :=
0.
0;
4726 comp random_variable;
4728 n := array_length(grid,
1);
4729 IF n
IS NULL OR n <
2 THEN
4730 RAISE EXCEPTION
'provsql.empirical_cdf: grid must have at least two entries';
4732 IF array_length(cdf,
1) <> n
THEN
4733 RAISE EXCEPTION
'provsql.empirical_cdf: grid and cdf must have the same length (got % and %)',
4734 n, array_length(cdf,
1);
4737 RAISE EXCEPTION
'provsql.empirical_cdf: at most 10000 grid points are supported (got %)', n;
4740 IF grid[i]
IS NULL OR grid[i] =
'NaN'::float8
4741 OR grid[i] =
'Infinity'::float8
OR grid[i] =
'-Infinity'::float8
THEN
4742 RAISE EXCEPTION
'provsql.empirical_cdf: grid[%] must be finite (got %)', i, grid[i];
4744 IF i >
1 AND NOT grid[i] > grid[i-1]
THEN
4745 RAISE EXCEPTION
'provsql.empirical_cdf: grid must be strictly increasing (grid[%] = %, grid[%] = %)',
4746 i-1, grid[i-1], i, grid[i];
4748 IF cdf[i]
IS NULL OR cdf[i] =
'NaN'::float8
OR cdf[i] <
0 OR cdf[i] >
1 THEN
4749 RAISE EXCEPTION
'provsql.empirical_cdf: cdf[%] must be in [0,1] (got %)', i, cdf[i];
4751 IF i >
1 AND cdf[i] < cdf[i-1]
THEN
4752 RAISE EXCEPTION
'provsql.empirical_cdf: cdf must be non-decreasing (cdf[%] = %, cdf[%] = %)',
4753 i-1, cdf[i-1], i, cdf[i];
4756 IF abs(cdf[n] -
1.
0) > 1e-9
THEN
4757 RAISE EXCEPTION
'provsql.empirical_cdf: cdf must end at 1 within 1e-9 (got %)', cdf[n];
4763 FOR i
IN REVERSE n..
1 LOOP
4764 w :=
CASE WHEN i =
1 THEN cdf[
1]
ELSE cdf[i] - cdf[i-1]
END;
4768 comp :=
CASE WHEN i =
1 THEN provsql.as_random(grid[
1])
4769 ELSE provsql.uniform(grid[i-1], grid[i])
END;
4774 remaining := remaining + w;
4775 acc := provsql.mixture(least(
1.
0, w / remaining), comp, acc);
4780$$
LANGUAGE plpgsql STRICT VOLATILE PARALLEL SAFE;
4801CREATE OR REPLACE
FUNCTION as_random(c
double precision)
4802 RETURNS random_variable
AS
4810 c_canon
double precision := c +
0.
0;
4811 c_text
varchar :=
CAST(c_canon
AS VARCHAR);
4812 token UUID := public.uuid_generate_v5(
4813 provsql.uuid_ns_provsql(), concat(
'value', c_text));
4815 PERFORM provsql.create_gate(token,
'value');
4816 PERFORM provsql.set_extra(token, c_text);
4817 RETURN provsql.random_variable_make(token);
4819$$
LANGUAGE plpgsql STRICT IMMUTABLE PARALLEL SAFE;
4834CREATE CAST (
double precision AS random_variable)
4835 WITH FUNCTION as_random(
double precision)
AS IMPLICIT;
4838CREATE OR REPLACE
FUNCTION as_random(c
INTEGER)
4839 RETURNS random_variable
AS
4840$$
SELECT provsql.as_random(c::
double precision); $$
4841LANGUAGE sql STRICT IMMUTABLE PARALLEL SAFE;
4844CREATE OR REPLACE
FUNCTION as_random(c
NUMERIC)
4845 RETURNS random_variable
AS
4846$$
SELECT provsql.as_random(c::
double precision); $$
4847LANGUAGE sql STRICT IMMUTABLE PARALLEL SAFE;
4850CREATE CAST (
INTEGER AS random_variable)
4851 WITH FUNCTION as_random(
INTEGER)
AS IMPLICIT;
4854CREATE CAST (
NUMERIC AS random_variable)
4855 WITH FUNCTION as_random(
NUMERIC)
AS IMPLICIT;
4887CREATE OR REPLACE
FUNCTION random_variable_plus(
4888 a random_variable, b random_variable)
4889 RETURNS random_variable
AS
4891 SELECT provsql.random_variable_make(
4892 provsql.provenance_arith(
4896$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
4899CREATE OR REPLACE
FUNCTION random_variable_minus(
4900 a random_variable, b random_variable)
4901 RETURNS random_variable
AS
4903 SELECT provsql.random_variable_make(
4904 provsql.provenance_arith(
4908$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
4911CREATE OR REPLACE
FUNCTION random_variable_times(
4912 a random_variable, b random_variable)
4913 RETURNS random_variable
AS
4915 SELECT provsql.random_variable_make(
4916 provsql.provenance_arith(
4920$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
4923CREATE OR REPLACE
FUNCTION random_variable_div(
4924 a random_variable, b random_variable)
4925 RETURNS random_variable
AS
4927 SELECT provsql.random_variable_make(
4928 provsql.provenance_arith(
4932$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
4935CREATE OR REPLACE
FUNCTION random_variable_neg(a random_variable)
4936 RETURNS random_variable
AS
4938 SELECT provsql.random_variable_make(
4939 provsql.provenance_arith(
4942$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
4951CREATE OR REPLACE
FUNCTION random_variable_pow(
4952 a random_variable, b random_variable)
4953 RETURNS random_variable
AS
4955 SELECT provsql.random_variable_make(
4956 provsql.provenance_arith(
4960$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
4969CREATE OR REPLACE
FUNCTION ln(a random_variable)
4970 RETURNS random_variable
AS
4972 SELECT provsql.random_variable_make(
4973 provsql.provenance_arith(
4976$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
4979CREATE OR REPLACE
FUNCTION exp(a random_variable)
4980 RETURNS random_variable
AS
4982 SELECT provsql.random_variable_make(
4983 provsql.provenance_arith(
4986$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
4994CREATE OR REPLACE
FUNCTION pow(a random_variable, b random_variable)
4995 RETURNS random_variable
AS
4997 SELECT provsql.random_variable_pow(a, b);
4998$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5000CREATE OR REPLACE
FUNCTION power(a random_variable, b random_variable)
5001 RETURNS random_variable
AS
5003 SELECT provsql.random_variable_pow(a, b);
5004$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5011CREATE OR REPLACE
FUNCTION sqrt(a random_variable)
5012 RETURNS random_variable
AS
5014 SELECT provsql.random_variable_pow(a, provsql.as_random(
0.
5));
5015$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5025CREATE OR REPLACE
FUNCTION random_variable_cmp_oid(sym TEXT)
5028 SELECT (sym ||
'(double precision,double precision)')::regoperator::oid;
5029$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5045CREATE OR REPLACE
FUNCTION random_variable_cmp_placeholder(
5046 a random_variable, b random_variable)
5050 RAISE EXCEPTION
'random_variable comparison must be rewritten by the '
5051 'ProvSQL planner hook (is provsql.active off?)';
5053$$
LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
5055CREATE OR REPLACE
FUNCTION random_variable_lt(
5056 a random_variable, b random_variable)
RETURNS BOOLEAN AS
5057$$
SELECT provsql.random_variable_cmp_placeholder(a, b); $$
5058LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5060CREATE OR REPLACE
FUNCTION random_variable_le(
5061 a random_variable, b random_variable)
RETURNS BOOLEAN AS
5062$$
SELECT provsql.random_variable_cmp_placeholder(a, b); $$
5063LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5065CREATE OR REPLACE
FUNCTION random_variable_eq(
5066 a random_variable, b random_variable)
RETURNS BOOLEAN AS
5067$$
SELECT provsql.random_variable_cmp_placeholder(a, b); $$
5068LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5070CREATE OR REPLACE
FUNCTION random_variable_ne(
5071 a random_variable, b random_variable)
RETURNS BOOLEAN AS
5072$$
SELECT provsql.random_variable_cmp_placeholder(a, b); $$
5073LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5075CREATE OR REPLACE
FUNCTION random_variable_ge(
5076 a random_variable, b random_variable)
RETURNS BOOLEAN AS
5077$$
SELECT provsql.random_variable_cmp_placeholder(a, b); $$
5078LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5080CREATE OR REPLACE
FUNCTION random_variable_gt(
5081 a random_variable, b random_variable)
RETURNS BOOLEAN AS
5082$$
SELECT provsql.random_variable_cmp_placeholder(a, b); $$
5083LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5091CREATE OR REPLACE
FUNCTION rv_cmp_lt(
5092 a random_variable, b random_variable)
RETURNS UUID
AS
5094 SELECT provsql.provenance_cmp(
5096 provsql.random_variable_cmp_oid(
'<'),
5098$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5101CREATE OR REPLACE
FUNCTION rv_cmp_le(
5102 a random_variable, b random_variable)
RETURNS UUID
AS
5104 SELECT provsql.provenance_cmp(
5106 provsql.random_variable_cmp_oid(
'<='),
5108$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5111CREATE OR REPLACE
FUNCTION rv_cmp_eq(
5112 a random_variable, b random_variable)
RETURNS UUID
AS
5114 SELECT provsql.provenance_cmp(
5116 provsql.random_variable_cmp_oid(
'='),
5118$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5121CREATE OR REPLACE
FUNCTION rv_cmp_ne(
5122 a random_variable, b random_variable)
RETURNS UUID
AS
5124 SELECT provsql.provenance_cmp(
5126 provsql.random_variable_cmp_oid(
'<>'),
5128$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5131CREATE OR REPLACE
FUNCTION rv_cmp_ge(
5132 a random_variable, b random_variable)
RETURNS UUID
AS
5134 SELECT provsql.provenance_cmp(
5136 provsql.random_variable_cmp_oid(
'>='),
5138$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5141CREATE OR REPLACE
FUNCTION rv_cmp_gt(
5142 a random_variable, b random_variable)
RETURNS UUID
AS
5144 SELECT provsql.provenance_cmp(
5146 provsql.random_variable_cmp_oid(
'>'),
5148$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5151 LEFTARG = random_variable,
5152 RIGHTARG = random_variable,
5153 PROCEDURE = random_variable_plus,
5158 LEFTARG = random_variable,
5159 RIGHTARG = random_variable,
5160 PROCEDURE = random_variable_minus
5164 LEFTARG = random_variable,
5165 RIGHTARG = random_variable,
5166 PROCEDURE = random_variable_times,
5171 LEFTARG = random_variable,
5172 RIGHTARG = random_variable,
5173 PROCEDURE = random_variable_div
5178 RIGHTARG = random_variable,
5179 PROCEDURE = random_variable_neg
5183 LEFTARG = random_variable,
5184 RIGHTARG = random_variable,
5185 PROCEDURE = random_variable_pow
5189 LEFTARG = random_variable,
5190 RIGHTARG = random_variable,
5191 PROCEDURE = random_variable_lt,
5197 LEFTARG = random_variable,
5198 RIGHTARG = random_variable,
5199 PROCEDURE = random_variable_le,
5205 LEFTARG = random_variable,
5206 RIGHTARG = random_variable,
5207 PROCEDURE = random_variable_eq,
5213 LEFTARG = random_variable,
5214 RIGHTARG = random_variable,
5215 PROCEDURE = random_variable_ne,
5221 LEFTARG = random_variable,
5222 RIGHTARG = random_variable,
5223 PROCEDURE = random_variable_ge,
5229 LEFTARG = random_variable,
5230 RIGHTARG = random_variable,
5231 PROCEDURE = random_variable_gt,
5250CREATE OR REPLACE
FUNCTION random_variable_btree_cmp(
5251 a random_variable, b random_variable)
RETURNS INTEGER AS
5254 RAISE EXCEPTION
'comparison or ordering of random_variable values is '
5255 'meaningless: a random_variable is a distribution, not a scalar'
5257 'Compare them as a probabilistic event -- in a WHERE / JOIN clause or '
5258 'with probability(x > y); take order statistics with provsql.greatest / '
5259 'provsql.least (or the min / max aggregates); summarise numerically with '
5260 'expected / variance / support.';
5262$$
LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
5269CREATE OPERATOR CLASS random_variable_ops
5270 DEFAULT FOR TYPE random_variable
USING btree
AS
5276 FUNCTION 1 random_variable_btree_cmp(random_variable, random_variable);
5294CREATE OR REPLACE
FUNCTION random_variable_cond(rv random_variable, cond UUID)
5295 RETURNS random_variable
AS
5303 IF cond
IS NULL OR cond = gate_one()
THEN
5313 cond := provsql.evidence_as_observation(cond);
5316 IF get_gate_type(tgt) =
'conditioned'
5317 AND array_length(get_children(tgt),
1) =
2 THEN
5320 ch := get_children(tgt);
5322 ev := provenance_times(ch[
2], cond);
5327 result := public.uuid_generate_v5(uuid_ns_provsql(),
5328 concat(
'conditioned', tgt, ev));
5329 PERFORM create_gate(
result,
'conditioned',
ARRAY[tgt, ev]);
5330 RETURN (
result)::random_variable;
5332$$
LANGUAGE plpgsql
SET search_path=provsql,pg_temp,public
5333 SECURITY DEFINER PARALLEL SAFE;
5336 LEFTARG = random_variable,
5338 PROCEDURE = random_variable_cond
5352CREATE OR REPLACE
FUNCTION random_variable_cond_predicate(
5353 rv random_variable, predicate
BOOLEAN)
RETURNS random_variable
AS
5356 RAISE EXCEPTION
'random_variable | (predicate) must be rewritten by the '
5357 'ProvSQL planner hook: the right operand must be a Boolean combination '
5358 'of random_variable comparisons (is provsql.active off?)';
5360$$
LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
5363 LEFTARG = random_variable,
5365 PROCEDURE = random_variable_cond_predicate
5376CREATE OR REPLACE
FUNCTION rv_conditioned_target(token UUID)
RETURNS UUID
AS
5379 WHEN provsql.get_gate_type(token) =
'conditioned'
5380 AND array_length(provsql.get_children(token),
1) =
2
5381 THEN (provsql.get_children(token))[
1]
5384$$
LANGUAGE sql STABLE PARALLEL SAFE
SET search_path=provsql,pg_temp,public;
5394CREATE OR REPLACE
FUNCTION rv_conditioned_prov(token UUID, prov UUID)
5398 WHEN provsql.get_gate_type(token) =
'conditioned'
5399 AND array_length(provsql.get_children(token),
1) =
2
5400 THEN provsql.provenance_times((provsql.get_children(token))[
2], prov)
5403$$
LANGUAGE sql STABLE PARALLEL SAFE
SET search_path=provsql,pg_temp,public;
5435CREATE OR REPLACE
FUNCTION evidence_as_observation(ev UUID)
RETURNS UUID
AS
5443 IF ev
IS NULL OR provsql.get_gate_type(ev) <>
'cmp' THEN
5446 ch := provsql.get_children(ev);
5447 IF array_length(ch,
1) <>
2 THEN
5453 SELECT info1
INTO i1
FROM provsql.get_infos(ev);
5454 IF (
SELECT oprname
FROM pg_catalog.pg_operator
WHERE oid = i1)
IS DISTINCT FROM '=' THEN
5457 IF provsql.get_gate_type(ch[
1]) =
'rv'
5458 AND provsql.get_gate_type(ch[
2]) =
'value' THEN
5459 leaf := ch[
1]; datum_gate := ch[
2];
5460 ELSIF provsql.get_gate_type(ch[
2]) =
'rv'
5461 AND provsql.get_gate_type(ch[
1]) =
'value' THEN
5462 leaf := ch[
2]; datum_gate := ch[
1];
5466 RETURN provsql.observe((leaf)::random_variable,
5467 provsql.get_extra(datum_gate)::
double precision);
5469$$
LANGUAGE plpgsql VOLATILE
5470 SET search_path=provsql,pg_temp,public SECURITY DEFINER PARALLEL SAFE;
5493CREATE OR REPLACE
FUNCTION observe(x random_variable, datum
double precision)
5497 leaf UUID := (x)::UUID;
5500 IF provsql.get_gate_type(leaf) <>
'rv' THEN
5501 RAISE EXCEPTION
'provsql.observe: the argument must be a bare '
5502 'random-variable leaf (a gate_rv), got a % gate', provsql.get_gate_type(leaf)
5503 USING HINT =
'observe binds a datum to a single distribution leaf; '
5504 'observing a derived quantity (a sum, product, or comparison) needs '
5505 'a change-of-variables density and is out of scope.';
5507 IF NOT provsql.is_finite_float8(datum)
THEN
5508 RAISE EXCEPTION
'provsql.observe: datum must be finite (got %)', datum;
5510 result := public.uuid_generate_v4();
5511 PERFORM provsql.create_gate(
result,
'observe',
ARRAY[leaf]);
5512 PERFORM provsql.set_extra(
result, datum::TEXT);
5515$$
LANGUAGE plpgsql VOLATILE
5516 SET search_path=provsql,pg_temp,public SECURITY DEFINER PARALLEL SAFE;
5525CREATE OR REPLACE
FUNCTION and_agg_sfunc(state UUID, ev UUID)
5528 SELECT provsql.provenance_times(state, ev);
5529$$
LANGUAGE sql PARALLEL SAFE;
5539CREATE AGGREGATE and_agg(UUID) (
5540 SFUNC = and_agg_sfunc,
5552CREATE OR REPLACE
FUNCTION evidence(evidence UUID)
5553 RETURNS double precision
5554 AS 'provsql',
'rv_evidence' LANGUAGE C STRICT PARALLEL SAFE;
5565CREATE OR REPLACE
FUNCTION observe_atoms(evidence UUID)
5568 WITH RECURSIVE walk(tok)
AS (
5572 FROM walk,
LATERAL unnest(provsql.get_children(walk.tok))
AS c
5573 WHERE provsql.get_gate_type(walk.tok) =
'times'
5575 SELECT array_agg(tok
ORDER BY tok)
5577 WHERE provsql.get_gate_type(tok) =
'observe';
5578$$
LANGUAGE sql STABLE PARALLEL SAFE
SET search_path=provsql,pg_temp,public;
5602CREATE OR REPLACE
FUNCTION shapley_observe(
5603 target UUID, evidence UUID, payoff TEXT
DEFAULT 'expected')
5604 RETURNS TABLE(observation UUID,
value double precision)
AS
5610 pv
double precision[];
5612 fact
double precision[];
5619 sh
double precision;
5623 IF payoff
NOT IN (
'expected',
'variance')
THEN
5624 RAISE EXCEPTION
'provsql.shapley_observe: payoff must be ''expected'' or '
5625 '''variance'' (got %)', payoff;
5627 atoms := provsql.observe_atoms(evidence);
5628 n := coalesce(array_length(atoms,
1),
0);
5630 RAISE EXCEPTION
'provsql.shapley_observe: evidence contains no observe() '
5631 'atoms (got a % gate)', provsql.get_gate_type(evidence);
5634 RAISE EXCEPTION
'provsql.shapley_observe: exact attribution over % '
5635 'observations is exponential; capped at 12 (sampling-based '
5636 'attribution is future work)', n;
5640 fact :=
ARRAY[
1::
double precision];
5641 FOR i
IN 1..n LOOP fact := fact || (fact[i] * i);
END LOOP;
5644 pv := array_fill(
NULL::
double precision,
ARRAY[nmasks]);
5645 popc := array_fill(
0,
ARRAY[nmasks]);
5648 FOR mask
IN 0 .. nmasks -
1 LOOP
5649 subset :=
ARRAY[]::UUID[];
5651 FOR i
IN 0 .. n -
1 LOOP
5652 IF (mask >> i) &
1 =
1 THEN
5653 subset := subset || atoms[i +
1];
5657 popc[mask +
1] := cnt;
5659 ev_s := provsql.gate_one();
5661 ev_s := provsql.provenance_times(VARIADIC subset);
5663 IF payoff =
'expected' THEN
5664 pv[mask +
1] := provsql.rv_moment(target,
1,
false, ev_s);
5666 pv[mask +
1] := provsql.rv_moment(target,
2,
true, ev_s);
5671 FOR i
IN 0 .. n -
1 LOOP
5674 FOR mask
IN 0 .. nmasks -
1 LOOP
5675 IF (mask >> i) &
1 =
0 THEN
5676 s_size := popc[mask +
1];
5678 sh := sh + (fact[s_size +
1] * fact[n - s_size] / fact[n +
1])
5679 * (pv[(mask | bit) +
1] - pv[mask +
1]);
5682 observation := atoms[i +
1];
5687$$
LANGUAGE plpgsql VOLATILE
5688 SET search_path=provsql,pg_temp,public SECURITY DEFINER;
5718CREATE OR REPLACE
FUNCTION "greatest"(VARIADIC args random_variable[])
5719 RETURNS random_variable
AS
5724 IF args
IS NULL THEN
5727 SELECT array_agg(
DISTINCT (a)::UUID)
INTO children
5728 FROM unnest(args) a
WHERE a
IS NOT NULL;
5729 IF children
IS NULL OR array_length(children,
1)
IS NULL THEN
5732 IF array_length(children,
1) =
1 THEN
5733 RETURN provsql.random_variable_make(children[
1]);
5735 RETURN provsql.random_variable_make(
5736 provsql.provenance_arith(
5, children));
5738$$
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE;
5740CREATE OR REPLACE
FUNCTION "least"(VARIADIC args random_variable[])
5741 RETURNS random_variable
AS
5746 IF args
IS NULL THEN
5749 SELECT array_agg(
DISTINCT (a)::UUID)
INTO children
5750 FROM unnest(args) a
WHERE a
IS NOT NULL;
5751 IF children
IS NULL OR array_length(children,
1)
IS NULL THEN
5754 IF array_length(children,
1) =
1 THEN
5755 RETURN provsql.random_variable_make(children[
1]);
5757 RETURN provsql.random_variable_make(
5758 provsql.provenance_arith(
6, children));
5760$$
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE;
5771CREATE OR REPLACE
FUNCTION rv_case(
5774RETURNS random_variable
AS
5776 SELECT provsql.random_variable_make(provsql.provenance_case(children));
5777$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5793CREATE OR REPLACE
FUNCTION agg_case(
5798 SELECT provsql.agg_token_make(t, coalesce(provsql.agg_gate_value(t),
0))
5799 FROM (
SELECT provsql.provenance_case(children)
AS t)
AS s;
5800$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5845CREATE OR REPLACE
FUNCTION rv_aggregate_semimod(
5846 prov UUID, rv random_variable)
5847 RETURNS random_variable
AS
5849 SELECT provsql.mixture(prov, rv, provsql.as_random(
0::
double precision));
5850$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5864CREATE OR REPLACE
FUNCTION rv_aggregate_semimod(
5865 prov UUID, rv random_variable,
identity double precision)
5866 RETURNS random_variable
AS
5868 SELECT provsql.mixture(prov, rv, provsql.as_random(
identity));
5869$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5883CREATE OR REPLACE
FUNCTION rv_aggregate_indicator(prov UUID)
5884 RETURNS random_variable
AS
5886 SELECT provsql.rv_aggregate_semimod(prov, provsql.as_random(
1::
double precision));
5887$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
5900CREATE OR REPLACE
FUNCTION rv_aggregate_indicator(prov UUID, rv random_variable)
5901 RETURNS random_variable
AS
5903 SELECT CASE WHEN rv
IS NULL THEN NULL
5904 ELSE provsql.rv_aggregate_indicator(prov)
END;
5905$$
LANGUAGE sql IMMUTABLE PARALLEL SAFE;
5916CREATE OR REPLACE
FUNCTION sum_rv_sfunc(
5917 state UUID[], rv random_variable)
5921 WHEN rv
IS NULL THEN state
5922 ELSE array_append(state, (rv)::UUID)
5924$$
LANGUAGE sql IMMUTABLE PARALLEL SAFE;
5939CREATE OR REPLACE
FUNCTION sum_rv_ffunc(state UUID[])
5940 RETURNS random_variable
AS
5945 IF state
IS NULL OR array_length(state,
1)
IS NULL THEN
5948 IF array_length(state,
1) =
1 THEN
5949 RETURN provsql.random_variable_make(state[
1]);
5951 arith_token := provsql.provenance_arith(
0, state);
5952 RETURN provsql.random_variable_make(arith_token);
5954$$
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE;
5956CREATE AGGREGATE sum(random_variable) (
5957 SFUNC = sum_rv_sfunc,
5960 FINALFUNC = sum_rv_ffunc
5976CREATE OR REPLACE
FUNCTION rv_sum_or_null_ffunc(state UUID[])
5977 RETURNS random_variable
AS
5980 IF state
IS NULL OR array_length(state,
1)
IS NULL THEN
5983 IF array_length(state,
1) =
1 THEN
5984 RETURN provsql.random_variable_make(state[
1]);
5986 RETURN provsql.random_variable_make(
5987 provsql.provenance_arith(
0, state));
5989$$
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE;
5991CREATE AGGREGATE rv_sum_or_null(random_variable) (
5992 SFUNC = sum_rv_sfunc,
5995 FINALFUNC = rv_sum_or_null_ffunc
6022CREATE OR REPLACE
FUNCTION avg_rv_ffunc(state UUID[])
6023 RETURNS random_variable
AS
6030 denom_state UUID[] :=
'{}';
6033 IF state
IS NULL THEN
6036 n := array_length(state,
1);
6041 one_uuid := (provsql.as_random(
1::
double precision))::UUID;
6043 denom_state := array_append(denom_state, one_uuid);
6047 num_token := state[
1];
6048 denom_token := denom_state[
1];
6050 num_token := provsql.provenance_arith(
0, state);
6051 denom_token := provsql.provenance_arith(
0, denom_state);
6054 RETURN provsql.random_variable_make(
6055 provsql.provenance_arith(
6057 ARRAY[num_token, denom_token]));
6059$$
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE;
6061CREATE AGGREGATE avg(random_variable) (
6062 SFUNC = sum_rv_sfunc,
6065 FINALFUNC = avg_rv_ffunc
6093CREATE OR REPLACE
FUNCTION product_rv_ffunc(state UUID[])
6094 RETURNS random_variable
AS
6097 IF state
IS NULL OR array_length(state,
1)
IS NULL THEN
6100 IF array_length(state,
1) =
1 THEN
6101 RETURN provsql.random_variable_make(state[
1]);
6103 RETURN provsql.random_variable_make(
6104 provsql.provenance_arith(
1, state));
6106$$
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE;
6108CREATE AGGREGATE product(random_variable) (
6109 SFUNC = sum_rv_sfunc,
6112 FINALFUNC = product_rv_ffunc
6140CREATE OR REPLACE
FUNCTION extremum_rv_ffunc(
6141 state UUID[], op
INTEGER,
identity double precision)
6142 RETURNS random_variable
AS
6145 IF state
IS NULL OR array_length(state,
1)
IS NULL THEN
6148 IF array_length(state,
1) =
1 THEN
6149 RETURN provsql.random_variable_make(state[
1]);
6151 RETURN provsql.random_variable_make(
6152 provsql.provenance_arith(op, state));
6154$$
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE;
6156CREATE OR REPLACE
FUNCTION max_rv_ffunc(state UUID[])
6157 RETURNS random_variable
AS
6160 SELECT provsql.extremum_rv_ffunc(state,
5,
'-Infinity'::
double precision);
6161$$
LANGUAGE sql IMMUTABLE PARALLEL SAFE;
6163CREATE OR REPLACE
FUNCTION min_rv_ffunc(state UUID[])
6164 RETURNS random_variable
AS
6167 SELECT provsql.extremum_rv_ffunc(state,
6,
'Infinity'::
double precision);
6168$$
LANGUAGE sql IMMUTABLE PARALLEL SAFE;
6170CREATE AGGREGATE max(random_variable) (
6171 SFUNC = sum_rv_sfunc,
6174 FINALFUNC = max_rv_ffunc
6177CREATE AGGREGATE min(random_variable) (
6178 SFUNC = sum_rv_sfunc,
6181 FINALFUNC = min_rv_ffunc
6209CREATE OR REPLACE
FUNCTION rv_stat1_sfunc(state UUID[], x random_variable)
6213 WHEN x
IS NULL THEN state
6214 ELSE state ||
ARRAY[(provsql.as_random(
1::
double precision))::UUID,
6217$$
LANGUAGE sql IMMUTABLE PARALLEL SAFE;
6223CREATE OR REPLACE
FUNCTION rv_stat2_sfunc(
6224 state UUID[], x random_variable, y random_variable)
6228 WHEN x
IS NULL OR y
IS NULL THEN state
6229 ELSE state ||
ARRAY[(provsql.as_random(
1::
double precision))::UUID,
6230 (x)::UUID, (y)::UUID]
6232$$
LANGUAGE sql IMMUTABLE PARALLEL SAFE;
6237CREATE OR REPLACE
FUNCTION rv_stat1_impl_sfunc(
6238 state UUID[], ind random_variable, x random_variable)
6242 WHEN x
IS NULL THEN state
6243 ELSE state ||
ARRAY[coalesce((ind)::UUID,
6244 (provsql.as_random(
1::
double precision))::UUID),
6247$$
LANGUAGE sql IMMUTABLE PARALLEL SAFE;
6251CREATE OR REPLACE
FUNCTION rv_stat2_impl_sfunc(
6252 state UUID[], ind random_variable, x random_variable, y random_variable)
6256 WHEN x
IS NULL OR y
IS NULL THEN state
6257 ELSE state ||
ARRAY[coalesce((ind)::UUID,
6258 (provsql.as_random(
1::
double precision))::UUID),
6259 (x)::UUID, (y)::UUID]
6261$$
LANGUAGE sql IMMUTABLE PARALLEL SAFE;
6278CREATE OR REPLACE
FUNCTION rv_stat_sum_tokens(
6279 state UUID[], stride
INTEGER,
6280 OUT n_tok UUID,
OUT sx_tok UUID,
OUT sxx_tok UUID,
6281 OUT sy_tok UUID,
OUT sxy_tok UUID,
OUT syy_tok UUID)
6285 nrows
INTEGER := coalesce(array_length(state,
1),
0) / stride;
6286 inds UUID[] :=
'{}';
6296 FOR i
IN 1..nrows LOOP
6297 ind := state[(i-1) * stride +
1];
6298 x := state[(i-1) * stride +
2];
6299 inds := array_append(inds, ind);
6300 xs := array_append(xs, provenance_arith(
1,
ARRAY[ind, x]));
6301 xxs := array_append(xxs, provenance_arith(
1,
ARRAY[ind, x, x]));
6303 y := state[(i-1) * stride +
3];
6304 ys := array_append(ys, provenance_arith(
1,
ARRAY[ind, y]));
6305 xys := array_append(xys, provenance_arith(
1,
ARRAY[ind, x, y]));
6306 yys := array_append(yys, provenance_arith(
1,
ARRAY[ind, y, y]));
6309 n_tok := provenance_arith(
0, inds);
6310 sx_tok := provenance_arith(
0, xs);
6311 sxx_tok := provenance_arith(
0, xxs);
6313 sy_tok := provenance_arith(
0, ys);
6314 sxy_tok := provenance_arith(
0, xys);
6315 syy_tok := provenance_arith(
0, yys);
6318$$
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE
6319 SET search_path=provsql,pg_temp,public SECURITY DEFINER;
6323CREATE OR REPLACE
FUNCTION rv_stat_var_pop_token(
6324 n_tok UUID, s_tok UUID, ss_tok UUID)
6327 SELECT provsql.provenance_arith(
2,
ARRAY[
6328 provsql.provenance_arith(
3,
ARRAY[ss_tok, n_tok]),
6329 provsql.provenance_arith(
1,
ARRAY[
6330 provsql.provenance_arith(
3,
ARRAY[s_tok, n_tok]),
6331 provsql.provenance_arith(
3,
ARRAY[s_tok, n_tok])])]);
6332$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
6337CREATE OR REPLACE
FUNCTION rv_stat_var_samp_token(
6338 n_tok UUID, s_tok UUID, ss_tok UUID)
6341 SELECT provsql.provenance_arith(
3,
ARRAY[
6342 provsql.provenance_arith(
2,
ARRAY[
6344 provsql.provenance_arith(
3,
ARRAY[
6345 provsql.provenance_arith(
1,
ARRAY[s_tok, s_tok]), n_tok])]),
6346 provsql.provenance_arith(
2,
ARRAY[
6347 n_tok, (provsql.as_random(
1::
double precision))::UUID])]);
6348$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
6353CREATE OR REPLACE
FUNCTION rv_stat_sqrt_token(v_tok UUID)
6356 SELECT provsql.provenance_arith(
7,
ARRAY[
6357 provsql.provenance_arith(
5,
ARRAY[
6358 v_tok, (provsql.as_random(
0::
double precision))::UUID]),
6359 (provsql.as_random(
0.
5::
double precision))::UUID]);
6360$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
6364CREATE OR REPLACE
FUNCTION rv_stat_covar_pop_token(
6365 n_tok UUID, sx_tok UUID, sy_tok UUID, sxy_tok UUID)
6368 SELECT provsql.provenance_arith(
2,
ARRAY[
6369 provsql.provenance_arith(
3,
ARRAY[sxy_tok, n_tok]),
6370 provsql.provenance_arith(
1,
ARRAY[
6371 provsql.provenance_arith(
3,
ARRAY[sx_tok, n_tok]),
6372 provsql.provenance_arith(
3,
ARRAY[sy_tok, n_tok])])]);
6373$$
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE;
6376CREATE OR REPLACE
FUNCTION covar_pop_rv_ffunc(state UUID[])
6377 RETURNS random_variable
AS
6382 IF state
IS NULL OR array_length(state,
1)
IS NULL THEN
6385 SELECT *
INTO t
FROM rv_stat_sum_tokens(state,
3);
6386 RETURN random_variable_make(
6387 rv_stat_covar_pop_token(t.n_tok, t.sx_tok, t.sy_tok, t.sxy_tok));
6389$$
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE
6390 SET search_path=provsql,pg_temp,public SECURITY DEFINER;
6394CREATE OR REPLACE
FUNCTION covar_samp_rv_ffunc(state UUID[])
6395 RETURNS random_variable
AS
6400 IF state
IS NULL OR array_length(state,
1)
IS NULL THEN
6403 SELECT *
INTO t
FROM rv_stat_sum_tokens(state,
3);
6404 RETURN random_variable_make(
6405 provenance_arith(
3,
ARRAY[
6406 provenance_arith(
2,
ARRAY[
6408 provenance_arith(
3,
ARRAY[
6409 provenance_arith(
1,
ARRAY[t.sx_tok, t.sy_tok]), t.n_tok])]),
6410 provenance_arith(
2,
ARRAY[
6411 t.n_tok, (as_random(
1::
double precision))::UUID])]));
6413$$
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE
6414 SET search_path=provsql,pg_temp,public SECURITY DEFINER;
6420CREATE OR REPLACE
FUNCTION corr_rv_ffunc(state UUID[])
6421 RETURNS random_variable
AS
6428 IF state
IS NULL OR array_length(state,
1)
IS NULL THEN
6431 SELECT *
INTO t
FROM rv_stat_sum_tokens(state,
3);
6432 vx := rv_stat_var_pop_token(t.n_tok, t.sx_tok, t.sxx_tok);
6433 vy := rv_stat_var_pop_token(t.n_tok, t.sy_tok, t.syy_tok);
6434 RETURN random_variable_make(
6435 provenance_arith(
3,
ARRAY[
6436 rv_stat_covar_pop_token(t.n_tok, t.sx_tok, t.sy_tok, t.sxy_tok),
6437 rv_stat_sqrt_token(provenance_arith(
1,
ARRAY[vx, vy]))]));
6439$$
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE
6440 SET search_path=provsql,pg_temp,public SECURITY DEFINER;
6443CREATE OR REPLACE
FUNCTION stddev_pop_rv_ffunc(state UUID[])
6444 RETURNS random_variable
AS
6449 IF state
IS NULL OR array_length(state,
1)
IS NULL THEN
6452 SELECT *
INTO t
FROM rv_stat_sum_tokens(state,
2);
6453 RETURN random_variable_make(
6455 rv_stat_var_pop_token(t.n_tok, t.sx_tok, t.sxx_tok)));
6457$$
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE
6458 SET search_path=provsql,pg_temp,public SECURITY DEFINER;
6461CREATE OR REPLACE
FUNCTION stddev_samp_rv_ffunc(state UUID[])
6462 RETURNS random_variable
AS
6467 IF state
IS NULL OR array_length(state,
1)
IS NULL THEN
6470 SELECT *
INTO t
FROM rv_stat_sum_tokens(state,
2);
6471 RETURN random_variable_make(
6473 rv_stat_var_samp_token(t.n_tok, t.sx_tok, t.sxx_tok)));
6475$$
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE
6476 SET search_path=provsql,pg_temp,public SECURITY DEFINER;
6478CREATE AGGREGATE covar_pop(random_variable, random_variable) (
6479 SFUNC = rv_stat2_sfunc,
6482 FINALFUNC = covar_pop_rv_ffunc
6485CREATE AGGREGATE covar_samp(random_variable, random_variable) (
6486 SFUNC = rv_stat2_sfunc,
6489 FINALFUNC = covar_samp_rv_ffunc
6492CREATE AGGREGATE corr(random_variable, random_variable) (
6493 SFUNC = rv_stat2_sfunc,
6496 FINALFUNC = corr_rv_ffunc
6499CREATE AGGREGATE stddev_pop(random_variable) (
6500 SFUNC = rv_stat1_sfunc,
6503 FINALFUNC = stddev_pop_rv_ffunc
6506CREATE AGGREGATE stddev_samp(random_variable) (
6507 SFUNC = rv_stat1_sfunc,
6510 FINALFUNC = stddev_samp_rv_ffunc
6516CREATE AGGREGATE rv_covar_pop_impl(
6517 random_variable, random_variable, random_variable) (
6518 SFUNC = rv_stat2_impl_sfunc,
6521 FINALFUNC = covar_pop_rv_ffunc
6524CREATE AGGREGATE rv_covar_samp_impl(
6525 random_variable, random_variable, random_variable) (
6526 SFUNC = rv_stat2_impl_sfunc,
6529 FINALFUNC = covar_samp_rv_ffunc
6532CREATE AGGREGATE rv_corr_impl(
6533 random_variable, random_variable, random_variable) (
6534 SFUNC = rv_stat2_impl_sfunc,
6537 FINALFUNC = corr_rv_ffunc
6540CREATE AGGREGATE rv_stddev_pop_impl(random_variable, random_variable) (
6541 SFUNC = rv_stat1_impl_sfunc,
6544 FINALFUNC = stddev_pop_rv_ffunc
6547CREATE AGGREGATE rv_stddev_samp_impl(random_variable, random_variable) (
6548 SFUNC = rv_stat1_impl_sfunc,
6551 FINALFUNC = stddev_samp_rv_ffunc
6566CREATE OR REPLACE
FUNCTION rv_percentile_make(fraction
double precision,
6568 RETURNS random_variable
AS
6573 IF fraction
IS NULL THEN
6576 IF fraction <
0 OR fraction >
1 THEN
6578 'percentile_cont: fraction must be between 0 and 1 (got %)', fraction;
6580 token := public.uuid_generate_v5(
6582 concat(
'arith',
'10', pairs::TEXT, fraction::TEXT));
6583 PERFORM create_gate(token,
'arith', pairs);
6584 PERFORM set_infos(token,
10);
6585 PERFORM set_extra(token, fraction::TEXT);
6586 RETURN random_variable_make(token);
6588$$
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE
6589 SET search_path=provsql,pg_temp,public SECURITY DEFINER;
6598CREATE OR REPLACE
FUNCTION percentile_cont_rv_sfunc(
6599 state UUID[], x random_variable)
6602 SELECT provsql.rv_stat1_sfunc(state, x);
6603$$
LANGUAGE sql IMMUTABLE PARALLEL SAFE;
6607CREATE OR REPLACE
FUNCTION percentile_cont_rv_ffunc(
6608 state UUID[], fraction
double precision)
6609 RETURNS random_variable
AS
6612 WHEN state
IS NULL OR array_length(state,
1)
IS NULL THEN NULL
6613 ELSE provsql.rv_percentile_make(fraction, state)
6615$$
LANGUAGE sql IMMUTABLE PARALLEL SAFE;
6617CREATE AGGREGATE percentile_cont(
double precision ORDER BY random_variable) (
6618 SFUNC = percentile_cont_rv_sfunc,
6621 FINALFUNC = percentile_cont_rv_ffunc
6626CREATE TYPE rv_percentile_state
AS (
6627 fraction
double precision,
6634CREATE OR REPLACE
FUNCTION rv_percentile_impl_sfunc(
6635 state rv_percentile_state, fraction
double precision,
6636 ind random_variable, x random_variable)
6637 RETURNS rv_percentile_state
AS
6640 coalesce((state).fraction, fraction),
6642 WHEN x
IS NULL THEN (state).tokens
6643 ELSE (state).tokens ||
6644 ARRAY[coalesce((ind)::UUID,
6645 (provsql.as_random(
1::
double precision))::UUID),
6647 END)::provsql.rv_percentile_state;
6648$$
LANGUAGE sql IMMUTABLE PARALLEL SAFE;
6651CREATE OR REPLACE
FUNCTION rv_percentile_impl_ffunc(state rv_percentile_state)
6652 RETURNS random_variable
AS
6655 WHEN state
IS NULL OR array_length((state).tokens,
1)
IS NULL THEN NULL
6656 ELSE provsql.rv_percentile_make((state).fraction, (state).tokens)
6658$$
LANGUAGE sql IMMUTABLE PARALLEL SAFE;
6660CREATE AGGREGATE rv_percentile_impl(
6661 double precision, random_variable, random_variable) (
6662 SFUNC = rv_percentile_impl_sfunc,
6663 STYPE = rv_percentile_state,
6664 INITCOND =
'(,"{}")',
6665 FINALFUNC = rv_percentile_impl_ffunc
6686CREATE OR REPLACE
FUNCTION provenance_delta
6695 IF token
IS NULL THEN
6699 IF token = gate_zero()
OR token = gate_one()
THEN
6703 delta_token:=uuid_generate_v5(uuid_ns_provsql(),concat(
'delta',token));
6705 PERFORM create_gate(delta_token,
'delta',
ARRAY[token::UUID]);
6709$$
LANGUAGE plpgsql
SET search_path=provsql,pg_temp,public SECURITY DEFINER PARALLEL SAFE IMMUTABLE;
6726CREATE OR REPLACE
FUNCTION provenance_aggregate(
6731 is_scalar
BOOLEAN DEFAULT false)
6732 RETURNS AGG_TOKEN
AS
6742 tokens := array_remove(tokens,
NULL);
6743 c:=COALESCE(array_length(tokens,
1),
0);
6745 agg_val =
CAST(val
as VARCHAR);
6748 agg_tok := gate_zero();
6761 agg_tok := uuid_generate_v5(
6763 concat(
'agg',aggfnoid,tokens,
CASE WHEN is_scalar
THEN 'S' ELSE '' END));
6764 PERFORM create_gate(agg_tok,
'agg', tokens);
6765 PERFORM set_infos(agg_tok, aggfnoid,
6766 CASE WHEN is_scalar
THEN aggtype | (-2147483648)
ELSE aggtype
END);
6767 PERFORM set_extra(agg_tok, agg_val);
6770 RETURN '( '||agg_tok||
' , '||agg_val||
' )';
6772$$
LANGUAGE plpgsql PARALLEL SAFE
SET search_path=provsql,pg_temp,public SECURITY DEFINER IMMUTABLE;
6783CREATE OR REPLACE
FUNCTION provenance_semimod(val ANYELEMENT, token UUID)
6798 SELECT uuid_generate_v5(uuid_ns_provsql(),concat(
'value',
CAST(val
AS VARCHAR)))
6800 SELECT uuid_generate_v5(uuid_ns_provsql(),concat(
'semimod',value_token,token))
6804 PERFORM create_gate(value_token,
'value');
6805 PERFORM set_extra(value_token,
CAST(val
AS VARCHAR));
6808 PERFORM create_gate(semimod_token,
'semimod',
ARRAY[token::UUID,value_token]);
6810 RETURN semimod_token;
6812$$
LANGUAGE plpgsql PARALLEL SAFE
SET search_path=provsql,pg_temp,public SECURITY DEFINER IMMUTABLE;
6838CREATE OR REPLACE
FUNCTION probability_evaluate(
6841 arguments TEXT =
NULL)
6842 RETURNS DOUBLE PRECISION AS
6843 'provsql',
'probability_evaluate' LANGUAGE C STABLE;
6858CREATE OR REPLACE
FUNCTION probability(
6861 arguments TEXT =
NULL)
6862 RETURNS DOUBLE PRECISION AS
6863 'provsql',
'probability_evaluate' LANGUAGE C STABLE;
6892CREATE OR REPLACE
FUNCTION probability(
6895 arguments TEXT =
NULL)
6896 RETURNS DOUBLE PRECISION AS
6898 SELECT predicate::
INTEGER::
double precision;
6899$$
LANGUAGE sql IMMUTABLE PARALLEL SAFE;
6908CREATE OR REPLACE
FUNCTION probability_bounds(
6910 OUT lower
DOUBLE PRECISION,
6911 OUT upper DOUBLE PRECISION)
AS
6912 'provsql',
'probability_bounds' LANGUAGE C STABLE;
6935CREATE OR REPLACE
FUNCTION expected(
6937 prov UUID = gate_one(),
6939 arguments TEXT =
NULL)
6940 RETURNS DOUBLE PRECISION AS $$
6941 SELECT moment(
input,
1, prov,
method, arguments);
6942$$
LANGUAGE sql PARALLEL SAFE STABLE
SET search_path=provsql SECURITY DEFINER;
6954CREATE OR REPLACE
FUNCTION rv_moment(
6955 token UUID, k
INTEGER, central
BOOLEAN,
6956 prov UUID
DEFAULT gate_one())
6957 RETURNS double precision
6958 AS 'provsql',
'rv_moment' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
6964CREATE OR REPLACE
FUNCTION agg_avg_moment_exact(token UUID, k
INTEGER)
6965 RETURNS double precision
6966 AS 'provsql',
'agg_avg_moment_exact' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
6974CREATE OR REPLACE
FUNCTION agg_collapsed_moment(token UUID, k
INTEGER)
6975 RETURNS double precision
6976 AS 'provsql',
'agg_collapsed_moment' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
6983CREATE OR REPLACE
FUNCTION agg_collapsed_moments(token UUID)
6984 RETURNS double precision[]
6985 AS 'provsql',
'agg_collapsed_moments' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
7003CREATE OR REPLACE
FUNCTION agg_defined_event(token UUID)
7006 gt PROVENANCE_GATE := get_gate_type(token);
7013 running_neg UUID := gate_one();
7014 parts UUID[] :=
'{}';
7017 SELECT proname
INTO fname
7018 FROM pg_proc
WHERE oid = (get_infos(token)).info1;
7019 IF fname
IN (
'sum',
'count')
THEN
7022 SELECT array_agg((get_children(c))[
1])
INTO toks
7023 FROM unnest(get_children(token))
AS c;
7024 IF toks
IS NULL THEN
7027 RETURN provenance_plus(toks);
7028 ELSIF gt =
'case' THEN
7029 wires := get_children(token);
7030 nw := array_length(wires,
1);
7033 parts := parts || provenance_times(
7034 running_neg, wires[
2 * i -
1],
7035 agg_defined_event(wires[
2 * i]));
7036 running_neg := provenance_times(running_neg,
7037 provenance_not(wires[
2 * i -
1]));
7039 parts := parts || provenance_times(running_neg,
7040 agg_defined_event(wires[nw]));
7041 RETURN provenance_plus(parts);
7046$$
LANGUAGE plpgsql STABLE STRICT PARALLEL SAFE
7047 SET search_path=provsql,pg_temp,public SECURITY DEFINER;
7079CREATE OR REPLACE
FUNCTION agg_raw_moment(
7082 prov UUID = gate_one(),
7084 arguments TEXT =
NULL)
7085 RETURNS DOUBLE PRECISION AS $$
7087 aggregation_function
VARCHAR;
7089 pair_children UUID[];
7096 total_probability float8;
7100 distinct_tok UUID[];
7105 IF token
IS NULL OR k
IS NULL THEN
7109 RAISE EXCEPTION
'agg_raw_moment(): k must be non-negative (got %)', k;
7131 IF get_gate_type(token) =
'case' THEN
7136 wires UUID[] := get_children(token);
7137 nw
INTEGER := array_length(get_children(token),
1);
7138 m
INTEGER := (array_length(get_children(token),
1) -
1) /
2;
7139 running_neg UUID := gate_one();
7144 def_mass float8 :=
0;
7149 prov_p := probability(prov);
7150 IF prov_p
IS NULL OR prov_p <=
0 THEN
7155 FOR ci
IN 1 .. m +
1 LOOP
7157 region_full := provenance_times(running_neg, wires[
2 * ci -
1], prov);
7158 vuid := wires[
2 * ci];
7160 provenance_times(running_neg, provenance_not(wires[
2 * ci -
1]));
7162 region_full := provenance_times(running_neg, prov);
7165 p := probability(provenance_times(region_full,
7166 agg_defined_event(vuid)));
7174 IF get_gate_type(vuid) =
'value' THEN
7175 bm := power(
CAST(get_extra(vuid)
AS float8), k);
7176 ELSIF get_gate_type(vuid)
IN (
'agg',
'case')
THEN
7177 bm := agg_raw_moment(agg_token_make(vuid,
0), k, region_full,
7180 bm := rv_moment(vuid, k,
false, region_full);
7182 total := total + p * bm;
7183 def_mass := def_mass + p;
7186 IF def_mass <= epsilon()
THEN
7189 RETURN total / def_mass;
7193 IF get_gate_type(token) <>
'agg' THEN
7194 IF get_gate_type(token)
IN (
'arith',
'conditioned')
THEN
7195 RAISE EXCEPTION
'expected / variance / moment over an arithmetic '
7196 'combination of aggregates (e.g. SUM(x) + SUM(y) or SUM(x) + 5), or a '
7197 'conditioning of one, is not yet supported: a moment can be taken only '
7198 'over a single aggregate (SUM / COUNT / MIN / MAX), optionally '
7199 'conditioned (SUM(x) | C)'
7200 USING HINT =
'Take the moment of each aggregate separately, or condition '
7201 'the bare aggregate.';
7203 RAISE EXCEPTION
USING MESSAGE=
'Wrong gate type for agg_raw_moment computation';
7210 SELECT pp.proname::
varchar FROM pg_proc pp
7211 WHERE oid=(get_infos(token)).info1
7212 INTO aggregation_function;
7214 child_pairs := get_children(token);
7215 n := COALESCE(array_length(child_pairs,
1),
0);
7217 IF aggregation_function =
'sum' OR aggregation_function =
'count' THEN
7236 IF prov = gate_one()
AND k <=
2 THEN
7237 total := agg_collapsed_moment((token)::UUID, k);
7238 IF total
IS NOT NULL THEN
7244 vals :=
ARRAY[]::float8[];
7245 toks :=
ARRAY[]::UUID[];
7247 pair_children := get_children(child_pairs[i]);
7248 toks := toks || pair_children[
1];
7249 vals := vals ||
CAST(get_extra(pair_children[
2])
AS float8);
7255 tup := array_fill(
1,
ARRAY[k]);
7259 prod_v := prod_v * vals[tup[j]];
7262 SELECT array_agg(
DISTINCT toks[idx])
INTO distinct_tok
7263 FROM unnest(tup)
AS idx;
7265 IF prov <> gate_one()
THEN
7266 distinct_tok := distinct_tok || prov;
7268 conj_token := provenance_times(VARIADIC distinct_tok);
7269 prob := probability_evaluate(conj_token,
method, arguments);
7271 total := total + prod_v * prob;
7274 WHILE d >=
1 AND tup[d] = n LOOP
7279 tup[d] := tup[d] +
1;
7281 ELSIF aggregation_function =
'min' OR aggregation_function =
'max' THEN
7289 WHEN aggregation_function =
'max'
7290 THEN power(-1::float8, k)
7306 SELECT (get_children(c))[
1]
AS tok,
7307 (
CASE WHEN aggregation_function=
'max' THEN -1
ELSE 1 END)
7308 *
CAST(get_extra((get_children(c))[
2])
AS DOUBLE PRECISION)
AS v
7309 FROM UNNEST(child_pairs)
AS c
7310 )
SELECT sign_max * COALESCE(SUM(p * power(v, k)),
0)
FROM (
7312 probability_evaluate(
7313 CASE WHEN prov = gate_one()
7314 THEN provenance_monus(provenance_plus(ARRAY_AGG(t1.tok)),
7315 provenance_plus(ARRAY_AGG(t2.tok)))
7316 ELSE provenance_times(prov,
7317 provenance_monus(provenance_plus(ARRAY_AGG(t1.tok)),
7318 provenance_plus(ARRAY_AGG(t2.tok))))
END,
7319 method, arguments)
AS p
7320 FROM tok_value t1
LEFT OUTER JOIN tok_value t2
ON t1.v > t2.v
7325 SELECT probability_evaluate(
7326 CASE WHEN prov = gate_one()
7327 THEN provenance_plus(ARRAY_AGG(tok))
7328 ELSE provenance_times(prov, provenance_plus(ARRAY_AGG(tok)))
END,
7330 FROM (
SELECT (get_children(c))[
1]
AS tok
FROM UNNEST(child_pairs)
AS c) s
7331 INTO total_probability;
7333 IF total_probability <= epsilon()
THEN
7336 RETURN total / total_probability;
7337 ELSIF aggregation_function =
'avg' THEN
7354 IF prov = gate_one()
THEN
7355 total := agg_avg_moment_exact((token)::UUID, k);
7356 IF total
IS NOT NULL THEN
7360 RETURN rv_moment((token)::UUID, k,
false, prov);
7362 RAISE EXCEPTION
USING MESSAGE=
7363 'Cannot compute moment for aggregation function ' || aggregation_function;
7367 IF prov <> gate_one()
7369 AND total <>
'Infinity'::float8
7370 AND total <>
'-Infinity'::float8
THEN
7371 total := total / probability_evaluate(prov,
method, arguments);
7376$$
LANGUAGE plpgsql PARALLEL SAFE
SET search_path=provsql SECURITY DEFINER;
7389CREATE OR REPLACE
FUNCTION variance(
7391 prov UUID = gate_one(),
7393 arguments TEXT =
NULL)
7394 RETURNS DOUBLE PRECISION AS $$
7399 IF pg_typeof(
input) =
'random_variable'::REGTYPE
THEN
7400 IF input IS NULL THEN
7410 RETURN provsql.rv_moment(
7411 rv_conditioned_target((
input::random_variable)::UUID),
2,
true,
7412 rv_conditioned_prov((
input::random_variable)::UUID, prov));
7415 IF pg_typeof(
input) =
'AGG_TOKEN'::REGTYPE
THEN
7416 IF input IS NULL THEN
7424 IF rv_conditioned_prov(
input::UUID, prov) = gate_one()
THEN
7425 DECLARE ms float8[];
7427 ms := agg_collapsed_moments(
7428 (agg_conditioned_target(
input::AGG_TOKEN))::UUID);
7429 IF ms
IS NOT NULL THEN
7430 RETURN ms[
2] - ms[
1] * ms[
1];
7434 m1 := agg_raw_moment(agg_conditioned_target(
input::AGG_TOKEN),
1,
7435 rv_conditioned_prov(
input::UUID, prov),
method, arguments);
7436 m2 := agg_raw_moment(agg_conditioned_target(
input::AGG_TOKEN),
2,
7437 rv_conditioned_prov(
input::UUID, prov),
method, arguments);
7438 IF m1
IS NULL OR m2
IS NULL THEN
7441 RETURN m2 - m1 * m1;
7445 IF pg_typeof(
input) =
'UUID'::REGTYPE
THEN
7446 IF input IS NULL THEN
7449 m1 := provsql.probability_evaluate(provsql.cond(
input::UUID, prov),
7451 RETURN m1 * (
1 - m1);
7454 RAISE EXCEPTION
'variance() is not yet supported for input type %', pg_typeof(
input);
7456$$
LANGUAGE plpgsql PARALLEL SAFE
SET search_path=provsql SECURITY DEFINER;
7469CREATE OR REPLACE
FUNCTION moment(
7472 prov UUID = gate_one(),
7474 arguments TEXT =
NULL)
7475 RETURNS DOUBLE PRECISION AS $$
7477 IF pg_typeof(
input) =
'random_variable'::REGTYPE
THEN
7478 IF input IS NULL OR k
IS NULL THEN
7483 RETURN provsql.rv_moment(
7484 rv_conditioned_target((
input::random_variable)::UUID), k,
false,
7485 rv_conditioned_prov((
input::random_variable)::UUID, prov));
7488 IF pg_typeof(
input) =
'AGG_TOKEN'::REGTYPE
THEN
7489 RETURN agg_raw_moment(agg_conditioned_target(
input::AGG_TOKEN), k,
7490 rv_conditioned_prov(
input::UUID, prov),
method, arguments);
7497 IF pg_typeof(
input) =
'UUID'::REGTYPE
THEN
7498 IF input IS NULL OR k
IS NULL THEN
7504 RETURN provsql.probability_evaluate(provsql.cond(
input::UUID, prov),
7508 RAISE EXCEPTION
'moment() is not yet supported for input type %', pg_typeof(
input);
7510$$
LANGUAGE plpgsql PARALLEL SAFE
SET search_path=provsql SECURITY DEFINER;
7525CREATE OR REPLACE
FUNCTION rv_quantile(
7526 token UUID, p
double precision,
7527 prov UUID
DEFAULT gate_one())
7528 RETURNS double precision
7529 AS 'provsql',
'rv_quantile' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
7545CREATE OR REPLACE
FUNCTION quantile(
7548 prov UUID = gate_one(),
7550 arguments TEXT =
NULL)
7551 RETURNS DOUBLE PRECISION AS $$
7556 IF p <> p
OR p <
0 OR p >
1 THEN
7557 RAISE EXCEPTION
'quantile: p must be in [0, 1] (got %)', p;
7560 IF pg_typeof(
input) =
'random_variable'::REGTYPE
THEN
7561 IF input IS NULL THEN
7566 RETURN provsql.rv_quantile(
7567 rv_conditioned_target((
input::random_variable)::UUID), p,
7568 rv_conditioned_prov((
input::random_variable)::UUID, prov));
7571 IF pg_typeof(
input)
IN (
'smallint'::REGTYPE,
'INTEGER'::REGTYPE,
7572 'bigint'::REGTYPE,
'NUMERIC'::REGTYPE,
7573 'real'::REGTYPE,
'double precision'::REGTYPE)
THEN
7575 RETURN input::
double precision;
7578 RAISE EXCEPTION
'quantile() is not yet supported for input type %', pg_typeof(
input);
7580$$
LANGUAGE plpgsql PARALLEL SAFE
SET search_path=provsql SECURITY DEFINER;
7591CREATE OR REPLACE
FUNCTION rv_support(
7592 token UUID, prov UUID
DEFAULT gate_one(),
7593 OUT lo float8,
OUT hi float8)
7594 AS 'provsql',
'rv_support' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
7636CREATE OR REPLACE
FUNCTION support(
7638 prov UUID = gate_one(),
7640 arguments TEXT =
NULL,
7645 aggregation_function
VARCHAR;
7647 values_arr float8[];
7648 total_probability float8;
7650 IF input IS NULL THEN
7651 lo :=
NULL; hi :=
NULL;
RETURN;
7657 IF pg_typeof(
input)
IN (
7658 'smallint'::REGTYPE,
'INTEGER'::REGTYPE,
'bigint'::REGTYPE,
7659 'NUMERIC'::REGTYPE,
'real'::REGTYPE,
'double precision'::REGTYPE)
THEN
7660 lo :=
input::
double precision;
7661 hi :=
input::
double precision;
7674 IF pg_typeof(
input)
IN (
'random_variable'::REGTYPE,
'UUID'::REGTYPE)
THEN
7679 SELECT r.lo, r.hi
INTO lo, hi
7680 FROM provsql.rv_support(
7681 rv_conditioned_target(
input::UUID),
7682 rv_conditioned_prov(
input::UUID, prov)) r;
7686 IF pg_typeof(
input) =
'AGG_TOKEN'::REGTYPE
THEN
7691 atok AGG_TOKEN := agg_conditioned_target(
input::AGG_TOKEN);
7693 IF get_gate_type(atok) <>
'agg' THEN
7694 RAISE EXCEPTION
USING MESSAGE=
'Wrong gate type for support computation';
7696 SELECT pp.proname::
varchar FROM pg_proc pp
7697 WHERE oid=(get_infos(atok)).info1
7698 INTO aggregation_function;
7699 child_pairs := get_children(atok);
7701 IF aggregation_function =
'sum' OR aggregation_function =
'count' THEN
7705 IF COALESCE(array_length(child_pairs,
1),
0) =
0 THEN
7706 lo :=
0; hi :=
0;
RETURN;
7708 SELECT sum(LEAST(v,
0::float8)), sum(GREATEST(v,
0::float8))
7710 FROM (
SELECT CAST(get_extra((get_children(c))[
2])
AS float8)
AS v
7711 FROM unnest(child_pairs)
AS c) sub;
7712 ELSIF aggregation_function =
'min' OR aggregation_function =
'max' THEN
7717 IF COALESCE(array_length(child_pairs,
1),
0) =
0 THEN
7718 lo :=
NULL; hi :=
NULL;
RETURN;
7721 SELECT min(v), max(v)
7723 FROM (
SELECT CAST(get_extra((get_children(c))[
2])
AS float8)
AS v
7724 FROM UNNEST(child_pairs)
AS c) sub;
7726 RAISE EXCEPTION
USING MESSAGE=
7727 'Cannot compute support for aggregation function ' || aggregation_function;
7733 RAISE EXCEPTION
'support() is not yet supported for input type %', pg_typeof(
input);
7735$$
LANGUAGE plpgsql PARALLEL SAFE
SET search_path=provsql SECURITY DEFINER;
7748CREATE OR REPLACE
FUNCTION central_moment(
7751 prov UUID = gate_one(),
7753 arguments TEXT =
NULL)
7754 RETURNS DOUBLE PRECISION AS $$
7764 IF pg_typeof(
input) =
'random_variable'::REGTYPE
THEN
7765 IF input IS NULL OR k
IS NULL THEN
7770 RETURN provsql.rv_moment(
7771 rv_conditioned_target((
input::random_variable)::UUID), k,
true,
7772 rv_conditioned_prov((
input::random_variable)::UUID, prov));
7775 IF pg_typeof(
input) =
'AGG_TOKEN'::REGTYPE
THEN
7776 IF input IS NULL OR k
IS NULL THEN
7780 RAISE EXCEPTION
'central_moment(): k must be non-negative (got %)', k;
7782 IF k =
0 THEN RETURN 1;
END IF;
7783 IF k =
1 THEN RETURN 0;
END IF;
7785 mu := agg_raw_moment(agg_conditioned_target(
input::AGG_TOKEN),
1,
7786 rv_conditioned_prov(
input::UUID, prov),
method, arguments);
7787 IF mu
IS NULL THEN RETURN NULL;
END IF;
7790 IF mu =
'Infinity'::float8
OR mu =
'-Infinity'::float8
THEN
7798 raw_i := agg_raw_moment(agg_conditioned_target(
input::AGG_TOKEN), i,
7799 rv_conditioned_prov(
input::UUID, prov),
method, arguments);
7800 IF raw_i
IS NULL THEN RETURN NULL;
END IF;
7801 total := total + binom * power(-mu, k - i) * raw_i;
7804 binom := binom * (k_double - i) / (i +
1);
7812 IF pg_typeof(
input) =
'UUID'::REGTYPE
THEN
7813 IF input IS NULL OR k
IS NULL THEN
7817 RAISE EXCEPTION
'central_moment(): k must be non-negative (got %)', k;
7819 IF k =
0 THEN RETURN 1;
END IF;
7820 IF k =
1 THEN RETURN 0;
END IF;
7821 mu := provsql.probability_evaluate(provsql.cond(
input::UUID, prov),
7823 RETURN (
1 - mu) * power(-mu, k) + mu * power(
1 - mu, k);
7826 RAISE EXCEPTION
'central_moment() is not yet supported for input type %', pg_typeof(
input);
7828$$
LANGUAGE plpgsql PARALLEL SAFE
SET search_path=provsql SECURITY DEFINER;
7831CREATE OR REPLACE
FUNCTION rv_covariance(x UUID, y UUID, prov UUID)
7832 RETURNS double precision
7833 AS 'provsql',
'rv_covariance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
7858CREATE OR REPLACE
FUNCTION covariance(
7859 x random_variable, y random_variable, prov UUID
DEFAULT gate_one())
7860 RETURNS double precision AS $$
7861 SELECT provsql.rv_covariance((x)::UUID, (y)::UUID, prov);
7862$$
LANGUAGE sql PARALLEL SAFE STABLE
SET search_path=provsql SECURITY DEFINER;
7877CREATE OR REPLACE
FUNCTION stddev(
7878 x random_variable, prov UUID
DEFAULT gate_one())
7879 RETURNS double precision AS $$
7880 SELECT sqrt(provsql.variance(x, prov));
7881$$
LANGUAGE sql PARALLEL SAFE STABLE
SET search_path=provsql SECURITY DEFINER;
7884CREATE OR REPLACE
FUNCTION rv_correlation(x UUID, y UUID, prov UUID)
7885 RETURNS double precision
7886 AS 'provsql',
'rv_correlation' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
7903CREATE OR REPLACE
FUNCTION correlation(
7904 x random_variable, y random_variable, prov UUID
DEFAULT gate_one())
7905 RETURNS double precision AS $$
7906 SELECT provsql.rv_correlation((x)::UUID, (y)::UUID, prov);
7907$$
LANGUAGE sql PARALLEL SAFE STABLE
SET search_path=provsql SECURITY DEFINER;
7910CREATE OR REPLACE
FUNCTION rv_entropy(token UUID, prov UUID)
7911 RETURNS double precision
7912 AS 'provsql',
'rv_entropy' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
7930CREATE OR REPLACE
FUNCTION entropy(
7931 x random_variable, prov UUID
DEFAULT gate_one())
7932 RETURNS double precision AS $$
7933 SELECT provsql.rv_entropy((x)::UUID, prov);
7934$$
LANGUAGE sql PARALLEL SAFE STABLE
SET search_path=provsql SECURITY DEFINER;
7937CREATE OR REPLACE
FUNCTION rv_kl(p UUID, q UUID)
7938 RETURNS double precision
7939 AS 'provsql',
'rv_kl' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
7954CREATE OR REPLACE
FUNCTION kl(p random_variable, q random_variable)
7955 RETURNS double precision AS $$
7956 SELECT provsql.rv_kl((p)::UUID, (q)::UUID);
7957$$
LANGUAGE sql PARALLEL SAFE STABLE
SET search_path=provsql SECURITY DEFINER;
7961CREATE OR REPLACE
FUNCTION rv_mutual_information(x UUID, y UUID)
7962 RETURNS double precision
7963 AS 'provsql',
'rv_mutual_information' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
7977CREATE OR REPLACE
FUNCTION mutual_information(
7978 x random_variable, y random_variable)
7979 RETURNS double precision AS $$
7980 SELECT provsql.rv_mutual_information((x)::UUID, (y)::UUID);
7981$$
LANGUAGE sql PARALLEL SAFE STABLE
SET search_path=provsql SECURITY DEFINER;
7995CREATE OR REPLACE
FUNCTION shapley(
7999 arguments TEXT =
NULL,
8000 banzhaf
BOOLEAN =
'f')
8001 RETURNS DOUBLE PRECISION AS
8002 'provsql',
'shapley' LANGUAGE C STABLE;
8005CREATE OR REPLACE
FUNCTION shapley_all_vars(
8007 IN method TEXT =
NULL,
8008 IN arguments TEXT =
NULL,
8009 IN banzhaf
BOOLEAN =
'f',
8011 OUT value DOUBLE PRECISION)
8012 RETURNS SETOF RECORD
AS
8013 'provsql',
'shapley_all_vars'
8017CREATE OR REPLACE
FUNCTION banzhaf(
8021 arguments TEXT =
NULL)
8022 RETURNS DOUBLE PRECISION AS
8023 $$
SELECT provsql.shapley(token, variable,
method, arguments,
't') $$
8027CREATE OR REPLACE
FUNCTION banzhaf_all_vars(
8029 IN method TEXT =
NULL,
8030 IN arguments TEXT =
NULL,
8032 OUT value DOUBLE PRECISION)
8033 RETURNS SETOF RECORD
AS
8034 $$
SELECT *
FROM provsql.shapley_all_vars(token,
method, arguments,
't') $$
8069CREATE OR REPLACE
FUNCTION reachability_evaluate(
8073 probabilities
DOUBLE PRECISION[],
8077 RETURNS DOUBLE PRECISION AS
8078 'provsql',
'reachability_evaluate' LANGUAGE C IMMUTABLE PARALLEL SAFE;
8108CREATE OR REPLACE
FUNCTION reachability_compile_stats(
8110 IN destinations
INT[],
8112 IN probabilities
DOUBLE PRECISION[],
8115 IN directed
BOOLEAN,
8116 OUT probability
DOUBLE PRECISION,
8117 OUT data_treewidth
INT,
8119 OUT max_states
BIGINT,
8120 OUT nb_gates
BIGINT,
8121 OUT nb_variables
BIGINT)
8122 AS 'provsql',
'reachability_compile_stats'
8123 LANGUAGE C IMMUTABLE PARALLEL SAFE;
8159CREATE OR REPLACE
FUNCTION ucq_joint_compile_stats(
8160 IN disjunct_nvars
INT[],
8161 IN atom_disjunct
INT[],
8164 IN atom_arity
INT[],
8166 IN fact_elems
INT[],
8167 IN fact_arity
INT[],
8168 IN fact_tokens UUID[],
8169 IN fact_probs
DOUBLE PRECISION[],
8170 OUT probability
DOUBLE PRECISION,
8171 OUT joint_treewidth
INT,
8172 OUT data_treewidth_lb
INT,
8173 OUT circuit_treewidth_lb
INT,
8175 OUT max_states
BIGINT,
8177 OUT n_enumerating
INT)
8178 AS 'provsql',
'ucq_joint_compile_stats'
8179 LANGUAGE C IMMUTABLE PARALLEL SAFE;
8188CREATE OR REPLACE
FUNCTION ucq_joint_compile_stats(
8191 IN fact_elems
INT[],
8192 IN fact_arity
INT[],
8193 IN fact_tokens UUID[],
8194 IN fact_probs
DOUBLE PRECISION[],
8195 OUT probability
DOUBLE PRECISION,
8196 OUT joint_treewidth
INT,
8197 OUT data_treewidth_lb
INT,
8198 OUT circuit_treewidth_lb
INT,
8200 OUT max_states
BIGINT,
8202 OUT n_enumerating
INT)
8205 dnv
INT[] :=
'{}'; adisj
INT[] :=
'{}'; arel
INT[] :=
'{}';
8206 avars
INT[] :=
'{}'; aarity
INT[] :=
'{}';
8207 d JSONB; a JSONB; v TEXT; didx
INT :=
0;
8209 FOR d
IN SELECT *
FROM jsonb_array_elements(query->
'disjuncts') LOOP
8210 dnv := dnv || (d->>
'n_vars')::
INT;
8211 FOR a
IN SELECT *
FROM jsonb_array_elements(d->
'atoms') LOOP
8212 adisj := adisj || didx;
8213 arel := arel || (a->>
'rel')::
INT;
8214 aarity := aarity || jsonb_array_length(a->
'vars');
8215 FOR v
IN SELECT *
FROM jsonb_array_elements_text(a->
'vars') LOOP
8216 avars := avars || v::
INT;
8221 SELECT s.probability, s.joint_treewidth, s.data_treewidth_lb,
8222 s.circuit_treewidth_lb, s.n_bags, s.max_states, s.dd_size,
8224 INTO probability, joint_treewidth, data_treewidth_lb,
8225 circuit_treewidth_lb, n_bags, max_states, dd_size, n_enumerating
8226 FROM ucq_joint_compile_stats(dnv, adisj, arel, avars, aarity,
8227 fact_rel, fact_elems, fact_arity, fact_tokens, fact_probs) s;
8229$$
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE;
8250CREATE OR REPLACE
FUNCTION ucq_joint_compile_stats_tracked(
8251 IN disjunct_nvars
INT[],
8252 IN atom_disjunct
INT[],
8255 IN atom_arity
INT[],
8257 IN fact_elems
INT[],
8258 IN fact_arity
INT[],
8259 IN fact_tokens UUID[],
8260 OUT probability
DOUBLE PRECISION,
8261 OUT joint_treewidth
INT,
8262 OUT data_treewidth_lb
INT,
8263 OUT circuit_treewidth_lb
INT,
8265 OUT max_states
BIGINT,
8267 OUT n_enumerating
INT)
8268 AS 'provsql',
'ucq_joint_compile_stats_tracked'
8269 LANGUAGE C STABLE PARALLEL SAFE;
8275CREATE OR REPLACE
FUNCTION ucq_joint_compile_stats_tracked(
8278 IN fact_elems
INT[],
8279 IN fact_arity
INT[],
8280 IN fact_tokens UUID[],
8281 OUT probability
DOUBLE PRECISION,
8282 OUT joint_treewidth
INT,
8283 OUT data_treewidth_lb
INT,
8284 OUT circuit_treewidth_lb
INT,
8286 OUT max_states
BIGINT,
8288 OUT n_enumerating
INT)
8291 dnv
INT[] :=
'{}'; adisj
INT[] :=
'{}'; arel
INT[] :=
'{}';
8292 avars
INT[] :=
'{}'; aarity
INT[] :=
'{}';
8293 d JSONB; a JSONB; v TEXT; didx
INT :=
0;
8295 FOR d
IN SELECT *
FROM jsonb_array_elements(query->
'disjuncts') LOOP
8296 dnv := dnv || (d->>
'n_vars')::
INT;
8297 FOR a
IN SELECT *
FROM jsonb_array_elements(d->
'atoms') LOOP
8298 adisj := adisj || didx;
8299 arel := arel || (a->>
'rel')::
INT;
8300 aarity := aarity || jsonb_array_length(a->
'vars');
8301 FOR v
IN SELECT *
FROM jsonb_array_elements_text(a->
'vars') LOOP
8302 avars := avars || v::
INT;
8307 SELECT s.probability, s.joint_treewidth, s.data_treewidth_lb,
8308 s.circuit_treewidth_lb, s.n_bags, s.max_states, s.dd_size,
8310 INTO probability, joint_treewidth, data_treewidth_lb,
8311 circuit_treewidth_lb, n_bags, max_states, dd_size, n_enumerating
8312 FROM ucq_joint_compile_stats_tracked(dnv, adisj, arel, avars, aarity,
8313 fact_rel, fact_elems, fact_arity, fact_tokens) s;
8315$$
LANGUAGE plpgsql STABLE PARALLEL SAFE;
8330CREATE OR REPLACE
FUNCTION ucq_joint_materialize_tracked(
8331 disjunct_nvars
INT[],
8332 atom_disjunct
INT[],
8341 'provsql',
'ucq_joint_materialize_tracked' LANGUAGE C VOLATILE;
8351CREATE OR REPLACE
FUNCTION ucq_joint_materialize_tracked(
8359 dnv
INT[] :=
'{}'; adisj
INT[] :=
'{}'; arel
INT[] :=
'{}';
8360 avars
INT[] :=
'{}'; aarity
INT[] :=
'{}';
8361 d JSONB; a JSONB; v TEXT; didx
INT :=
0;
8363 FOR d
IN SELECT *
FROM jsonb_array_elements(query->
'disjuncts') LOOP
8364 dnv := dnv || (d->>
'n_vars')::
INT;
8365 FOR a
IN SELECT *
FROM jsonb_array_elements(d->
'atoms') LOOP
8366 adisj := adisj || didx;
8367 arel := arel || (a->>
'rel')::
INT;
8368 aarity := aarity || jsonb_array_length(a->
'vars');
8369 FOR v
IN SELECT *
FROM jsonb_array_elements_text(a->
'vars') LOOP
8370 avars := avars || v::
INT;
8375 RETURN ucq_joint_materialize_tracked(dnv, adisj, arel, avars, aarity,
8376 fact_rel, fact_elems, fact_arity, fact_tokens);
8378$$
LANGUAGE plpgsql VOLATILE;
8412CREATE OR REPLACE
FUNCTION ucq_joint_provenance(
8413 descriptor JSONB, fallback UUID
DEFAULT NULL)
8416 legs TEXT;
sql TEXT; saved TEXT;
8417 fact_rel
INT[]; fact_elems
INT[]; fact_arity
INT[]; fact_tokens UUID[];
8418 dnv
INT[]:=
'{}'; adisj
INT[]:=
'{}'; arel
INT[]:=
'{}';
8419 avars
INT[]:=
'{}'; aarity
INT[]:=
'{}';
8420 d jsonb; a jsonb; v TEXT; didx
INT:=
0;
8423 FOR d
IN SELECT *
FROM jsonb_array_elements(descriptor->
'disjuncts') LOOP
8424 dnv := dnv || (d->>
'n_vars')::
INT;
8425 FOR a
IN SELECT *
FROM jsonb_array_elements(d->
'atoms') LOOP
8426 adisj := adisj || didx; arel := arel || (a->>
'rel')::
INT;
8427 aarity := aarity || jsonb_array_length(a->
'vars');
8428 FOR v
IN SELECT *
FROM jsonb_array_elements_text(a->
'vars') LOOP
8429 avars := avars || v::
INT;
8439 format(
'SELECT %s, ARRAY[%s]::TEXT[], provsql FROM %s%s',
8441 (
SELECT string_agg(format(
'(%I)::TEXT', c),
',')
8442 FROM jsonb_array_elements_text(descriptor->
'elem_cols'->(rn-1)::
INT) c),
8446 CASE WHEN coalesce(descriptor->
'rel_where'->>(rn-1)::
INT,
'') <>
''
8447 THEN ' WHERE '||(descriptor->
'rel_where'->>(rn-1)::
INT)
8451 FROM jsonb_array_elements_text(descriptor->
'relations')
WITH ORDINALITY t(rel, rn);
8454 WITH facts(rel,elems,tok)
AS (%s),
8455 ord
AS (
SELECT row_number()
OVER ()
AS ord, rel, elems, tok
FROM facts),
8456 dict
AS (
SELECT val, (dense_rank()
OVER (
ORDER BY val))-1
AS id
8457 FROM (
SELECT DISTINCT unnest(elems)
AS val
FROM facts) u)
8458 SELECT (
SELECT array_agg(rel
ORDER BY ord)
FROM ord),
8459 (
SELECT array_agg(cardinality(elems)
ORDER BY ord)
FROM ord),
8460 (
SELECT array_agg(tok
ORDER BY ord)
FROM ord),
8461 (
SELECT array_agg(dd.id
ORDER BY o.ord, e.k)
8462 FROM ord o,
LATERAL unnest(o.elems)
WITH ORDINALITY e(val,k)
8463 JOIN dict dd
ON dd.val = e.val)
8468 saved := current_setting(
'provsql.active',
true);
8469 PERFORM set_config(
'provsql.active',
'off',
true);
8470 EXECUTE sql INTO fact_rel, fact_arity, fact_tokens, fact_elems;
8471 PERFORM set_config(
'provsql.active', saved,
true);
8473 RETURN ucq_joint_materialize_tracked(dnv,adisj,arel,avars,aarity,
8474 fact_rel,fact_elems,fact_arity,fact_tokens);
8475EXCEPTION
WHEN OTHERS
THEN
8481$$
LANGUAGE plpgsql VOLATILE;
8499CREATE OR REPLACE
FUNCTION ucq_mobius_materialize_tracked(
8500 disjunct_nvars
INT[],
8501 atom_disjunct
INT[],
8509 lineage UUID
DEFAULT NULL)
8511 'provsql',
'ucq_mobius_materialize_tracked' LANGUAGE C VOLATILE;
8520CREATE OR REPLACE
FUNCTION ucq_mobius_compile_stats(
8521 IN disjunct_nvars
INT[],
8522 IN atom_disjunct
INT[],
8525 IN atom_arity
INT[],
8527 IN fact_elems
INT[],
8528 IN fact_arity
INT[],
8529 IN fact_tokens UUID[],
8530 OUT probability
DOUBLE PRECISION,
8531 OUT n_components
INT,
8532 OUT n_cnf_conjuncts
INT,
8533 OUT lattice_size
INT,
8535 OUT n_cancelled
INT,
8536 OUT cancelled_hard
BOOLEAN,
8538 OUT memo_hits
BIGINT)
8539 AS 'provsql',
'ucq_mobius_compile_stats'
8540 LANGUAGE C VOLATILE;
8554CREATE OR REPLACE
FUNCTION mobius_or_null(tok UUID)
8556 SELECT CASE WHEN tok
IS NOT NULL AND provsql.get_gate_type(tok) =
'mobius'
8558$$
LANGUAGE sql STABLE;
8566CREATE OR REPLACE
FUNCTION ucq_mobius_provenance(
8567 descriptor JSONB, fallback UUID
DEFAULT NULL)
8570 legs TEXT;
sql TEXT; saved TEXT;
8571 fact_rel
INT[]; fact_elems
INT[]; fact_arity
INT[]; fact_tokens UUID[];
8572 dnv
INT[]:=
'{}'; adisj
INT[]:=
'{}'; arel
INT[]:=
'{}';
8573 avars
INT[]:=
'{}'; aarity
INT[]:=
'{}';
8574 d jsonb; a jsonb; v TEXT; didx
INT:=
0;
8576 FOR d
IN SELECT *
FROM jsonb_array_elements(descriptor->
'disjuncts') LOOP
8577 dnv := dnv || (d->>
'n_vars')::
INT;
8578 FOR a
IN SELECT *
FROM jsonb_array_elements(d->
'atoms') LOOP
8579 adisj := adisj || didx; arel := arel || (a->>
'rel')::
INT;
8580 aarity := aarity || jsonb_array_length(a->
'vars');
8581 FOR v
IN SELECT *
FROM jsonb_array_elements_text(a->
'vars') LOOP
8582 avars := avars || v::
INT;
8589 format(
'SELECT %s, ARRAY[%s]::TEXT[], provsql FROM %s%s',
8591 (
SELECT string_agg(format(
'(%I)::TEXT', c),
',')
8592 FROM jsonb_array_elements_text(descriptor->
'elem_cols'->(rn-1)::
INT) c),
8594 CASE WHEN coalesce(descriptor->
'rel_where'->>(rn-1)::
INT,
'') <>
''
8595 THEN ' WHERE '||(descriptor->
'rel_where'->>(rn-1)::
INT)
8599 FROM jsonb_array_elements_text(descriptor->
'relations')
WITH ORDINALITY t(rel, rn);
8602 WITH facts(rel,elems,tok)
AS (%s),
8603 ord
AS (
SELECT row_number()
OVER ()
AS ord, rel, elems, tok
FROM facts),
8604 dict
AS (
SELECT val, (dense_rank()
OVER (
ORDER BY val))-1
AS id
8605 FROM (
SELECT DISTINCT unnest(elems)
AS val
FROM facts) u)
8606 SELECT (
SELECT array_agg(rel
ORDER BY ord)
FROM ord),
8607 (
SELECT array_agg(cardinality(elems)
ORDER BY ord)
FROM ord),
8608 (
SELECT array_agg(tok
ORDER BY ord)
FROM ord),
8609 (
SELECT array_agg(dd.id
ORDER BY o.ord, e.k)
8610 FROM ord o,
LATERAL unnest(o.elems)
WITH ORDINALITY e(val,k)
8611 JOIN dict dd
ON dd.val = e.val)
8614 saved := current_setting(
'provsql.active',
true);
8615 PERFORM set_config(
'provsql.active',
'off',
true);
8616 EXECUTE sql INTO fact_rel, fact_arity, fact_tokens, fact_elems;
8617 PERFORM set_config(
'provsql.active', saved,
true);
8622 RETURN ucq_mobius_materialize_tracked(dnv,adisj,arel,avars,aarity,
8623 fact_rel,fact_elems,fact_arity,fact_tokens, fallback);
8624EXCEPTION
WHEN OTHERS
THEN
8627$$
LANGUAGE plpgsql VOLATILE;
8635CREATE OR REPLACE
FUNCTION mobius_compile_stats(
8636 IN descriptor JSONB,
8637 OUT probability
DOUBLE PRECISION,
8638 OUT n_components
INT,
8639 OUT n_cnf_conjuncts
INT,
8640 OUT lattice_size
INT,
8642 OUT n_cancelled
INT,
8643 OUT cancelled_hard
BOOLEAN,
8645 OUT memo_hits
BIGINT)
8648 legs TEXT;
sql TEXT; saved TEXT;
8649 fact_rel
INT[]; fact_elems
INT[]; fact_arity
INT[]; fact_tokens UUID[];
8650 dnv
INT[]:=
'{}'; adisj
INT[]:=
'{}'; arel
INT[]:=
'{}';
8651 avars
INT[]:=
'{}'; aarity
INT[]:=
'{}';
8652 d jsonb; a jsonb; v TEXT; didx
INT:=
0;
8654 FOR d
IN SELECT *
FROM jsonb_array_elements(descriptor->
'disjuncts') LOOP
8655 dnv := dnv || (d->>
'n_vars')::
INT;
8656 FOR a
IN SELECT *
FROM jsonb_array_elements(d->
'atoms') LOOP
8657 adisj := adisj || didx; arel := arel || (a->>
'rel')::
INT;
8658 aarity := aarity || jsonb_array_length(a->
'vars');
8659 FOR v
IN SELECT *
FROM jsonb_array_elements_text(a->
'vars') LOOP
8660 avars := avars || v::
INT;
8667 format(
'SELECT %s, ARRAY[%s]::TEXT[], provsql FROM %s%s',
8669 (
SELECT string_agg(format(
'(%I)::TEXT', c),
',')
8670 FROM jsonb_array_elements_text(descriptor->
'elem_cols'->(rn-1)::
INT) c),
8672 CASE WHEN coalesce(descriptor->
'rel_where'->>(rn-1)::
INT,
'') <>
''
8673 THEN ' WHERE '||(descriptor->
'rel_where'->>(rn-1)::
INT)
8677 FROM jsonb_array_elements_text(descriptor->
'relations')
WITH ORDINALITY t(rel, rn);
8680 WITH facts(rel,elems,tok)
AS (%s),
8681 ord
AS (
SELECT row_number()
OVER ()
AS ord, rel, elems, tok
FROM facts),
8682 dict
AS (
SELECT val, (dense_rank()
OVER (
ORDER BY val))-1
AS id
8683 FROM (
SELECT DISTINCT unnest(elems)
AS val
FROM facts) u)
8684 SELECT (
SELECT array_agg(rel
ORDER BY ord)
FROM ord),
8685 (
SELECT array_agg(cardinality(elems)
ORDER BY ord)
FROM ord),
8686 (
SELECT array_agg(tok
ORDER BY ord)
FROM ord),
8687 (
SELECT array_agg(dd.id
ORDER BY o.ord, e.k)
8688 FROM ord o,
LATERAL unnest(o.elems)
WITH ORDINALITY e(val,k)
8689 JOIN dict dd
ON dd.val = e.val)
8692 saved := current_setting(
'provsql.active',
true);
8693 PERFORM set_config(
'provsql.active',
'off',
true);
8694 EXECUTE sql INTO fact_rel, fact_arity, fact_tokens, fact_elems;
8695 PERFORM set_config(
'provsql.active', saved,
true);
8697 SELECT s.probability, s.n_components, s.n_cnf_conjuncts, s.lattice_size,
8698 s.n_nonzero, s.n_cancelled, s.cancelled_hard, s.dd_size, s.memo_hits
8699 INTO probability, n_components, n_cnf_conjuncts, lattice_size,
8700 n_nonzero, n_cancelled, cancelled_hard, dd_size, memo_hits
8701 FROM ucq_mobius_compile_stats(dnv,adisj,arel,avars,aarity,
8702 fact_rel,fact_elems,fact_arity,fact_tokens) s;
8704$$
LANGUAGE plpgsql VOLATILE;
8717CREATE OR REPLACE
FUNCTION ucq_joint_gather(
8719 OUT disjunct_nvars
INT[],
OUT atom_disjunct
INT[],
OUT atom_rel
INT[],
8720 OUT atom_vars
INT[],
OUT atom_arity
INT[],
8721 OUT fact_rel
INT[],
OUT fact_elems
INT[],
OUT fact_arity
INT[],
8722 OUT fact_tokens UUID[],
OUT val_by_id TEXT[])
8725 legs TEXT;
sql TEXT; saved TEXT; d jsonb; a jsonb; v TEXT; didx
INT :=
0;
8727 disjunct_nvars:=
'{}'; atom_disjunct:=
'{}'; atom_rel:=
'{}';
8728 atom_vars:=
'{}'; atom_arity:=
'{}';
8729 FOR d
IN SELECT *
FROM jsonb_array_elements(descriptor->
'disjuncts') LOOP
8730 disjunct_nvars := disjunct_nvars || (d->>
'n_vars')::
INT;
8731 FOR a
IN SELECT *
FROM jsonb_array_elements(d->
'atoms') LOOP
8732 atom_disjunct := atom_disjunct || didx;
8733 atom_rel := atom_rel || (a->>
'rel')::
INT;
8734 atom_arity := atom_arity || jsonb_array_length(a->
'vars');
8735 FOR v
IN SELECT *
FROM jsonb_array_elements_text(a->
'vars') LOOP
8736 atom_vars := atom_vars || v::
INT;
8743 format(
'SELECT %s, ARRAY[%s]::TEXT[], provsql FROM %s%s', rn -
1,
8744 (
SELECT string_agg(format(
'(%I)::TEXT', c),
',')
8745 FROM jsonb_array_elements_text(descriptor->
'elem_cols'->(rn-1)::
INT) c),
8747 CASE WHEN coalesce(descriptor->
'rel_where'->>(rn-1)::
INT,
'') <>
''
8748 THEN ' WHERE '||(descriptor->
'rel_where'->>(rn-1)::
INT)
8752 FROM jsonb_array_elements_text(descriptor->
'relations')
WITH ORDINALITY t(rel, rn);
8755 WITH facts(rel,elems,tok)
AS (%s),
8756 ord
AS (
SELECT row_number()
OVER ()
AS ord, rel, elems, tok
FROM facts),
8757 dict
AS (
SELECT val, (dense_rank()
OVER (
ORDER BY val))-1
AS id
8758 FROM (
SELECT DISTINCT unnest(elems)
AS val
FROM facts) u)
8759 SELECT (
SELECT array_agg(rel
ORDER BY ord)
FROM ord),
8760 (
SELECT array_agg(cardinality(elems)
ORDER BY ord)
FROM ord),
8761 (
SELECT array_agg(tok
ORDER BY ord)
FROM ord),
8762 (
SELECT array_agg(dd.id
ORDER BY o.ord, e.k)
8763 FROM ord o,
LATERAL unnest(o.elems)
WITH ORDINALITY e(val,k)
8764 JOIN dict dd
ON dd.val = e.val),
8765 (
SELECT array_agg(val
ORDER BY id)
FROM dict)
8768 saved := current_setting(
'provsql.active',
true);
8769 PERFORM set_config(
'provsql.active',
'off',
true);
8770 EXECUTE sql INTO fact_rel, fact_arity, fact_tokens, fact_elems, val_by_id;
8771 PERFORM set_config(
'provsql.active', saved,
true);
8773$$
LANGUAGE plpgsql VOLATILE;
8790CREATE OR REPLACE
FUNCTION ucq_joint_provenance_answer(
8791 descriptor JSONB, head_vars
INT[], head_vals TEXT[], fallback UUID
DEFAULT NULL)
8792RETURNS UUID
AS 'provsql',
'ucq_joint_provenance_answer'
8803CREATE OR REPLACE
FUNCTION ucq_mobius_provenance_answer(
8804 descriptor JSONB, head_vars
INT[], head_vals TEXT[], fallback UUID
DEFAULT NULL)
8805RETURNS UUID
AS 'provsql',
'ucq_mobius_provenance_answer'
8846CREATE OR REPLACE
FUNCTION reachability_materialize(
8848 IN destinations
INT[],
8850 IN probabilities
DOUBLE PRECISION[],
8851 IN block_keys UUID[],
8852 IN block_indices
INT[],
8853 IN source_vertices
INT[],
8854 IN source_tokens UUID[],
8855 IN source_probabilities
DOUBLE PRECISION[],
8856 IN directed
BOOLEAN,
8859 RETURNS SETOF RECORD
AS
8860 'provsql',
'reachability_materialize' LANGUAGE C VOLATILE;
8893CREATE OR REPLACE
FUNCTION reachability_materialize_hops(
8895 IN destinations
INT[],
8897 IN probabilities
DOUBLE PRECISION[],
8898 IN block_keys UUID[],
8899 IN block_indices
INT[],
8900 IN source_vertices
INT[],
8901 IN source_tokens UUID[],
8902 IN source_probabilities
DOUBLE PRECISION[],
8903 IN directed
BOOLEAN,
8909 RETURNS SETOF RECORD
AS
8910 'provsql',
'reachability_materialize_hops' LANGUAGE C VOLATILE;
8942CREATE OR REPLACE
FUNCTION reachability_materialize_any(
8944 IN destinations
INT[],
8946 IN probabilities
DOUBLE PRECISION[],
8947 IN block_keys UUID[],
8948 IN block_indices
INT[],
8949 IN source_vertices
INT[],
8950 IN source_tokens UUID[],
8951 IN source_probabilities
DOUBLE PRECISION[],
8952 IN directed
BOOLEAN,
8954 IN member_vertices
INT[],
8957 RETURNS SETOF RECORD
AS
8958 'provsql',
'reachability_materialize_any' LANGUAGE C VOLATILE;
8988CREATE OR REPLACE
FUNCTION reachability_materialize_cover(
8992 probabilities
DOUBLE PRECISION[],
8994 block_indices
INT[],
8995 source_vertices
INT[],
8996 source_tokens UUID[],
8997 source_probabilities
DOUBLE PRECISION[],
8999 member_vertices
INT[])
9001 'provsql',
'reachability_materialize_cover' LANGUAGE C VOLATILE;
9038CREATE OR REPLACE
FUNCTION plant_reach_any_groups(
9040 node_attribute TEXT,
9041 member_rel REGCLASS,
9042 member_attribute TEXT,
9043 group_attribute TEXT,
9045 source_attribute TEXT,
9046 destination_attribute TEXT,
9049 edge_quals TEXT
DEFAULT NULL,
9050 source_rel REGCLASS
DEFAULT NULL,
9051 source_rel_attribute TEXT
DEFAULT NULL,
9052 edge_sql TEXT
DEFAULT NULL,
9053 member_quals TEXT
DEFAULT NULL)
9062 sp
double precision[];
9063 gids
INT[] :=
ARRAY[]::
INT[];
9064 mids
INT[] :=
ARRAY[]::
INT[];
9067 verbosity
INT := coalesce(current_setting(
'provsql.verbose_level',
true)::
INT,
0);
9072 IF EXISTS (
SELECT 1 FROM pg_attribute
9073 WHERE attrelid = member_rel
AND attname =
'provsql'
9074 AND atttypid =
'UUID'::REGTYPE
AND NOT attisdropped)
THEN
9078 IF source_rel
IS NOT NULL THEN
9079 SELECT g.source_values, g.source_tokens, g.source_probabilities
9081 FROM provsql.gather_reachability_sources(source_rel,
9082 source_rel_attribute) g;
9084 sv :=
ARRAY[]::TEXT[];
9085 st :=
ARRAY[]::UUID[];
9086 sp :=
ARRAY[]::float8[];
9089 sv :=
ARRAY[source_value];
9090 st :=
ARRAY[
'00000000-0000-0000-0000-000000000000'::UUID];
9091 sp :=
ARRAY[
1.
0::float8];
9094 e := provsql.gather_reachability_edges(edge_rel, source_attribute,
9095 destination_attribute,
9096 sv, edge_quals, edge_sql);
9108 DROP TABLE IF EXISTS provsql_reach_any_flat_tmp;
9110 'CREATE TEMP TABLE provsql_reach_any_flat_tmp AS '
9111 ||
'SELECT w.%1$I::TEXT AS node_val, provsql.provenance() AS tok, '
9112 ||
' t.%5$I AS grp_key '
9113 ||
'FROM %2$I w JOIN %3$s t ON w.%1$I = t.%4$I'
9117 || coalesce(
' WHERE ' || member_quals,
''),
9118 node_attribute, work_name, member_rel::TEXT, member_attribute,
9120 PERFORM provsql.remove_provenance(
'provsql_reach_any_flat_tmp');
9121 DROP TABLE IF EXISTS provsql_reach_any_groups_tmp;
9122 CREATE TEMP
TABLE provsql_reach_any_groups_tmp
AS
9123 SELECT (row_number()
OVER ())::
INT AS gid, members, toks
FROM (
9124 SELECT array_agg(node_val)
AS members, array_agg(tok)
AS toks
9125 FROM provsql_reach_any_flat_tmp
9126 GROUP BY grp_key
HAVING count(*) >=
2) g;
9127 DROP TABLE provsql_reach_any_flat_tmp;
9129 FOR grp
IN SELECT gid, members
FROM provsql_reach_any_groups_tmp LOOP
9130 FOR m
IN SELECT DISTINCT unnest(grp.members)
AS val LOOP
9131 vid := array_position(e.vertices, m.val);
9132 IF vid
IS NOT NULL THEN
9133 gids := gids || grp.gid;
9134 mids := mids || vid;
9138 IF cardinality(gids) =
0 THEN
9139 DROP TABLE provsql_reach_any_groups_tmp;
9144 SELECT a.group_id, a.token
AS any_token, t.toks
9145 FROM provsql.reachability_materialize_any(
9146 e.sources, e.destinations, e.tokens, e.probabilities,
9147 e.block_keys, e.block_indices, e.extra_ids, st, sp,
9148 directed, gids, mids) a
9149 JOIN provsql_reach_any_groups_tmp t
ON t.gid = a.group_id
9151 canonical := public.uuid_generate_v5(
9152 provsql.uuid_ns_provsql(),
9153 concat(
'plus-canonical',
9154 (
SELECT array_agg(tok
ORDER BY tok)
9155 FROM unnest(grp.toks) tok)));
9156 PERFORM provsql.create_gate(canonical,
'plus',
ARRAY[grp.any_token]);
9157 PERFORM provsql.set_infos(canonical,
1);
9159 DROP TABLE provsql_reach_any_groups_tmp;
9160 IF verbosity >=
20 THEN
9163 PERFORM set_config(
'client_min_messages',
'notice',
true);
9164 RAISE NOTICE
'ProvSQL: certified any-member gates planted for the aggregation of "%" by %.%',
9165 work_name, member_rel, group_attribute;
9166 PERFORM set_config(
'client_min_messages',
'warning',
true);
9168 EXCEPTION
WHEN OTHERS
THEN
9169 IF verbosity >=
10 THEN
9170 PERFORM set_config(
'client_min_messages',
'notice',
true);
9171 RAISE NOTICE
'ProvSQL: any-member planting for "%" skipped (%)',
9173 PERFORM set_config(
'client_min_messages',
'warning',
true);
9179$$
LANGUAGE plpgsql
SET client_min_messages = warning;
9215CREATE OR REPLACE
FUNCTION plant_reach_cover(
9217 node_attribute TEXT,
9219 source_attribute TEXT,
9220 destination_attribute TEXT,
9224 edge_quals TEXT
DEFAULT NULL,
9225 source_rel REGCLASS
DEFAULT NULL,
9226 source_rel_attribute TEXT
DEFAULT NULL,
9227 edge_sql TEXT
DEFAULT NULL)
9234 sp
double precision[];
9237 vids
INT[] :=
ARRAY[]::
INT[];
9239 toks UUID[] :=
ARRAY[]::UUID[];
9242 verbosity
INT := coalesce(current_setting(
'provsql.verbose_level',
true)::
INT,
0);
9245 IF source_rel
IS NOT NULL THEN
9246 SELECT g.source_values, g.source_tokens, g.source_probabilities
9248 FROM provsql.gather_reachability_sources(source_rel,
9249 source_rel_attribute) g;
9251 sv :=
ARRAY[]::TEXT[];
9252 st :=
ARRAY[]::UUID[];
9253 sp :=
ARRAY[]::float8[];
9256 sv :=
ARRAY[source_value];
9257 st :=
ARRAY[
'00000000-0000-0000-0000-000000000000'::UUID];
9258 sp :=
ARRAY[
1.
0::float8];
9261 e := provsql.gather_reachability_edges(edge_rel, source_attribute,
9262 destination_attribute,
9263 sv, edge_quals, edge_sql);
9269 FOREACH val
IN ARRAY node_values LOOP
9270 vid := array_position(e.vertices, val);
9274 vids := vids || vid;
9275 EXECUTE format(
'SELECT provsql FROM %I WHERE %I::TEXT = $1',
9276 work_name, node_attribute)
9281 toks := toks || tok;
9284 cover_token := provsql.reachability_materialize_cover(
9285 e.sources, e.destinations, e.tokens, e.probabilities,
9286 e.block_keys, e.block_indices, e.extra_ids, st, sp,
9289 SELECT public.uuid_generate_v5(
9290 provsql.uuid_ns_provsql(),
9291 concat(
'times-canonical', array_agg(t
ORDER BY t)))
9294 PERFORM provsql.create_gate(canonical,
'times',
ARRAY[cover_token]);
9295 PERFORM provsql.set_infos(canonical,
1);
9296 IF verbosity >=
20 THEN
9299 PERFORM set_config(
'client_min_messages',
'notice',
true);
9300 RAISE NOTICE
'ProvSQL: certified all-members gate planted for the self-join of "%"',
9302 PERFORM set_config(
'client_min_messages',
'warning',
true);
9304 EXCEPTION
WHEN OTHERS
THEN
9305 IF verbosity >=
10 THEN
9306 PERFORM set_config(
'client_min_messages',
'notice',
true);
9307 RAISE NOTICE
'ProvSQL: all-members planting for "%" skipped (%)',
9309 PERFORM set_config(
'client_min_messages',
'warning',
true);
9315$$
LANGUAGE plpgsql
SET client_min_messages = warning;
9330CREATE OR REPLACE
FUNCTION token_conjunctive_leaves(token UUID)
9333WITH RECURSIVE walk(g)
AS (
9336 SELECT c
FROM walk w,
unnest(provsql.get_children(w.g))
AS c
9337 WHERE provsql.get_gate_type(w.g)
IN (
'times',
'project',
'eq',
'annotation')
9339SELECT CASE WHEN bool_and(provsql.get_gate_type(g)
9340 IN (
'times',
'project',
'eq',
'annotation',
'input'))
9341 THEN array_agg(
DISTINCT g)
9342 FILTER (
WHERE provsql.get_gate_type(g) =
'input')
9345$$
LANGUAGE sql STABLE;
9386CREATE OR REPLACE
FUNCTION gather_reachability_edges(
9388 IN source_attribute TEXT,
9389 IN destination_attribute TEXT,
9390 IN extra_vertices TEXT[],
9391 IN edge_quals TEXT
DEFAULT NULL,
9392 IN rel_sql TEXT
DEFAULT NULL,
9394 OUT destinations
INT[],
9396 OUT probabilities
DOUBLE PRECISION[],
9397 OUT block_keys UUID[],
9398 OUT block_indices
INT[],
9399 OUT extra_ids
INT[],
9400 OUT vertices TEXT[])
9409 verbosity
INT := coalesce(current_setting(
'provsql.verbose_level',
true)::
INT,
0);
9417 IF rel
IS NOT NULL AND rel_sql
IS NULL THEN
9418 tkind := (provsql.get_table_info(rel::oid)).kind;
9420 IF tkind
NOT IN (
'tid',
'bid')
THEN
9423 IF tkind =
'bid' THEN
9424 SELECT string_agg(quote_ident(a.attname) ||
'::TEXT',
' || '','' || '
9427 FROM unnest((provsql.get_table_info(rel::oid)).block_key)
9428 WITH ORDINALITY
AS k(attnum, ord)
9429 JOIN pg_attribute a
ON a.attrelid = rel
AND a.attnum = k.attnum;
9431 bkey_expr := coalesce(bkey_expr, quote_literal(
''));
9433 IF tkind
IS NOT NULL AND verbosity >=
20 THEN
9438 PERFORM set_config(
'client_min_messages',
'notice',
true);
9439 RAISE NOTICE
'ProvSQL: catalog characterises % as %', rel,
upper(tkind);
9440 PERFORM set_config(
'client_min_messages',
'warning',
true);
9449 DROP TABLE IF EXISTS provsql_reachability_edges_tmp;
9451 'CREATE TEMP TABLE provsql_reachability_edges_tmp AS '
9452 ||
'SELECT %1$I::TEXT AS u, %2$I::TEXT AS v, '
9453 ||
'provsql.strip_annotations(provsql.provenance()) AS token%5$s '
9454 ||
'FROM %3$s WHERE %1$I IS NOT NULL AND %2$I IS NOT NULL%4$s',
9455 source_attribute, destination_attribute,
9456 CASE WHEN rel_sql
IS NULL THEN rel::TEXT
9457 ELSE '(' || rel_sql ||
') AS provsql_edge_subquery' END,
9458 CASE WHEN edge_quals
IS NULL THEN ''
9459 ELSE ' AND (' || edge_quals ||
')' END,
9460 CASE WHEN tkind =
'bid'
9461 THEN ', public.uuid_generate_v5(provsql.uuid_ns_provsql(), '
9462 || quote_literal(
'bidblock' || rel::TEXT ||
':')
9463 ||
' || ' || bkey_expr ||
') AS bkey'
9464 ELSE ', NULL::UUID AS bkey' END);
9465 PERFORM provsql.remove_provenance(
'provsql_reachability_edges_tmp');
9467 DROP TABLE IF EXISTS provsql_reachability_support_tmp;
9468 IF tkind
IS NULL THEN
9471 IF EXISTS (
SELECT 1 FROM provsql_reachability_edges_tmp
9472 WHERE provsql.get_gate_type(token)
NOT IN (
'input',
'mulinput',
'times',
9473 'project',
'eq'))
THEN
9474 DROP TABLE provsql_reachability_edges_tmp;
9475 RAISE EXCEPTION
'reachability: the provenance of % must consist of base input, repair_key, or conjunctive join tokens', coalesce(rel::TEXT,
'the edge query');
9477 CREATE TEMP
TABLE provsql_reachability_support_tmp
AS
9478 SELECT t.token, l.leaf
9479 FROM (
SELECT DISTINCT token
FROM provsql_reachability_edges_tmp
9480 WHERE provsql.get_gate_type(token)
IN (
'times',
'project',
'eq')) t,
9481 LATERAL unnest(provsql.token_conjunctive_leaves(t.token))
AS l(leaf);
9483 FROM (
SELECT DISTINCT token
FROM provsql_reachability_edges_tmp) t
9484 WHERE provsql.get_gate_type(t.token)
IN (
'times',
'project',
'eq')
9485 AND provsql.token_conjunctive_leaves(t.token)
IS NULL)
THEN
9486 DROP TABLE provsql_reachability_support_tmp;
9487 DROP TABLE provsql_reachability_edges_tmp;
9488 RAISE EXCEPTION
'reachability: a join-defined edge token is not a pure conjunction of base tuples';
9490 IF EXISTS (
SELECT 1 FROM (
9491 SELECT leaf
FROM provsql_reachability_support_tmp
9493 SELECT DISTINCT token
FROM provsql_reachability_edges_tmp
9494 WHERE provsql.get_gate_type(token) =
'input'
9496 GROUP BY leaf
HAVING count(*) >
1)
THEN
9497 DROP TABLE provsql_reachability_support_tmp;
9498 DROP TABLE provsql_reachability_edges_tmp;
9499 RAISE EXCEPTION
'reachability: join-defined edges share base tuples (their supports overlap), so they are not independent';
9508 IF tkind =
'tid' THEN
9509 sel_probs :=
'coalesce(provsql.get_prob(e.token), 1.0)';
9510 sel_bkeys := $
sql$
'00000000-0000-0000-0000-000000000000'::UUID$
sql$;
9512 ELSIF tkind =
'bid' THEN
9513 sel_probs :=
'coalesce(provsql.get_prob(e.token), 1.0)';
9514 sel_bkeys := $
sql$
CASE WHEN provsql.get_gate_type(e.token) =
'mulinput'
9516 ELSE '00000000-0000-0000-0000-000000000000'::UUID
END$
sql$;
9517 sel_bidx :=
'e.bidx';
9519 sel_probs := $
sql$
CASE WHEN provsql.get_gate_type(e.token)
IN (
'times',
'project',
'eq')
9520 THEN (
SELECT CASE WHEN bool_or(coalesce(provsql.get_prob(s.leaf),
1.
0) =
0)
9522 ELSE exp(sum(ln(coalesce(provsql.get_prob(s.leaf),
1.
0))))
END
9523 FROM provsql_reachability_support_tmp s
9524 WHERE s.token = e.token)
9525 ELSE coalesce(provsql.get_prob(e.token),
1.
0)
END$
sql$;
9526 sel_bkeys := $
sql$
CASE WHEN provsql.get_gate_type(e.token) =
'mulinput'
9527 THEN (provsql.get_children(e.token))[
1]
9528 ELSE '00000000-0000-0000-0000-000000000000'::UUID
END$
sql$;
9529 sel_bidx := $
sql$
CASE WHEN provsql.get_gate_type(e.token) =
'mulinput'
9530 THEN (provsql.get_infos(e.token)).info1
ELSE 0 END$
sql$;
9536 SELECT u
AS x
FROM provsql_reachability_edges_tmp
9537 UNION SELECT v
FROM provsql_reachability_edges_tmp
9538 UNION SELECT unnest($
1)),
9540 SELECT x, (row_number()
OVER (
ORDER BY x))::
INT AS id
FROM verts)
9541 SELECT array_agg(iu.id), array_agg(iv.id),
9546 (
SELECT array_agg(i.id
ORDER BY ev.ord)
9547 FROM unnest($
1)
WITH ORDINALITY
AS ev(x, ord)
9548 JOIN ids i
ON i.x = ev.x),
9549 (
SELECT array_agg(x
ORDER BY id)
FROM ids)
9551 (row_number()
OVER (
PARTITION BY t.bkey))::
INT AS bidx
9552 FROM provsql_reachability_edges_tmp t) e
9553 JOIN ids iu
ON iu.x = e.u
9554 JOIN ids iv
ON iv.x = e.v
9555 $
sql$, sel_probs, sel_bkeys, sel_bidx)
9556 INTO sources, destinations, tokens, probabilities, block_keys,
9557 block_indices, extra_ids, vertices
9558 USING extra_vertices;
9560 DROP TABLE provsql_reachability_edges_tmp;
9561 DROP TABLE IF EXISTS provsql_reachability_support_tmp;
9566$$
LANGUAGE plpgsql
SET client_min_messages = warning;
9585CREATE OR REPLACE
FUNCTION gather_reachability_sources(
9587 IN source_attribute TEXT,
9588 OUT source_values TEXT[],
9589 OUT source_tokens UUID[],
9590 OUT source_probabilities
DOUBLE PRECISION[])
9598 SELECT 1 FROM pg_attribute
9599 WHERE attrelid = rel
AND attname =
'provsql'
9600 AND atttypid =
'UUID'::REGTYPE
AND NOT attisdropped)
9608 tkind := (get_table_info(rel::oid)).kind;
9609 IF tkind =
'bid' THEN
9610 RAISE EXCEPTION
'reachability: % is block-independent (repair_key); block-correlated source sets are not supported', rel;
9614 DROP TABLE IF EXISTS provsql_reachability_sources_tmp;
9617 'CREATE TEMP TABLE provsql_reachability_sources_tmp AS '
9618 ||
'SELECT %1$I::TEXT AS x, provenance() AS token '
9619 ||
'FROM %2$s WHERE %1$I IS NOT NULL',
9620 source_attribute, rel);
9621 PERFORM remove_provenance(
'provsql_reachability_sources_tmp');
9622 IF tkind
IS DISTINCT FROM 'tid'
9623 AND EXISTS (
SELECT 1 FROM provsql_reachability_sources_tmp
9624 WHERE get_gate_type(token) <>
'input')
THEN
9625 DROP TABLE provsql_reachability_sources_tmp;
9626 RAISE EXCEPTION
'reachability: the provenance of % must consist of base input tokens (independent tuples); views or query results are not supported', rel;
9628 SELECT array_agg(x), array_agg(token),
9629 array_agg(coalesce(get_prob(token),
1.
0))
9630 INTO source_values, source_tokens, source_probabilities
9631 FROM provsql_reachability_sources_tmp;
9632 DROP TABLE provsql_reachability_sources_tmp;
9635 'CREATE TEMP TABLE provsql_reachability_sources_tmp AS '
9636 ||
'SELECT DISTINCT %1$I::TEXT AS x FROM %2$s WHERE %1$I IS NOT NULL',
9637 source_attribute, rel);
9638 SELECT array_agg(x),
9639 array_agg(
'00000000-0000-0000-0000-000000000000'::UUID),
9640 array_agg(
1.
0::float8)
9641 INTO source_values, source_tokens, source_probabilities
9642 FROM provsql_reachability_sources_tmp;
9643 DROP TABLE provsql_reachability_sources_tmp;
9646$$
LANGUAGE plpgsql
SET search_path=provsql,pg_temp,public
SET client_min_messages = warning;
9690CREATE OR REPLACE
FUNCTION eval_reachability(
9692 source_attribute TEXT,
9693 destination_attribute TEXT,
9701 edge_quals TEXT
DEFAULT NULL,
9702 source_rel REGCLASS
DEFAULT NULL,
9703 source_rel_attribute TEXT
DEFAULT NULL,
9704 edge_sql TEXT
DEFAULT NULL,
9705 hop_bound
INT DEFAULT NULL,
9706 hop_seed
INT DEFAULT NULL,
9707 hops_position
INT DEFAULT NULL)
9714 sp
double precision[];
9715 verbosity
INT := coalesce(current_setting(
'provsql.verbose_level',
true)::
INT,
0);
9718 IF source_rel
IS NOT NULL THEN
9721 SELECT g.source_values, g.source_tokens, g.source_probabilities
9723 FROM provsql.gather_reachability_sources(source_rel,
9724 source_rel_attribute) g;
9726 sv :=
ARRAY[]::TEXT[];
9727 st :=
ARRAY[]::UUID[];
9728 sp :=
ARRAY[]::float8[];
9732 sv :=
ARRAY[source_value];
9733 st :=
ARRAY[
'00000000-0000-0000-0000-000000000000'::UUID];
9734 sp :=
ARRAY[
1.
0::float8];
9737 e := provsql.gather_reachability_edges(edge_rel, source_attribute,
9738 destination_attribute,
9739 sv, edge_quals, edge_sql);
9740 IF to_regclass(work_name)
IS NOT NULL THEN
9741 EXECUTE format(
'DROP TABLE %I', work_name);
9743 EXECUTE format(
'CREATE TEMP TABLE %I (%s, provsql UUID)', work_name, coldef);
9744 IF hop_bound
IS NULL THEN
9746 'INSERT INTO %I SELECT ($1::TEXT[])[m.vertex]::%s, m.token '
9747 ||
'FROM provsql.reachability_materialize($2, $3, $4, $5, $6, $7, $8, $9, $10, $11) m',
9749 USING e.vertices, e.sources, e.destinations, e.tokens, e.probabilities,
9750 e.block_keys, e.block_indices, e.extra_ids, st, sp, directed;
9755 'INSERT INTO %I SELECT %s, m.token '
9756 ||
'FROM provsql.reachability_materialize_hops($2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) m',
9758 CASE WHEN hops_position =
1
9759 THEN format(
'm.hops, ($1::TEXT[])[m.vertex]::%s', coltype)
9760 ELSE format(
'($1::TEXT[])[m.vertex]::%s, m.hops', coltype)
END)
9761 USING e.vertices, e.sources, e.destinations, e.tokens, e.probabilities,
9762 e.block_keys, e.block_indices, e.extra_ids, st, sp, directed,
9763 hop_bound, hop_seed;
9765 IF verbosity >=
20 THEN
9766 RAISE NOTICE
'ProvSQL: recursive CTE "%" compiled along a tree decomposition of %',
9767 work_name, coalesce(edge_rel::TEXT,
'the join-defined edge query');
9769 EXCEPTION
WHEN OTHERS
THEN
9770 IF verbosity >=
10 THEN
9771 RAISE NOTICE
'ProvSQL: reachability route for "%" fell back to the generic fixpoint (%)',
9774 PERFORM provsql.eval_recursive(body_sql, work_name, colnames, coldef);
9796CREATE OR REPLACE
FUNCTION view_circuit(
9798 token2desc REGCLASS,
9801 'provsql',
'view_circuit' LANGUAGE C;
9814CREATE OR REPLACE
FUNCTION compile_to_ddnnf_dot(
9818 'provsql',
'compile_to_ddnnf_dot' LANGUAGE C;
9833CREATE OR REPLACE
FUNCTION compile_to_ddnnf(
9837 'provsql',
'compile_to_ddnnf' LANGUAGE C;
9854CREATE OR REPLACE
FUNCTION ddnnf_stats(
9858 'provsql',
'ddnnf_stats' LANGUAGE C;
9872CREATE OR REPLACE
FUNCTION tseytin_cnf(
9874 weighted
BOOLEAN =
TRUE,
9875 mapping
BOOLEAN =
TRUE)
9877 'provsql',
'tseytin_cnf' LANGUAGE C;
9891CREATE OR REPLACE
FUNCTION tseytin_cnf_mapping_json(token UUID)
9893 'provsql',
'tseytin_cnf_mapping_json' LANGUAGE C;
9895CREATE OR REPLACE
FUNCTION tseytin_cnf_mapping(token UUID)
9896 RETURNS TABLE(variable
INT, gate UUID, probability FLOAT8)
AS $$
9897 SELECT variable, gate, probability
9898 FROM jsonb_to_recordset(tseytin_cnf_mapping_json(token))
9899 AS x(variable
INT, gate UUID, probability FLOAT8)
9901$$
LANGUAGE SQL STABLE;
9913CREATE OR REPLACE
FUNCTION tree_decomposition_dot(
9916 'provsql',
'tree_decomposition_dot' LANGUAGE C;
9936CREATE OR REPLACE
FUNCTION tool_available(name TEXT)
9938 'provsql',
'tool_available' LANGUAGE C STRICT;
9968CREATE TABLE IF NOT EXISTS tool_overrides(
9969 name TEXT
PRIMARY KEY,
9970 removed
BOOLEAN NOT NULL DEFAULT false,
9974 input_formats TEXT[],
9979 dependencies TEXT[],
9981 argtpl_circuit TEXT,
9984SELECT pg_catalog.pg_extension_config_dump(
'tool_overrides',
'');
9996CREATE OR REPLACE
FUNCTION tool_registry_list()
9997 RETURNS TABLE(name TEXT, kind TEXT, executable TEXT, operations TEXT[],
9998 input_formats TEXT[], output_format TEXT, parser TEXT,
9999 preference
INT, enabled
BOOLEAN, argtpl TEXT,
10000 argtpl_circuit TEXT, endpoint TEXT, available
BOOLEAN)
AS
10001 'provsql',
'tool_registry_list' LANGUAGE C STABLE;
10006CREATE OR REPLACE VIEW tools
AS
10007 SELECT name, kind, executable, operations, input_formats, output_format,
10008 parser, preference, enabled, argtpl, argtpl_circuit, endpoint,
10010 FROM tool_registry_list();
10045CREATE OR REPLACE
FUNCTION register_tool(
10047 executable TEXT
DEFAULT NULL,
10048 kind TEXT
DEFAULT 'cli',
10049 operations TEXT[]
DEFAULT NULL,
10050 input_formats TEXT[]
DEFAULT NULL,
10051 output_format TEXT
DEFAULT NULL,
10052 parser TEXT
DEFAULT NULL,
10053 argtpl TEXT
DEFAULT NULL,
10054 argtpl_circuit TEXT
DEFAULT NULL,
10055 preference
INT DEFAULT 0,
10056 enabled
BOOLEAN DEFAULT true,
10057 endpoint TEXT
DEFAULT NULL)
10059 'provsql',
'tool_registry_register' LANGUAGE C;
10062CREATE OR REPLACE
FUNCTION unregister_tool(name TEXT)
10064 'provsql',
'tool_registry_unregister' LANGUAGE C STRICT;
10067CREATE OR REPLACE
FUNCTION set_tool_enabled(name TEXT, enabled
BOOLEAN)
10069 'provsql',
'tool_registry_set_enabled' LANGUAGE C STRICT;
10072CREATE OR REPLACE
FUNCTION set_tool_preference(name TEXT, preference
INT)
10074 'provsql',
'tool_registry_set_preference' LANGUAGE C STRICT;
10078REVOKE ALL ON FUNCTION register_tool(TEXT, TEXT, TEXT, TEXT[], TEXT[], TEXT, TEXT, TEXT, TEXT,
INT,
BOOLEAN, TEXT)
FROM PUBLIC;
10079REVOKE ALL ON FUNCTION unregister_tool(TEXT)
FROM PUBLIC;
10080REVOKE ALL ON FUNCTION set_tool_enabled(TEXT,
BOOLEAN)
FROM PUBLIC;
10081REVOKE ALL ON FUNCTION set_tool_preference(TEXT,
INT)
FROM PUBLIC;
10089CREATE OR REPLACE
FUNCTION to_provxml(
10091 token2desc REGCLASS =
NULL)
10093 'provsql',
'to_provxml' LANGUAGE C;
10096CREATE OR REPLACE
FUNCTION provenance()
RETURNS UUID
AS
10097 'provsql',
'provenance' LANGUAGE C;
10105CREATE OR REPLACE
FUNCTION where_provenance(token UUID)
10107 'provsql',
'where_provenance' LANGUAGE C;
10118CREATE OR REPLACE
FUNCTION reset_constants_cache()
10120 'provsql',
'reset_constants_cache' LANGUAGE C;
10122SELECT reset_constants_cache();
10124SELECT create_gate(gate_zero(),
'zero');
10125SELECT create_gate(gate_one(),
'one');
10130CREATE TYPE QUERY_TYPE_ENUM
AS ENUM (
10147CREATE FUNCTION sr_formula(token ANYELEMENT, token2value REGCLASS =
NULL)
10151 IF token
IS NULL THEN
10154 RETURN provsql.provenance_evaluate_compiled(
10161$$
LANGUAGE plpgsql PARALLEL SAFE STABLE;
10164CREATE FUNCTION sr_counting(token ANYELEMENT, token2value REGCLASS)
10168 RETURN provsql.provenance_evaluate_compiled(
10175$$
LANGUAGE plpgsql STRICT PARALLEL SAFE STABLE;
10178CREATE FUNCTION sr_why(token ANYELEMENT, token2value REGCLASS)
10182 RETURN provsql.provenance_evaluate_compiled(
10189$$
LANGUAGE plpgsql STRICT PARALLEL SAFE STABLE;
10192CREATE FUNCTION sr_how(token ANYELEMENT, token2value REGCLASS)
10196 RETURN provsql.provenance_evaluate_compiled(
10203$$
LANGUAGE plpgsql STRICT PARALLEL SAFE STABLE;
10206CREATE FUNCTION sr_which(token ANYELEMENT, token2value REGCLASS)
10210 RETURN provsql.provenance_evaluate_compiled(
10217$$
LANGUAGE plpgsql STRICT PARALLEL SAFE STABLE;
10225CREATE FUNCTION sr_boolexpr(token ANYELEMENT, token2value REGCLASS =
NULL)
10229 IF token
IS NULL THEN
10232 RETURN provsql.provenance_evaluate_compiled(
10239$$
LANGUAGE plpgsql PARALLEL SAFE STABLE;
10242CREATE FUNCTION sr_boolean(token ANYELEMENT, token2value REGCLASS)
10246 RETURN provsql.provenance_evaluate_compiled(
10253$$
LANGUAGE plpgsql STRICT PARALLEL SAFE STABLE;
10256CREATE FUNCTION true_nonzero(token UUID)
10258 'provsql',
'true_nonzero' LANGUAGE C PARALLEL SAFE STABLE;
10286CREATE FUNCTION nonzero(token UUID,
10287 semiring TEXT
DEFAULT NULL,
10288 mapping REGCLASS
DEFAULT NULL)
10292 IF token
IS NULL THEN
10295 IF semiring
IS NULL THEN
10296 RETURN provsql.true_nonzero(token);
10297 ELSIF semiring =
'BOOLEAN' THEN
10298 RETURN provsql.provenance_evaluate_compiled(token, mapping,
'BOOLEAN',
TRUE);
10299 ELSIF semiring =
'counting' THEN
10300 RETURN provsql.provenance_evaluate_compiled(token, mapping,
'counting',
1) <>
0;
10302 RAISE EXCEPTION
'nonzero: unsupported semiring "%" (supported: BOOLEAN, counting; NULL for the universal zero test)', semiring;
10305$$
LANGUAGE plpgsql PARALLEL SAFE STABLE;
10316CREATE FUNCTION present(token UUID)
10319 SELECT provsql.nonzero(token,
'BOOLEAN');
10320$$
LANGUAGE sql PARALLEL SAFE STABLE;
10334CREATE FUNCTION sr_tropical(token ANYELEMENT, token2value REGCLASS,
10335 nonnegative
BOOLEAN =
false)
10339 RETURN provsql.provenance_evaluate_compiled(
10342 CASE WHEN nonnegative
THEN 'tropical_nonneg' ELSE 'tropical' END,
10346$$
LANGUAGE plpgsql STRICT PARALLEL SAFE STABLE;
10353CREATE FUNCTION sr_viterbi(token ANYELEMENT, token2value REGCLASS)
10357 RETURN provsql.provenance_evaluate_compiled(
10364$$
LANGUAGE plpgsql STRICT PARALLEL SAFE STABLE;
10373CREATE FUNCTION sr_lukasiewicz(token ANYELEMENT, token2value REGCLASS)
10377 RETURN provsql.provenance_evaluate_compiled(
10384$$
LANGUAGE plpgsql STRICT PARALLEL SAFE STABLE;
10401CREATE FUNCTION sr_minmax(token UUID, token2value REGCLASS, element_one ANYENUM)
10405 RETURN provsql.provenance_evaluate_compiled(
10412$$
LANGUAGE plpgsql STRICT PARALLEL SAFE STABLE;
10426CREATE FUNCTION sr_maxmin(token UUID, token2value REGCLASS, element_one ANYENUM)
10430 RETURN provsql.provenance_evaluate_compiled(
10437$$
LANGUAGE plpgsql STRICT PARALLEL SAFE STABLE;
10448CREATE FUNCTION choose_function(state ANYELEMENT, data ANYELEMENT)
10449 RETURNS ANYELEMENT
AS
10452 IF state
IS NULL THEN
10458$$
LANGUAGE plpgsql PARALLEL SAFE IMMUTABLE;
10461CREATE AGGREGATE choose(ANYELEMENT) (
10462 SFUNC = choose_function,
10478CREATE OR REPLACE
FUNCTION explode_table(_tbl TEXT, AGG_TOKEN TEXT)
10486 SELECT n.nspname
INTO _nsp
10487 FROM pg_class c
JOIN pg_namespace n
ON n.oid = c.relnamespace
10488 WHERE c.oid = _tbl::REGCLASS;
10491 CREATE TABLE %1$I.temp_exploded AS
10494 provsql.get_extra(children[2]) AS new_t,
10495 provsql.provenance_times(children[1], provsql) AS new_provsql
10498 SELECT provsql.get_children(sm) AS children
10499 FROM UNNEST(provsql.get_children(%3$I)) AS sm
10500 ) AS sub', _nsp, _tbl, AGG_TOKEN);
10501 EXECUTE format(
'DROP TABLE %I.%I', _nsp, _tbl);
10502 EXECUTE format(
'ALTER TABLE %I.temp_exploded DROP COLUMN %I, DROP COLUMN provsql', _nsp, AGG_TOKEN);
10503 EXECUTE format(
'ALTER TABLE %I.temp_exploded RENAME COLUMN new_t TO %I', _nsp, AGG_TOKEN);
10504 EXECUTE format(
'ALTER TABLE %I.temp_exploded RENAME COLUMN new_provsql TO provsql', _nsp);
10505 EXECUTE format(
'ALTER TABLE %I.temp_exploded RENAME TO %I', _nsp, _tbl);
10507$$
LANGUAGE plpgsql;
10531CREATE OR REPLACE
FUNCTION setup_search_path()
10533 LANGUAGE plpgsql
AS $$
10535 db TEXT := current_database();
10541 SELECT s.setconfig
INTO cfg
10542 FROM pg_db_role_setting s
10543 JOIN pg_database d
ON d.oid = s.setdatabase
10544 WHERE d.datname = db
AND s.setrole =
0;
10546 IF cfg
IS NOT NULL THEN
10547 SELECT substr(e, length(
'search_path=') +
1)
INTO cur
10548 FROM unnest(cfg)
AS e
10549 WHERE e
LIKE 'search_path=%';
10552 IF cur
IS NULL THEN
10555 new_path :=
'"$user", public, provsql';
10556 EXECUTE format(
'ALTER DATABASE %I SET search_path = %s', db, new_path);
10557 RAISE NOTICE
'ProvSQL: set search_path = % for database "%" (no previous database-level setting). Only new sessions are affected.',
10564 SELECT 1 FROM unnest(string_to[](cur,
','))
AS p
10565 WHERE btrim(btrim(p),
'"') =
'provsql')
10567 RAISE NOTICE
'ProvSQL: search_path for database "%" already contains provsql (= %); no change.',
10572 new_path := cur ||
', provsql';
10573 EXECUTE format(
'ALTER DATABASE %I SET search_path = %s', db, new_path);
10574 RAISE NOTICE
'ProvSQL: appended provsql to search_path for database "%" (now: %). Only new sessions are affected.',
10580GRANT USAGE
ON SCHEMA provsql
TO PUBLIC;
10582SET search_path
TO public;
10597 has_provsql
BOOLEAN;
10599 SELECT reset_val
INTO rp
FROM pg_settings
WHERE name =
'search_path';
10600 SELECT bool_or(btrim(btrim(p),
'"') =
'provsql')
10602 FROM unnest(string_to[](coalesce(rp,
''),
','))
AS p;
10603 IF NOT coalesce(has_provsql,
false)
THEN
10604 SET LOCAL client_min_messages = notice;
10605 RAISE NOTICE
'ProvSQL: schema "provsql" is not in your default search_path (currently: %).', rp;
10606 RAISE NOTICE
'ProvSQL operators and functions are resolved through search_path. Run "SELECT provsql.setup_search_path();" to add it, or set it manually (e.g. ALTER DATABASE % SET search_path = "$user", public, provsql).', quote_ident(current_database());
10620SELECT provsql.reset_constants_cache();
10621SET search_path
TO provsql;
10636CREATE TABLE update_provenance (
10639 query_type QUERY_TYPE_ENUM,
10641 ts
TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
10642 valid_time TSTZMULTIRANGE
DEFAULT TSTZMULTIRANGE(tstzrange(
CURRENT_TIMESTAMP,
NULL))
10648CREATE OR REPLACE
FUNCTION add_provenance(_tbl REGCLASS)
10656 SELECT 1 FROM pg_attribute
10657 WHERE attrelid = _tbl
AND attname =
'provsql' AND NOT attisdropped
10659 RAISE NOTICE
'table % already has provenance tracking', _tbl;
10665 EXECUTE format(
'ALTER TABLE %s ADD COLUMN provsql UUID', _tbl);
10667 'UPDATE %s SET provsql = public.uuid_generate_v4() WHERE provsql IS NULL',
10669 EXECUTE format(
'CREATE INDEX ON %s(provsql)', _tbl);
10671 'CREATE TRIGGER provenance_guard BEFORE INSERT OR UPDATE OF provsql '
10672 'ON %s FOR EACH ROW EXECUTE PROCEDURE provsql.provenance_guard()',
10675 EXECUTE format(
'CREATE TRIGGER insert_statement AFTER INSERT ON %s REFERENCING NEW TABLE AS NEW_TABLE FOR EACH STATEMENT EXECUTE PROCEDURE provsql.insert_statement_trigger()', _tbl);
10676 EXECUTE format(
'CREATE TRIGGER delete_statement AFTER DELETE ON %s REFERENCING OLD TABLE AS OLD_TABLE FOR EACH STATEMENT EXECUTE PROCEDURE provsql.delete_statement_trigger()', _tbl);
10677 EXECUTE format(
'CREATE TRIGGER update_statement AFTER UPDATE ON %s REFERENCING OLD TABLE AS OLD_TABLE NEW TABLE AS NEW_TABLE FOR EACH STATEMENT EXECUTE PROCEDURE provsql.update_statement_trigger()', _tbl);
10679 PERFORM provsql.set_table_info(_tbl::oid,
'tid');
10680 PERFORM provsql.set_ancestors(_tbl::oid,
ARRAY[_tbl::oid]);
10682$$
LANGUAGE plpgsql SECURITY DEFINER;
10688CREATE OR REPLACE
FUNCTION delete_statement_trigger()
10697 enable_trigger BOOL;
10699 enable_trigger := current_setting(
'provsql.update_provenance',
true);
10700 IF enable_trigger =
'f' THEN
10703 delete_token := public.uuid_generate_v4();
10705 PERFORM create_gate(delete_token,
'update');
10709 FROM pg_stat_activity
10710 WHERE pid = pg_backend_pid();
10712 INSERT INTO update_provenance (provsql, query, query_type, username, ts, valid_time)
10713 VALUES (delete_token, query_text,
'DELETE',
current_user,
CURRENT_TIMESTAMP, TSTZMULTIRANGE(tstzrange(
CURRENT_TIMESTAMP,
NULL)));
10715 PERFORM set_config(
'provsql.update_provenance',
'off',
false);
10716 EXECUTE format(
'INSERT INTO %I.%I SELECT * FROM OLD_TABLE;', TG_TABLE_SCHEMA, TG_TABLE_NAME);
10717 PERFORM set_config(
'provsql.update_provenance',
'on',
false);
10719 FOR r
IN (
SELECT *
FROM OLD_TABLE) LOOP
10720 old_token := r.provsql;
10721 new_token := provenance_monus(old_token, delete_token);
10723 PERFORM set_config(
'provsql.update_provenance',
'off',
false);
10724 EXECUTE format(
'UPDATE %I.%I SET provsql = $1 WHERE provsql = $2;', TG_TABLE_SCHEMA, TG_TABLE_NAME)
10725 USING new_token, old_token;
10726 PERFORM set_config(
'provsql.update_provenance',
'on',
false);
10731$$
LANGUAGE plpgsql
SET search_path=provsql,pg_temp SECURITY DEFINER;
10740CREATE OR REPLACE
FUNCTION insert_statement_trigger()
10749 enable_trigger BOOL;
10751 enable_trigger := current_setting(
'provsql.update_provenance',
true);
10752 IF enable_trigger =
'f' THEN
10756 insert_token := public.uuid_generate_v4();
10758 PERFORM create_gate(insert_token,
'update');
10762 FROM pg_stat_activity
10763 WHERE pid = pg_backend_pid();
10765 INSERT INTO update_provenance (provsql, query, query_type, username, ts, valid_time)
10766 VALUES (insert_token, query_text,
'INSERT',
current_user,
CURRENT_TIMESTAMP, TSTZMULTIRANGE(tstzrange(
CURRENT_TIMESTAMP,
NULL)));
10768 FOR r
IN (
SELECT *
FROM NEW_TABLE) LOOP
10769 old_token := r.provsql;
10770 new_token := provenance_times(old_token, insert_token);
10771 PERFORM set_config(
'provsql.update_provenance',
'off',
false);
10772 EXECUTE format(
'UPDATE %I.%I SET provsql = $1 WHERE provsql = $2;', TG_TABLE_SCHEMA, TG_TABLE_NAME)
10773 USING new_token, old_token;
10774 PERFORM set_config(
'provsql.update_provenance',
'on',
false);
10779$$
LANGUAGE plpgsql
SET search_path=provsql,pg_temp SECURITY DEFINER;
10787CREATE OR REPLACE
FUNCTION update_statement_trigger()
10796 enable_trigger BOOL;
10798 enable_trigger := current_setting(
'provsql.update_provenance',
true);
10799 IF enable_trigger =
'f' THEN
10802 update_token := public.uuid_generate_v4();
10804 PERFORM create_gate(update_token,
'update');
10808 FROM pg_stat_activity
10809 WHERE pid = pg_backend_pid();
10811 INSERT INTO update_provenance (provsql, query, query_type, username, ts, valid_time)
10812 VALUES (update_token, query_text,
'UPDATE',
current_user,
CURRENT_TIMESTAMP, TSTZMULTIRANGE(tstzrange(
CURRENT_TIMESTAMP,
NULL)));
10814 FOR r
IN (
SELECT *
FROM NEW_TABLE) LOOP
10815 old_token := r.provsql;
10816 new_token := provenance_times(old_token, update_token);
10818 PERFORM set_config(
'provsql.update_provenance',
'off',
false);
10819 EXECUTE format(
'UPDATE %I.%I SET provsql = $1 WHERE provsql = $2;', TG_TABLE_SCHEMA, TG_TABLE_NAME)
10820 USING new_token, old_token;
10821 PERFORM set_config(
'provsql.update_provenance',
'on',
false);
10824 PERFORM set_config(
'provsql.update_provenance',
'off',
false);
10825 EXECUTE format(
'INSERT INTO %I.%I SELECT * FROM OLD_TABLE;', TG_TABLE_SCHEMA, TG_TABLE_NAME);
10826 PERFORM set_config(
'provsql.update_provenance',
'on',
false);
10828 FOR r
IN (
SELECT *
FROM OLD_TABLE) LOOP
10829 old_token := r.provsql;
10830 new_token := provenance_monus(old_token, update_token);
10832 PERFORM set_config(
'provsql.update_provenance',
'off',
false);
10833 EXECUTE format(
'UPDATE %I.%I SET provsql = $1 WHERE provsql = $2;', TG_TABLE_SCHEMA, TG_TABLE_NAME)
10834 USING new_token, old_token;
10835 PERFORM set_config(
'provsql.update_provenance',
'on',
false);
10840$$
LANGUAGE plpgsql
SET search_path=provsql,pg_temp SECURITY DEFINER;
10852SET search_path
TO provsql;
10866CREATE FUNCTION sr_temporal(token ANYELEMENT, token2value REGCLASS)
10867 RETURNS TSTZMULTIRANGE
AS
10870 RETURN provsql.provenance_evaluate_compiled(
10874 '{(,)}'::TSTZMULTIRANGE
10877$$
LANGUAGE plpgsql STRICT PARALLEL SAFE STABLE;
10893CREATE FUNCTION sr_interval_num(token ANYELEMENT, token2value REGCLASS)
10894 RETURNS nummultirange
AS
10897 RETURN provsql.provenance_evaluate_compiled(
10901 '{(,)}'::nummultirange
10904$$
LANGUAGE plpgsql STRICT PARALLEL SAFE STABLE;
10920CREATE FUNCTION sr_interval_int(token ANYELEMENT, token2value REGCLASS)
10921 RETURNS int4multirange
AS
10924 RETURN provsql.provenance_evaluate_compiled(
10928 '{(,)}'::int4multirange
10931$$
LANGUAGE plpgsql STRICT PARALLEL SAFE STABLE;
10942CREATE OR REPLACE
FUNCTION union_tstzintervals(
10944 token2value REGCLASS
10946RETURNS TSTZMULTIRANGE
AS
10948 SELECT sr_temporal(token, token2value)
10949$$
LANGUAGE SQL PARALLEL SAFE STABLE;
10959CREATE OR REPLACE
FUNCTION timetravel(
10961 at_time TIMESTAMPTZ
10963RETURNS SETOF RECORD
10968 RETURN QUERY
EXECUTE format(
10972 sr_temporal(provenance(), %2$L)
10976 sr_temporal(provenance(), %2$L) @> %3$L::TIMESTAMPTZ
10979 'provsql.time_validity_view',
10994CREATE OR REPLACE
FUNCTION timeslice(
10996 from_time TIMESTAMPTZ,
10997 to_time TIMESTAMPTZ
10999RETURNS SETOF RECORD
11004 RETURN QUERY
EXECUTE format(
11008 sr_temporal(provenance(), %2$L)
11012 sr_temporal(provenance(), %2$L)
11013 && tstzrange(%3$L::TIMESTAMPTZ, %4$L::TIMESTAMPTZ)
11016 'provsql.time_validity_view',
11033CREATE OR REPLACE
FUNCTION history(
11038RETURNS SETOF RECORD
11043 condition TEXT :=
'';
11046 IF array_length(col_names,
1)
IS NULL
11047 OR array_length(col_values,
1)
IS NULL
11048 OR array_length(col_names,
1) != array_length(col_values,
1)
11050 RAISE EXCEPTION
'col_names and col_values must have the same (non-null) length';
11053 FOR i
IN 1..array_length(col_names,
1)
11056 condition := condition ||
' AND ';
11058 condition := condition || format(
'%I = %L', col_names[i], col_values[i]);
11061 RETURN QUERY
EXECUTE format(
11065 sr_temporal(provenance(), %L)
11072 'provsql.time_validity_view',
11085CREATE OR REPLACE
FUNCTION get_valid_time(
11089RETURNS TSTZMULTIRANGE
11093 result TSTZMULTIRANGE;
11098 sr_temporal(provenance(), %L)
11104 'provsql.time_validity_view',
11123CREATE OR REPLACE
FUNCTION undo(
11138 SELECT query
INTO undone_query
11139 FROM update_provenance
11143 IF undone_query
IS NULL THEN
11144 RAISE NOTICE
'Unable to find % in update_provenance', c;
11150 FROM pg_stat_activity
11151 WHERE pid = pg_backend_pid();
11153 undo_token := public.uuid_generate_v4();
11154 PERFORM create_gate(undo_token,
'update');
11155 INSERT INTO update_provenance(provsql, query, query_type, username, ts, valid_time)
11162 TSTZMULTIRANGE(tstzrange(
CURRENT_TIMESTAMP,
NULL))
11165 PERFORM set_config(
'provsql.update_provenance',
'off',
false);
11170 WHERE nspname
NOT IN (
'pg_catalog',
'information_schema',
'pg_toast',
'pg_temp_1',
'pg_toast_temp_1')
11173 EXECUTE format(
'SELECT tablename AS tname FROM pg_tables WHERE schemaname = %L', schema_rec.nspname)
11177 FROM information_schema.columns
11178 WHERE table_schema = schema_rec.nspname
11179 AND table_name = table_rec.tname
11180 AND table_name <>
'update_provenance'
11181 AND column_name =
'provsql'
11184 EXECUTE format(
'SELECT provsql AS x FROM %I.%I', schema_rec.nspname, table_rec.tname)
11186 new_x := replace_the_circuit(row_rec.x, c, undo_token);
11187 EXECUTE format(
'UPDATE %I.%I SET provsql = $1 WHERE provsql = $2',
11188 schema_rec.nspname, table_rec.tname)
11189 USING new_x, row_rec.x;
11195 PERFORM set_config(
'provsql.update_provenance',
'on',
false);
11211CREATE OR REPLACE
FUNCTION replace_the_circuit(
11223 ntype PROVENANCE_GATE;
11226 RETURN provenance_monus(c, u);
11228 ELSIF get_gate_type(x) =
'update' OR get_gate_type(x) =
'input' THEN
11232 FOREACH child
IN ARRAY get_children(x)
11234 nchildren := array_append(nchildren, replace_the_circuit(child, c, u));
11237 ntoken := public.uuid_generate_v4();
11238 ntype := get_gate_type(x);
11240 PERFORM create_gate(ntoken, ntype, nchildren);
11250CREATE VIEW provsql.time_validity_view
AS
11251 SELECT valid_time
AS value, provsql
AS provenance
FROM provsql.update_provenance;
11255SET search_path
TO public;
11262SELECT provsql.reset_constants_cache();