-- Incremental hardening; no records or application tables are copied. BEGIN; SET LOCAL lock_timeout = '10s'; SELECT pg_advisory_xact_lock(290926, 170003); DO $indexes$ DECLARE fk record; index_name text; BEGIN FOR fk IN SELECT c.conrelid, c.conkey, c.conname, t.relname, (SELECT string_agg(quote_ident(a.attname), ', ' ORDER BY k.position) FROM unnest(c.conkey) WITH ORDINALITY k(attnum, position) JOIN pg_attribute a ON a.attrelid=c.conrelid AND a.attnum=k.attnum) AS columns FROM pg_constraint c JOIN pg_class t ON t.oid=c.conrelid JOIN pg_namespace n ON n.oid=t.relnamespace WHERE n.nspname='mne' AND c.contype='f' ORDER BY t.relname,c.conname LOOP IF NOT EXISTS ( SELECT 1 FROM pg_index i JOIN pg_class ix ON ix.oid=i.indexrelid JOIN pg_am am ON am.oid=ix.relam WHERE i.indrelid=fk.conrelid AND i.indisvalid AND i.indisready AND i.indpred IS NULL AND am.amname='btree' AND i.indnkeyatts >= cardinality(fk.conkey) AND ARRAY(SELECT x FROM unnest(i.indkey::smallint[]) WITH ORDINALITY u(x,p) WHERE p <= cardinality(fk.conkey) ORDER BY p)=fk.conkey ) THEN index_name := 'ix_mne_003_' || md5(fk.relname || ':' || fk.conname); EXECUTE format('CREATE INDEX %I ON mne.%I (%s)', index_name, fk.relname, fk.columns); EXECUTE format('COMMENT ON INDEX mne.%I IS %L', index_name, 'Owned by 003_mne_integrity'); END IF; END LOOP; END $indexes$; CREATE OR REPLACE FUNCTION mne.reject_evidence_mutation() RETURNS trigger LANGUAGE plpgsql SET search_path = pg_catalog AS $guard$ BEGIN RAISE EXCEPTION 'MNE evidence is append-only: %.%', TG_TABLE_SCHEMA, TG_TABLE_NAME USING ERRCODE = '23514'; END $guard$; DO $triggers$ DECLARE table_name text; BEGIN FOREACH table_name IN ARRAY ARRAY['audit_events','business_status_history','document_versions','negotiation_events'] LOOP EXECUTE format('CREATE OR REPLACE TRIGGER mne_evidence_immutable BEFORE UPDATE OR DELETE OR TRUNCATE ON mne.%I FOR EACH STATEMENT EXECUTE FUNCTION mne.reject_evidence_mutation()', table_name); END LOOP; END $triggers$; COMMIT;