Monthly Summary: Add WHERE Clause Support to REFRESH MATERIALIZED VIEW — May 2026
Overview
This month saw significant technical maturation of the patch to add REFRESH MATERIALIZED VIEW mv WHERE <predicate> syntax to PostgreSQL. The proposal addresses a fundamental scalability limitation: full MV refreshes are O(N) when only O(delta) work is needed. The patch evolved from initial design discussions through concrete bug fixes and resolution of key architectural questions, culminating in a v3 implementation with formal correctness guarantees.
Architecture
The feature touches grammar/parser, rewrite/optimizer, executor/SPI, locking/concurrency, and catalog/index subsystems. It provides two refresh paths:
direct_modpath (FOR UPDATE + single CTE with upsert/anti-join delete): Uses ROW EXCLUSIVE lock, allowing concurrent readers AND writersmatch_mergepath (diff-based, reusing existing concurrent refresh infrastructure): Uses EXCLUSIVE lock, allowing readers but blocking writers
Both paths require a unique index on the MV for conflict resolution, row identification, and anti-join stale-row detection. Volatile functions are forbidden in the WHERE clause to ensure deterministic predicate evaluation.
Key Developments This Month
Deadlock Prevention
The deadlock concern from overlapping concurrent refreshes was resolved via deterministic lock ordering. The SELECT ... FOR UPDATE step now includes ORDER BY on unique key columns, and the upsert source is similarly ordered — a textbook total-ordering approach to deadlock prevention.
Scope Drift Bug Fix
A consistency bug was identified and fixed: when a row's non-key columns change such that it newly matches the WHERE predicate, but an old row with the same key exists in the MV (outside the predicate's match set), the match_merge path would fail with a unique constraint violation. Both paths now use INSERT ... ON CONFLICT DO UPDATE (or DO NOTHING for no non-key columns), resolving collisions in-place.
CONCURRENTLY Semantics Resolved
The semantic inversion issue was decisively resolved by remapping terminology:
CONCURRENTLY→direct_modpath (ROW EXCLUSIVE, most permissive — readers and writers allowed)- Non-concurrent →
match_mergepath (EXCLUSIVE, readers allowed, writers blocked)
This restores the historical meaning of CONCURRENTLY as "less restrictive locking."
ON CONFLICT Index Bug Fix
Dharin Shah identified a bug where indnatts (including INCLUDE columns) was used instead of indnkeyatts when building ON CONFLICT target lists, causing failures for indexes with INCLUDE columns.
Formal Correctness Statement
The author provided a detailed correctness argument for the direct_mod path under READ COMMITTED:
- FOR UPDATE locks held to transaction end bridge the locking SELECT and CTE
- Overlapping refreshes block for the full transaction duration
- The CTE executes as a single statement, providing atomic before/after visibility to readers
- Key collisions serialized by ON CONFLICT + unique index with last-writer-wins semantics
Performance Results
Benchmarks validate the O(delta) design goal:
- Single-row refreshes: ~43-45% of baseline TPS (constant overhead regardless of MV size)
- Batch=50 with 4+ clients: matches or exceeds full refresh due to reduced lock contention
- Performance is independent of total MV size, comparable to hand-maintained trigger tables
Open Issues
- Isolation level coverage: Correctness analysis only covers READ COMMITTED; REPEATABLE READ and SERIALIZABLE behavior unanalyzed
- Predicate push-down reliability: Performance depends on optimizer pushing WHERE to base tables; complex views (aggregation, CTEs, window functions) may not benefit
- Committer engagement: No committer has responded despite the feature's broad scope
- IVM interaction: Relationship to ongoing incremental view maintenance work undiscussed
- SQL standard deviation: Non-standard extension with no discussion of alternative syntax approaches