MP033warningauto-fix

require-concurrent-refresh-matview

REFRESH MATERIALIZED VIEW without CONCURRENTLY blocks all reads.

operation
Views
lock taken
ACCESS EXCLUSIVE
remediation
Fixed by --fix
category
Lock safety

What triggers it

Fires on a RefreshMatViewStmt where concurrent is not set and skipData (WITH NO DATA) is not set — a plain REFRESH that actually recomputes data.

What does not

REFRESH MATERIALIZED VIEW CONCURRENTLY returns null immediately since it is already safe, and so does REFRESH MATERIALIZED VIEW ... WITH NO DATA, which just marks the view unscannable rather than locking for a real refresh.

Where it applies

Applies to every PostgreSQL version MigrationPilot targets. It works on the SQL text alone — no database connection needed.

The lock, and what it blocks

A plain REFRESH holds ACCESS EXCLUSIVE on the materialized view for the whole query that recomputes it, blocking every SELECT against the view until it commits. CONCURRENTLY avoids this by building into a new copy and swapping.

Why it matters

Without CONCURRENTLY, REFRESH acquires ACCESS EXCLUSIVE, blocking all queries against the materialized view for the entire refresh duration. With CONCURRENTLY, reads continue against the old data until refresh completes.

Unsafe, and safe

Flagged

REFRESH MATERIALIZED VIEW mv_user_stats;

Safe alternative

REFRESH MATERIALIZED VIEW CONCURRENTLY mv_user_stats;

What it assumes

REFRESH CONCURRENTLY requires a UNIQUE index on the materialized view; a matview without one cannot switch to CONCURRENTLY, even though the rule does not check for that prerequisite before suggesting the fix.

What the CLI prints

migrationpilot analyze migration.sql
⚠ [MP033] WARNING (line 1)
  REFRESH MATERIALIZED VIEW "mv_user_stats" without CONCURRENTLY takes ACCESS EXCLUSIVE lock, blocking all reads for the entire refresh duration.

  Safe alternative:
  -- Use CONCURRENTLY to allow reads during refresh (requires a UNIQUE index):
  REFRESH MATERIALIZED VIEW CONCURRENTLY mv_user_stats;

  Why: REFRESH MATERIALIZED VIEW without CONCURRENTLY takes an ACCESS EXCLUSIVE lock for the entire refresh duration, blocking all queries against the view. REFRESH CONCURRENTLY allows reads to continue using the old data while the new data is being computed. Requires a UNIQUE index on the materialized view.
  Docs: https://migrationpilot.dev/rules/mp033

Generated by running the CLI's own formatter over the flagged example above, so it is the text the tool actually produces. A real run also reports the other rules that fire on the same statement; those blocks are left out here.

Turning it off

For one statement, put a comment on the line before it:

-- migrationpilot-disable MP033
REFRESH MATERIALIZED VIEW mv_user_stats;

For the whole project, in .migrationpilotrc.yml — by name or by id:

.migrationpilotrc.yml
rules:
  MP033: false

# or keep it, and downgrade it
rules:
  MP033:
    severity: warning

Try it

Open this rule's flagged example in the playground. It runs in your browser — edit it and watch the finding appear and disappear.

Run MP033 in the playground

Related rules