Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ true until the next version shipped.
computed from 1155:

awk -F'\t' '$4=="never"' test/check_ledger.tsv | wc -l
- Ungrouped vectorized aggregate over a unique-key inner Hash Join (#752).

The fold used to require a single base relation, so a star-schema join dropped it.
A unique dimension is a filter of the fact table, so the fold can keep running.
Duplicate-key dimensions, LEFT joins, extra Join Filters, and grouped aggregation over a join still use core Agg.
A Join Filter besides the hash clause is not a membership test, so the fold refuses it.
`pgcolumnar.enable_ungrouped_vector_agg` stays off by default.

checks_never_observed_red 1155 -> 1162
covered native_join_vector_agg, 13 checks, last-red 2026-09-12

- The mutation ledger covers a third suite: `differential`, 204 checks (#752).

Expand Down
2 changes: 1 addition & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ disk. It never changes the values that a table returns.
| `pgcolumnar.enable_bloom_filter` | boolean | `on` | Skip chunk groups on equality filters using per-chunk bloom filters. |
| `pgcolumnar.enable_join_runtime_filter` | boolean | `on` | Wrap a serial inner Hash Join so the build keys can skip fact-table groups and reject non-matching rows. Direct columnar scan only. Group skip needs the fact table clustered on the join key. On by default. |
| `pgcolumnar.enable_read_stream` | boolean | `on` | Prefetch block reads with the read stream API. Effective on PostgreSQL 17 and later. |
| `pgcolumnar.enable_ungrouped_vector_agg` | boolean | `off` | Answer an ungrouped aggregate (`count`, `sum`, `avg`, `min`, `max` with no `GROUP BY`) with a batch fold over decoded vectors instead of row-at-a-time. Off by default. |
| `pgcolumnar.enable_ungrouped_vector_agg` | boolean | `off` | Answer an ungrouped aggregate (`count`, `sum`, `avg`, `min`, `max` with no `GROUP BY`) with a batch fold over decoded vectors instead of row-at-a-time. A unique-key inner Hash Join can keep that fold. Off by default. |
| `pgcolumnar.enable_parallel_vector_agg` | boolean | `off` | Let the ungrouped batch fold run as a parallel partial aggregate under `Gather`, each worker folding its own row groups. Requires `pgcolumnar.enable_ungrouped_vector_agg`. Off by default. |
| `pgcolumnar.enable_column_projection` | boolean | `on` | Read only the columns a query references rather than every column of the row group. |
| `pgcolumnar.enable_index_fetch_penalty` | boolean | `on` | Charge a columnar index scan for the row-group decode its per-row heap fetches force, so the planner does not treat a columnar fetch as if it were a heap page read. Set to `off` to restore the pre-1.0-alpha planner behaviour. |
Expand Down
3 changes: 3 additions & 0 deletions docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ settings see the [configuration reference](configuration.md); for constraints se
The GUC `pgcolumnar.enable_join_runtime_filter` is on by default.
Group skip needs the fact table clustered on the join key.
It does not wrap LEFT, SEMI, ANTI, CROSS, parallel, or projection scans.
- Ungrouped vectorized aggregate over a unique-key inner Hash Join.
A unique dimension is a filter of the fact table, so the fold can keep running.
Duplicate-key dimensions and LEFT joins stay on the core Agg plan.
- Parallel scan across a table's row groups.
- Read stream prefetch of block reads on PostgreSQL 17 and later
(`pgcolumnar.enable_read_stream`).
Expand Down
16 changes: 16 additions & 0 deletions docs/how-to.md
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,22 @@ EXPLAIN (ANALYZE) SELECT count(*) FROM events;
surviving rows and decodes the filtered columns. Keep the filter on a sorted or
bloomed column so chunk-group skipping removes most groups first.

## Fold an aggregate over a unique-key join

An ungrouped aggregate over a unique-key inner Hash Join can keep the
vectorized fold.

```sql
SET pgcolumnar.enable_ungrouped_vector_agg = on;
EXPLAIN (COSTS OFF)
SELECT count(*), sum(fact.amount)
FROM fact JOIN dim ON fact.k = dim.k;
```

**Tuning.** The dimension needs a unique constraint on the join key.
A duplicate-key dimension keeps the core Agg plan.
Grouped aggregation over a join is not this path.

## Measure and introspect

Inspect physical layout, sort quality, and query plans.
Expand Down
14 changes: 11 additions & 3 deletions docs/limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -642,8 +642,9 @@ refuse it.

## Vectorized aggregate coverage

The vectorized aggregate path covers one shape only. That shape is
`SELECT agg(col) FROM t [WHERE ...]`, on one relation and with no grouping.
The vectorized aggregate path covers one ungrouped shape. That shape is
`SELECT agg(col) FROM t [WHERE ...]`, on one relation or a unique-key inner
Hash Join, with no grouping.

The target list may contain expressions over those aggregates. `count(*)::text`,
`avg(a)+avg(b)`, `round(avg(a), 2)` and `max(a)-min(a)` all take the path. What
Expand All @@ -664,9 +665,16 @@ Each other query uses the scalar plan and stays correct. These include `sum` or
- aggregates with `DISTINCT`
- `GROUP BY` (unless the opt-in grouped path below is enabled) and `HAVING`
- filters that are not simple
- joins
- joins other than a unique-key inner Hash Join
- a reference to a whole row or to a system column

A unique-key inner Hash Join is a filter of the fact table.
The ungrouped fold can run on that shape when
`pgcolumnar.enable_ungrouped_vector_agg` is on.
A dimension with duplicate keys stays on the core Agg plan.
A LEFT join stays on the core Agg plan.
Grouped aggregation over a join is not this path.

A separate opt-in path vectorizes `GROUP BY`. It is off by default. Set
`pgcolumnar.enable_group_vectorization` to `on` to enable it. It covers
`SELECT <keys>, agg(col) ... [WHERE ...] GROUP BY <keys>` on one columnar
Expand Down
1 change: 1 addition & 0 deletions docs/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ controlled by a setting in the [Configuration reference](configuration.md):
- `count(*)` answered from catalog metadata when there is no filter.
- Join runtime filter: a serial inner Hash Join can skip fact-table groups and reject non-matching rows using the build-side keys.
Cluster the fact table on the join key. Group skip is a no-op when every group holds every key.
- Vectorized aggregate over a unique-key inner Hash Join when the ungrouped fold is on.

#### Reading the filter counters

Expand Down
Loading
Loading