-- Reconcile schema details that pgschema does not preserve. -- -- pgschema reconciles DDL, but it does not execute seed DML or preserve every -- table storage parameter from schema/schema.sql. It also currently emits -- partition children as standalone CREATE TABLE statements. Every pgschema -- apply caller must run this idempotent script so fresh bootstraps converge on -- the same live database contract as migration-managed databases. DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_inherits WHERE inhparent = 'events'::regclass AND inhrelid = 'events_p_past'::regclass ) THEN -- pgschema may copy parent triggers onto standalone children. Drop -- those copies before ATTACH; PostgreSQL recreates inherited parent -- triggers while attaching and rejects same-named child triggers. DROP TRIGGER IF EXISTS events_created_at_floor ON events_p_past; DROP TRIGGER IF EXISTS community_write_fence_events ON events_p_past; DROP TRIGGER IF EXISTS trg_events_guard_channel_roster_snapshot ON events_p_past; ALTER TABLE events ATTACH PARTITION events_p_past FOR VALUES FROM (MINVALUE) TO ('2026-01-01'); END IF; IF NOT EXISTS ( SELECT 1 FROM pg_inherits WHERE inhparent = 'events'::regclass AND inhrelid = 'events_p2026_01'::regclass ) THEN DROP TRIGGER IF EXISTS events_created_at_floor ON events_p2026_01; DROP TRIGGER IF EXISTS community_write_fence_events ON events_p2026_01; DROP TRIGGER IF EXISTS trg_events_guard_channel_roster_snapshot ON events_p2026_01; ALTER TABLE events ATTACH PARTITION events_p2026_01 FOR VALUES FROM ('2026-01-01') TO ('2026-02-01'); END IF; IF NOT EXISTS ( SELECT 1 FROM pg_inherits WHERE inhparent = 'events'::regclass AND inhrelid = 'events_p2026_02'::regclass ) THEN DROP TRIGGER IF EXISTS events_created_at_floor ON events_p2026_02; DROP TRIGGER IF EXISTS community_write_fence_events ON events_p2026_02; DROP TRIGGER IF EXISTS trg_events_guard_channel_roster_snapshot ON events_p2026_02; ALTER TABLE events ATTACH PARTITION events_p2026_02 FOR VALUES FROM ('2026-02-01') TO ('2026-03-01'); END IF; IF NOT EXISTS ( SELECT 1 FROM pg_inherits WHERE inhparent = 'events'::regclass AND inhrelid = 'events_p2026_03'::regclass ) THEN DROP TRIGGER IF EXISTS events_created_at_floor ON events_p2026_03; DROP TRIGGER IF EXISTS community_write_fence_events ON events_p2026_03; DROP TRIGGER IF EXISTS trg_events_guard_channel_roster_snapshot ON events_p2026_03; ALTER TABLE events ATTACH PARTITION events_p2026_03 FOR VALUES FROM ('2026-03-01') TO ('2026-04-01'); END IF; IF NOT EXISTS ( SELECT 1 FROM pg_inherits WHERE inhparent = 'events'::regclass AND inhrelid = 'events_p2026_04'::regclass ) THEN DROP TRIGGER IF EXISTS events_created_at_floor ON events_p2026_04; DROP TRIGGER IF EXISTS community_write_fence_events ON events_p2026_04; DROP TRIGGER IF EXISTS trg_events_guard_channel_roster_snapshot ON events_p2026_04; ALTER TABLE events ATTACH PARTITION events_p2026_04 FOR VALUES FROM ('2026-04-01') TO ('2026-05-01'); END IF; IF NOT EXISTS ( SELECT 1 FROM pg_inherits WHERE inhparent = 'events'::regclass AND inhrelid = 'events_p2026_05'::regclass ) THEN DROP TRIGGER IF EXISTS events_created_at_floor ON events_p2026_05; DROP TRIGGER IF EXISTS community_write_fence_events ON events_p2026_05; DROP TRIGGER IF EXISTS trg_events_guard_channel_roster_snapshot ON events_p2026_05; ALTER TABLE events ATTACH PARTITION events_p2026_05 FOR VALUES FROM ('2026-05-01') TO ('2026-06-01'); END IF; IF NOT EXISTS ( SELECT 1 FROM pg_inherits WHERE inhparent = 'events'::regclass AND inhrelid = 'events_p2026_06'::regclass ) THEN DROP TRIGGER IF EXISTS events_created_at_floor ON events_p2026_06; DROP TRIGGER IF EXISTS community_write_fence_events ON events_p2026_06; DROP TRIGGER IF EXISTS trg_events_guard_channel_roster_snapshot ON events_p2026_06; ALTER TABLE events ATTACH PARTITION events_p2026_06 FOR VALUES FROM ('2026-06-01') TO ('2026-07-01'); END IF; IF NOT EXISTS ( SELECT 1 FROM pg_inherits WHERE inhparent = 'events'::regclass AND inhrelid = 'events_p_future'::regclass ) THEN DROP TRIGGER IF EXISTS events_created_at_floor ON events_p_future; DROP TRIGGER IF EXISTS community_write_fence_events ON events_p_future; DROP TRIGGER IF EXISTS trg_events_guard_channel_roster_snapshot ON events_p_future; ALTER TABLE events ATTACH PARTITION events_p_future FOR VALUES FROM ('2026-07-01') TO (MAXVALUE); END IF; -- When pgschema creates partition children as standalone tables, it also -- preserves the parent's identity column on delivery_log children. PostgreSQL -- rejects attaching a child table that has its own identity column, so each -- delivery_log attach path drops that standalone identity first. Raw -- schema-created partitions are already attached, so these branches do not -- run against inherited partition columns. IF NOT EXISTS ( SELECT 1 FROM pg_inherits WHERE inhparent = 'delivery_log'::regclass AND inhrelid = 'delivery_log_p_past'::regclass ) THEN ALTER TABLE delivery_log_p_past ALTER COLUMN id DROP IDENTITY IF EXISTS; DROP TRIGGER IF EXISTS community_write_fence_delivery_log ON delivery_log_p_past; ALTER TABLE delivery_log ATTACH PARTITION delivery_log_p_past FOR VALUES FROM (MINVALUE) TO ('2026-03-01'); END IF; IF NOT EXISTS ( SELECT 1 FROM pg_inherits WHERE inhparent = 'delivery_log'::regclass AND inhrelid = 'delivery_log_p2026_03'::regclass ) THEN ALTER TABLE delivery_log_p2026_03 ALTER COLUMN id DROP IDENTITY IF EXISTS; DROP TRIGGER IF EXISTS community_write_fence_delivery_log ON delivery_log_p2026_03; ALTER TABLE delivery_log ATTACH PARTITION delivery_log_p2026_03 FOR VALUES FROM ('2026-03-01') TO ('2026-04-01'); END IF; IF NOT EXISTS ( SELECT 1 FROM pg_inherits WHERE inhparent = 'delivery_log'::regclass AND inhrelid = 'delivery_log_p2026_04'::regclass ) THEN ALTER TABLE delivery_log_p2026_04 ALTER COLUMN id DROP IDENTITY IF EXISTS; DROP TRIGGER IF EXISTS community_write_fence_delivery_log ON delivery_log_p2026_04; ALTER TABLE delivery_log ATTACH PARTITION delivery_log_p2026_04 FOR VALUES FROM ('2026-04-01') TO ('2026-05-01'); END IF; IF NOT EXISTS ( SELECT 1 FROM pg_inherits WHERE inhparent = 'delivery_log'::regclass AND inhrelid = 'delivery_log_p2026_05'::regclass ) THEN ALTER TABLE delivery_log_p2026_05 ALTER COLUMN id DROP IDENTITY IF EXISTS; DROP TRIGGER IF EXISTS community_write_fence_delivery_log ON delivery_log_p2026_05; ALTER TABLE delivery_log ATTACH PARTITION delivery_log_p2026_05 FOR VALUES FROM ('2026-05-01') TO ('2026-06-01'); END IF; IF NOT EXISTS ( SELECT 1 FROM pg_inherits WHERE inhparent = 'delivery_log'::regclass AND inhrelid = 'delivery_log_p2026_06'::regclass ) THEN ALTER TABLE delivery_log_p2026_06 ALTER COLUMN id DROP IDENTITY IF EXISTS; DROP TRIGGER IF EXISTS community_write_fence_delivery_log ON delivery_log_p2026_06; ALTER TABLE delivery_log ATTACH PARTITION delivery_log_p2026_06 FOR VALUES FROM ('2026-06-01') TO ('2026-07-01'); END IF; IF NOT EXISTS ( SELECT 1 FROM pg_inherits WHERE inhparent = 'delivery_log'::regclass AND inhrelid = 'delivery_log_p_future'::regclass ) THEN ALTER TABLE delivery_log_p_future ALTER COLUMN id DROP IDENTITY IF EXISTS; DROP TRIGGER IF EXISTS community_write_fence_delivery_log ON delivery_log_p_future; ALTER TABLE delivery_log ATTACH PARTITION delivery_log_p_future FOR VALUES FROM ('2026-07-01') TO (MAXVALUE); END IF; END $$; -- pgschema moves the partition key to the front of a partitioned table's -- primary key when it creates the table, so events gets -- PRIMARY KEY (created_at, community_id, id) instead of the declared -- (community_id, created_at, id). Every later plan then drops and re-adds -- events_pkey and each declared child's key, which fails once the children are -- attached because their keys are inherited. Rebuild the key on the parent in -- the declared order; PostgreSQL recreates each partition's key with it. -- delivery_log already declares its partition key first. This block is a -- verbatim copy of scripts/repair-events-pkey.sql; keep the two identical. DO $$ DECLARE declared CONSTANT text := 'PRIMARY KEY (community_id, created_at, id)'; BEGIN -- Also runs before pgschema apply, when events may not exist yet. IF to_regclass('events') IS NULL THEN RETURN; END IF; IF ( SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conrelid = 'events'::regclass AND contype = 'p' ) IS DISTINCT FROM declared THEN -- The rebuild locks events and every partition ACCESS EXCLUSIVE. Fail -- fast instead of queueing behind a relay that still uses them. BEGIN PERFORM set_config('lock_timeout', '10s', true); ALTER TABLE events DROP CONSTRAINT IF EXISTS events_pkey; ALTER TABLE events ADD CONSTRAINT events_pkey PRIMARY KEY (community_id, created_at, id); EXCEPTION WHEN lock_not_available THEN RAISE EXCEPTION 'timed out locking events to rebuild events_pkey' USING HINT = 'Stop any relay using this database, then rerun.'; END; END IF; IF EXISTS ( SELECT 1 FROM pg_constraint WHERE contype = 'p' AND conrelid IN ( SELECT 'events'::regclass UNION ALL SELECT inhrelid FROM pg_inherits WHERE inhparent = 'events'::regclass ) AND pg_get_constraintdef(oid) <> declared ) THEN RAISE EXCEPTION 'events and its partitions must have %', declared; END IF; END $$; -- pgschema drops multi-column CHECK constraints. Restore the direct-action -- shape CHECK from schema/schema.sql (and migration 0055) verbatim. DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conrelid = 'relay_admin_actions'::regclass AND conname = 'relay_admin_actions_direct_shape' ) THEN ALTER TABLE relay_admin_actions ADD CONSTRAINT relay_admin_actions_direct_shape CHECK ( report_id IS NOT NULL OR (action = 'ban' AND enforcement_target_pubkey IS NOT NULL AND timeout_secs IS NULL AND timeout_until IS NULL) OR (action = 'timeout' AND enforcement_target_pubkey IS NOT NULL AND timeout_secs IS NOT NULL AND timeout_until IS NOT NULL) OR (action = 'delete' AND enforcement_target_event_id IS NOT NULL AND enforcement_target_pubkey IS NOT NULL AND timeout_secs IS NULL AND timeout_until IS NULL) ); END IF; END $$; -- pgschema reconciles DDL but does not apply seed DML or table storage -- parameters from schema/schema.sql. Restore those parts of the desired-state -- contract explicitly and fail the bootstrap if the live catalog disagrees. ALTER TABLE replica_heartbeat SET (vacuum_truncate = false); INSERT INTO replica_heartbeat (id) VALUES (1) ON CONFLICT (id) DO NOTHING; DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_class AS relation JOIN pg_namespace AS namespace ON namespace.oid = relation.relnamespace WHERE namespace.nspname = current_schema() AND relation.relname = 'replica_heartbeat' AND COALESCE( relation.reloptions @> ARRAY['vacuum_truncate=false']::text[], false ) ) THEN RAISE EXCEPTION 'replica_heartbeat must disable vacuum truncation after pgschema apply'; END IF; IF (SELECT count(*) FROM replica_heartbeat WHERE id = 1) <> 1 THEN RAISE EXCEPTION 'replica_heartbeat must contain its singleton row after pgschema apply'; END IF; END $$;