Skip to content

The ungrouped zone-map aggregate ignores a WHERE that references no column, and returns rows heap does not #1435

Closed

Description

@jdatcmd

Found by @OffgridwithJD reviewing #1429. Measured on PG17 and PG18 at default settings.

What happens

On a columnar table, an ungrouped aggregate whose WHERE references no column ignores that WHERE:

query columnar heap
SELECT count(*), min(v), max(v) FROM t WHERE (SELECT false) 1000 / 1 / 1000 0 / NULL / NULL
... WHERE now() < '2000-01-01' 1000 / 1 / 1000 0 rows counted
... WHERE current_user = 'x' 1000 / 1 / 1000 0 rows counted

The plan shows Columnar Vectorized Aggregates: 3, with no gating Result above it. This is a wrong answer, not a performance issue.

Cause, as read in the code

src/columnar_vector.c builds the ungrouped node's quals with extract_actual_clauses(factRel->baserestrictinfo, false) (around line 1567). With pseudoconstant = false that drops the gating quals, so quals is NIL and needsScan is false. The node then answers from the zone maps alone. The node's own gating-qual refusal, further down (around line 1639), is never reached on that path.

The grouped node has the same refusal, and checks it before building its quals, which is why it isn't affected. An RLS policy with no column reference is planned as an ordinary Filter, and was measured safe.

Proposed fix

Refuse the ungrouped node when baserestrictinfo holds a pseudoconstant qual, before needsScan is decided, as the grouped node does. The test should be red first, in both twins: each of the three queries above against heap, plus a premise that a column-free WHERE which is true still gets the vectorized plan.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions