| From: | Greg Burd <greg(at)burd(dot)me> |
|---|---|
| To: | Greg Burd <greg(at)burd(dot)me> |
| Cc: | Matthias van de Meent <boekewurm+postgres(at)gmail(dot)com>, PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>, Heikki Linnakangas <hlinnaka(at)iki(dot)fi>, Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>, Chris Cleveland <ccleveland(at)dieselpoint(dot)com> |
| Subject: | Re: Let an ordering index scan hand its ORDER BY value to the target list |
| Date: | 2026-10-09 13:28:18 |
| Message-ID: | ChGfmrrDcMPM9quqy69FYxEBqDxgkNG9EgqFal9njID6HIc1O_0E6lmVIbe4wOk-MLR4eGOneBki_awfQIBUlYB6Iz-z5B8BdMfGBkwFGGQ=@burd.me |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
On Thursday, October 8th, 2026 at 10:25 AM, Greg Burd <greg(at)burd(dot)me> wrote:
> An ordered or parallel Append still computes the expression above
> itself. That could be added if anyone cares.
I cared, so v8 adds 0007, which closes the gaps I listed. 0001-0006 are
unchanged from v7.
* The nullable side of an outer join. Above the join, the query's
copy of expensive(a.i) has the join's bit in a.i's nullingrels and
the scan's copy doesn't, so they never matched. But the join
null-extends the scan's value along with the Vars, so the two agree
on every row whenever the expression is strict. For a strict one,
0007 relabels the value with the joinrel's Vars as it passes it up.
At an outer join, setrefs matches it ignoring nullingrels, as it
already does for Vars there (NRM_SUPERSET), and the cost model credits
it the same way. A non-strict expression is still computed above the
join. Heikki's expensive() from 2023 isn't declared STRICT, so it's
in that group.
* Partial paths. 0006 emitted only parallel-safe values from them, but
a worker never evaluates the expression: it reads the stored value,
and everything above it references the column. So a PARALLEL
RESTRICTED function's index can feed a parallel plan now. Gather and
Gather Merge pass the values up.
* Ordered Append, MergeAppend and parallel Append, built the same way
as 0006's unordered Append.
Against 0006, paired runs as before (two sessions of 11 blocks; the
sessions agreed to within 0.4 points):
LEFT JOIN, nullable side -62%
FULL JOIN -95% (173 -> 8.8 ms)
coalesce(expensive(a.i), -1), nullable -40%
partitioned, ordered Append, merge join -29%
partitioned join, parallel allowed -19%
inner join / no expression index within +/-0.6%
The FULL JOIN number is a plan change. Without the value the planner
picks a hash full join over two seq scans and computes expensive() for
every row; with it, a merge full join over the index-only scan.
0007 costs very little at plan time: at most +2.5% planning over 0006
(eleven-rel inner join with the expression). Planner memory is the same
for inner joins and up to 3% higher for LEFT JOINs with the expression
(60 -> 62 kB at three rels).
MERGE was the one surprise. The first version stripped nullingrels
with remove_nulling_relids(), which errors on a MERGE's ROWID_VAR, and
merge.out and updatable_views.out caught it. 0007 uses its own small
mutator now. To check parallel safety, the correctness run has a
PARALLEL RESTRICTED indexed function that raises an error if it's ever
evaluated in a worker. A canary marked PARALLEL SAFE with the same
check does fail, so the check works. 48 query shapes under seven
settings (including debug_parallel_query), compared with
enable_indexonlyscan off: no mismatches. 0006 and 0007 each build
without warnings and pass the meson suite on master (16b94c40120).
The harness and samples are in cf7392-bench-0007.tar.gz.nocfbot.
best.
-greg
| Attachment | Content-Type | Size |
|---|---|---|
| v8-0005-Let-an-index-only-scan-win-on-the-index-expressio.patch | text/x-patch | 12.8 KB |
| v8-0007-Carry-index-computed-values-through-outer-joins-G.patch | text/x-patch | 42.3 KB |
| cf7392-bench-0007.tar.gz.nocfbot | application/octet-stream | 20.7 KB |
| v8-0004-Don-t-charge-an-index-only-scan-for-index-express.patch | text/x-patch | 8.5 KB |
| v8-0002-Don-t-charge-an-ordering-index-scan-for-ORDER-BY-.patch | text/x-patch | 23.2 KB |
| v8-0003-Let-an-ordering-index-scan-below-a-join-emit-its-.patch | text/x-patch | 19.6 KB |
| v8-0001-Let-an-ordering-index-scan-hand-its-ORDER-BY-valu.patch | text/x-patch | 41.8 KB |
| v8-0006-Carry-index-computed-values-up-through-joins-and-.patch | text/x-patch | 70.6 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Álvaro Herrera | 2026-10-09 13:50:04 | Re: Adding init-po and update-po targets to the meson build system |
| Previous Message | Nazir Bilal Yavuz | 2026-10-09 13:23:16 | Re: Adding init-po and update-po targets to the meson build system |