Foreign Keys

Adding a foreign key without NOT VALID takes SHARE ROW EXCLUSIVE on both the source and referenced table and scans the entire table to validate existing rows.

The Problem

sql
-- SHARE ROW EXCLUSIVE on posts AND users, full table scan
ALTER TABLE posts ADD CONSTRAINT fk_posts_user_id
  FOREIGN KEY (user_id) REFERENCES users(id);

Safe Rewrite

Run these as two separate migrations. NOT VALID and VALIDATE must not share a transaction; doing both in one tx defeats the safety primitive (the constraint stays write-locked from ADD CONSTRAINT through the full validation scan). pgfence flags same-tx NOT VALID + VALIDATE as an error.

Step 1: Add the constraint without validation (separate migration)

sql
SET lock_timeout = '2s';

-- NOT VALID: brief SHARE ROW EXCLUSIVE for metadata only, no table scan.
ALTER TABLE posts ADD CONSTRAINT fk_posts_user_id
  FOREIGN KEY (user_id) REFERENCES users(id) NOT VALID;

Step 2: Validate existing rows (separate migration)

sql
SET lock_timeout = '2s';

-- VALIDATE: SHARE UPDATE EXCLUSIVE, non-blocking scan of existing rows.
ALTER TABLE posts VALIDATE CONSTRAINT fk_posts_user_id;