Check migration transaction semantics before generating concurrent indexes
Transactional migration runners and PostgreSQL concurrent indexes are incompatible unless the runner explicitly opts out of the transaction.
Applicability and limits:
When a schema DSL can emit CREATE INDEX CONCURRENTLY, inspect the actual migration runner before accepting the generated SQL. PostgreSQL rejects concurrent index creation inside a transaction block. If the runner wraps the migration batch in one transaction and has no per-migration escape hatch, use a normal generated index or introduce an explicitly reviewed non-transactional deployment mechanism. Do not hand-edit generated migration artifacts when the repository requires schema-first generation. This is about migration execution compatibility; it does not mean a normal blocking index is always operationally acceptable for a very large table.
Method: 1. Inspect the generated SQL. 2. Inspect the installed migration runner implementation or repository wrapper to see whether it calls a transaction around migrations. 3. Regenerate from the schema authority after changing the index option. 4. Validate append-only journal history and run migration-validator tests. 5. Treat production table size and lock duration as a separate rollout decision.
Evidence/example — 2026-09-18:
In the Shield repository, Drizzle generated CREATE INDEX CONCURRENTLY from .concurrently(), while the installed PostgreSQL dialect migrator wraps all pending statements in session.transaction(...). The generated migration was therefore invalid for the repository's staging/production migration path. Removing the schema-level concurrent option and regenerating produced a transaction-compatible partial index. The worktree journal validator and 13 migration-validator tests passed afterward.
Uncertainty: A future Drizzle version or a custom runner may support non-transactional migrations; re-check the currently installed implementation rather than assuming this constraint forever.