SET LOCAL lock_timeout = '10s'; SET LOCAL statement_timeout = '60s'; -- 0071 was briefly released with only the singleton reservation lease fields. -- Add the durable lifecycle columns without rewriting that already-applied -- migration, then reconcile the singleton with the canonical outstanding -- pairing (when one exists). ALTER TABLE gateway_identity_pairing_start_reservation ADD COLUMN IF NOT EXISTS state text, ADD COLUMN IF NOT EXISTS revision_id uuid, ADD COLUMN IF NOT EXISTS updated_at timestamptz; UPDATE gateway_identity_pairing_start_reservation SET state=CASE WHEN revision_id IS NULL THEN 'starting' ELSE 'paired' END, updated_at=COALESCE(updated_at,created_at,now()) WHERE state IS NULL OR state NOT IN ('starting','paired') OR (state='starting' AND revision_id IS NOT NULL) OR (state='paired' AND revision_id IS NULL) OR updated_at IS NULL; WITH canonical_pairing AS ( SELECT exchange.id AS attempt_id, revision.id AS revision_id, exchange.expires_at FROM gateway_identity_configuration_revisions revision JOIN gateway_identity_onboarding_exchanges exchange ON exchange.revision_id=revision.id WHERE revision.state IN ('draft','validated','failed') AND NOT (exchange.status='cancelled' AND exchange.cleanup_status='completed') ORDER BY revision.created_at DESC,exchange.created_at DESC LIMIT 1 ) INSERT INTO gateway_identity_pairing_start_reservation( singleton,attempt_id,state,revision_id,expires_at,updated_at ) SELECT true,attempt_id,'paired',revision_id,expires_at,now() FROM canonical_pairing ON CONFLICT (singleton) DO UPDATE SET attempt_id=EXCLUDED.attempt_id, state=EXCLUDED.state, revision_id=EXCLUDED.revision_id, expires_at=EXCLUDED.expires_at, updated_at=now(); ALTER TABLE gateway_identity_pairing_start_reservation ALTER COLUMN state SET DEFAULT 'starting', ALTER COLUMN state SET NOT NULL, ALTER COLUMN updated_at SET DEFAULT now(), ALTER COLUMN updated_at SET NOT NULL; ALTER TABLE gateway_identity_pairing_start_reservation DROP CONSTRAINT IF EXISTS gateway_identity_pairing_start_state_value_check, DROP CONSTRAINT IF EXISTS gateway_identity_pairing_start_state_check; ALTER TABLE gateway_identity_pairing_start_reservation ADD CONSTRAINT gateway_identity_pairing_start_state_value_check CHECK (state IN ('starting','paired')), ADD CONSTRAINT gateway_identity_pairing_start_state_check CHECK ( (state='starting' AND revision_id IS NULL) OR (state='paired' AND revision_id IS NOT NULL) ); DO $migration$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conrelid='gateway_identity_pairing_start_reservation'::regclass AND contype='f' AND pg_get_constraintdef(oid) LIKE 'FOREIGN KEY (revision_id)%' ) THEN ALTER TABLE gateway_identity_pairing_start_reservation ADD CONSTRAINT gateway_identity_pairing_start_revision_fk FOREIGN KEY (revision_id) REFERENCES gateway_identity_configuration_revisions(id) ON DELETE RESTRICT; END IF; END $migration$; CREATE UNIQUE INDEX IF NOT EXISTS idx_gateway_identity_pairing_start_revision_unique ON gateway_identity_pairing_start_reservation(revision_id) WHERE revision_id IS NOT NULL; DROP INDEX IF EXISTS idx_gateway_identity_pairing_start_expiry; CREATE INDEX idx_gateway_identity_pairing_start_expiry ON gateway_identity_pairing_start_reservation(state,expires_at);