Partition pruning and index usage rely on the planner knowing the partition key predicate at plan time so it can avoid scanning partitions and use index seeks. Two common mistakes prevent that: 1) putting the predicate inside a materialized CTE (an optimization fence in older Postgres / default materialization semantics), and 2) expressing the predicate with non-constant/unstable expressions so the planner can’t evaluate it at plan time.Bad (disables pruning / index use):-- CTE is materialized; predicate inside prevents pushdown/pruning
WITH recent AS (
SELECT * FROM events -- events is partitioned by event_day
WHERE event_day >= current_date - interval '7 days'
)
SELECT count(*) FROM recent WHERE user_id = 123;
Because the CTE may be materialized, the planner can’t push the event_day filter down to the partitioned table; it may scan all partitions and then filter, missing partition pruning and index on (event_day,user_id).Better options to enable pruning and index use:1) Push the predicate to the outer query (or avoid materializing):-- Predicate applied directly to partitioned table; planner can prune
SELECT count(*)
FROM events
WHERE event_day >= current_date - interval '7 days'
AND user_id = 123;
2) If you need a CTE for readability but want pushdown, mark it NOT MATERIALIZED (Postgres >=12):WITH recent AS NOT MATERIALIZED (
SELECT * FROM events
WHERE event_day >= current_date - interval '7 days'
)
SELECT count(*) FROM recent WHERE user_id = 123;
3) Ensure the predicate is a planner-evaluable expression (avoid wrapper functions or runtime-only values). Prefer direct column comparisons to constants or stable functions:-- Use a concrete cutoff variable computed once so planner can consider it
SELECT count(*) FROM events
WHERE event_day >= (CURRENT_DATE - 7) -- simple date arithmetic
AND user_id = 123;
Why this matters:- Materialized CTE blocks predicate pushdown; planner may materialize and scan every partition.- Non-evaluable predicates (volatile functions, concatenation, prepared-statement parameter nuances) can prevent pruning.- When planner sees a direct constraint on the partition key it prunes partitions and can use per-partition indexes, drastically reducing IO.If you must use dynamic parameters in prepared statements, consider explicit query construction, prepared-plan-aware techniques (EXECUTE with constants), or ensure expressions are simple and immutable so the planner can prune.