Partial or expression index the migration's own queries cannot use
Note — this works and blocks nothing, but a performance regression is likely.
What it catches
A partial index only serves queries whose WHERE clause provably implies its predicate; an expression index only serves queries that use the identical expression. An index on lower(email) does nothing for WHERE email = …, and an index WHERE status = 'archived' does nothing for WHERE status = 'active'. When the migration itself contains such a query, the mismatch is visible before it ships.
Fires on
CREATE INDEX idx_users_lower_email ON users (lower(email));
UPDATE users SET verified = true WHERE email = 'a@example.com';Do this instead
Match the shapes: query through the same expression (WHERE lower(email) = …) or index the plain column; give the partial index the predicate the queries actually use. Queries elsewhere in the application are not visible here — the rule only reads this file.
CREATE INDEX CONCURRENTLY idx_users_lower_email ON users (lower(email));
-- and query through the same expression: WHERE lower(email) = lower($1) This rule runs in the hosted service on Startup and above — add --remote with a team token, or use the GitHub Action: npx bolvrk check migration.sql