| From: | Tomas Vondra <tomas(dot)vondra(at)postgresql(dot)org> |
|---|---|
| To: | pgsql-committers(at)lists(dot)postgresql(dot)org |
| Subject: | pgsql: Reject join input paths parameterized by pushed-down outer joins |
| Date: | 2026-11-11 17:49:14 |
| Message-ID: | E1xRCRe-00000000LHB-0kWL@gemulon.postgresql.org |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-committers |
Reject join input paths parameterized by pushed-down outer joins.
Commit a798660ebe3 taught try_nestloop_path(), try_mergejoin_path() and
try_hashjoin_path() to reject an input path with a nonsensical
parameterization. But that check has a gap, making it possible to hit
the assertion in get_joinrel_parampathinfo() added by 5b7b5518d0ea.
We need to reject all paths whose parameterization includes the outer
join being formed at the current join, since a value that is supposed to
be nulled by that outer join can't be available before the join is done.
That check considered only sjinfo->ojrelid, but that's insufficient
because of joins pushed down into the RHS per outer-join identity 3.
That relid is added only at the join that completes it, with the relid
of the outer join performed there (see add_outer_joins_to_relids).
This omission allowed building paths parameterized by a pushed-down
outer join, which is still nonsensical. In non-assert builds this is
mostly harmless - we add the patch to the joinrel pathlist, but as the
parameterization contains relids within the joinrel itself, no upper
join can supply that. And thus it will be unused.
To fix, determine the full set of outer joins computed at the current
join. That includes sjinfo's own OJ plus any pushed-down OJs completed
there. We do this once in add_paths_to_joinrel(), and save it in a new
JoinPathExtraData field, and check the input paths' parameterization
against that whole set. The partial-path variants need no change, since
they never accept input paths whose parameterization isn't satisfied by
the outer rel.
Backpatch to 16, where 2489d76c490 made Vars outer-join-aware and
9df8f903eb6 introduced the delayed addition of pushed-down outer join
relids. The checks added by a798660ebe3 did not cover this case.
The new JoinPathExtraData field needs to go at the end of the struct,
to avoid an ABI break. It's allocated only in add_paths_to_joinrel (on
stack), and only passed through a pointer, so this should be safe.
Author: Tomas Vondra <tomas(at)vondra(dot)me>
Reviewed-by: Tender Wang <tndrwang(at)gmail(dot)com>
Reviewed-by: Álvaro Herrera <alvherre(at)kurilemu(dot)de>
Reviewed-by: Richard Guo <guofenglinux(at)gmail(dot)com>
Backpatch-through: 16
Discussion: https://postgr.es/m/72d81c95-60fc-48bb-b5e2-e3346c17dd11@vondra.me
Branch
------
REL_16_STABLE
Details
-------
https://git.postgresql.org/pg/commitdiff/be913fabe79d73a37af55eafc3d3352db4c3d1ca
Modified Files
--------------
src/backend/optimizer/path/joinpath.c | 53 +++++++++++++++++++++++++++--------
src/include/nodes/pathnodes.h | 4 +++
src/test/regress/expected/join.out | 35 +++++++++++++++++++++++
src/test/regress/sql/join.sql | 15 ++++++++++
4 files changed, 95 insertions(+), 12 deletions(-)
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Masahiko Sawada | 2026-11-11 18:38:44 | pgsql: Fix the type of heap tuple count passed to parallel vacuum. |
| Previous Message | Tomas Vondra | 2026-11-11 17:49:00 | pgsql: Reject join input paths parameterized by pushed-down outer joins |