ERROR: unsupported join alias expression

Started by Richard Guo19 days ago2 messageshackers
Beta feature

Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.

appliestests failedCI history

You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:

docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t253818
psql -h localhost -U postgres

Built from patchset v2 (message #2), October 06, 2026 at 11:47 AM.

Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:

git clone --branch t253818_2 https://github.com/hackorum-dev/postgres.git

In a checkout you already have, add the fork once:

git remote add hackorum https://github.com/hackorum-dev/postgres.git

then, for this patchset and every later one:

git fetch hackorum t253818_2 && git checkout t253818_2

Patchset v2 (message #2) is on t253818_2

Jump to latest
#1Richard Guo
guofenglinux@gmail.com

Fuzzing with Claude on add_nullingrels_if_needed() found two cases
that fail with $subject.

create table t (a int);

-- 1
select count(j) from t x left join (t b cross join t c) j on true;
ERROR: unsupported join alias expression

-- 2
select (select j) from t x
left join ((select from t) b cross join (select from t) c) j on x.a = 1;
ERROR: unsupported join alias expression

The first one is raised in the parser. parseCheckAggregates()
flattens join alias Vars without a root, so it can't wrap the expanded
RowExpr in a PlaceHolderVar. I think we can leave such Vars
unexpanded when there is no root, because 1) the planner expands them
later as usual, and 2) a nulled whole-row Var can only match itself,
so nothing is lost for GROUP BY matching. See 0001.

The second one is raised in the planner. The join has no columns, so
the RowExpr has no Vars, and we fall back to evaluating the PHV at the
join's input rels. But that fallback is refused when the Var is an
outer reference from a subquery. I don't see why that's needed. Even
as an outer reference, the Var still refers to a join of root->parse,
since the planner always flattens with query == root->parse. So
looking the join up in root->parse gives the right relids for the PHV.
So I think we can just remove that check. See 0002.

- Richard

Attachments:

t253818_1
v1-0001-Fix-parser-failure-with-whole-row-join-alias-Vars.patchapplication/octet-stream; name=v1-0001-Fix-parser-failure-with-whole-row-join-alias-Vars.patchDownload+54-2
v1-0002-Fix-planner-failure-with-zero-column-join-alias-V.patchapplication/octet-stream; name=v1-0002-Fix-planner-failure-with-zero-column-join-alias-V.patchDownload+27-3
#2Richard Guo
guofenglinux@gmail.com
In reply to: Richard Guo (#1)
Re: ERROR: unsupported join alias expression

On Thu, Sep 17, 2026 at 3:45 PM Richard Guo <guofenglinux@gmail.com> wrote:

Fuzzing with Claude on add_nullingrels_if_needed() found two cases
that fail with $subject.

create table t (a int);

-- 1
select count(j) from t x left join (t b cross join t c) j on true;
ERROR: unsupported join alias expression

-- 2
select (select j) from t x
left join ((select from t) b cross join (select from t) c) j on x.a = 1;
ERROR: unsupported join alias expression

Some more bugs exist in add_nullingrels_if_needed().

-- 3
select j from t t1 left join
(lateral (values (t1.a)) as v(a) cross join t t2) as j on true, t t3;
ERROR: wrong phnullingrels (b 5) (expected (b)) for PlaceHolderVar 2

-- 4 Wrong result
insert into t values (1);
select (j is null) from t t1
left join (t t2 join lateral (select t1.a from (values (3)) v) ss(x)
on true) as j
on false;
?column?
----------
f
(1 row)

-- 5
select 1 from (unnest(array[1, (select sum(a) from t t1)]) as u(b)
right join (values (1)) as v(b) using (b)
full join t t2 on true) as j,
length(j::text) f;
ERROR: wrong phnullingrels (b 3 5) (expected (b 5)) for PlaceHolderVar 1

For 3 and 4, the problem is that if a LATERAL subquery under an inner
join has been pulled up, the join's alias list can contain a bare
lateral reference to a rel outside the join. That rel then ends up in
phrels, so the PHV is evaluated above the outer join that is supposed
to null it.

For 5, the problem is that for a variable-free expression we fall back
to the join's relids but remove the join's own outer-join relid. That
leaves a set spanning both sides of the outer join without the join
itself.

I've updated 0002 to fix all of these bugs in one go. 0001 is
unchanged.

- Richard

Attachments:

t253818_2
v2-0001-Fix-parser-failure-with-whole-row-join-alias-Vars.patchapplication/octet-stream; name=v2-0001-Fix-parser-failure-with-whole-row-join-alias-Vars.patchDownload+54-2
v2-0002-Fix-PlaceHolderVar-placement-for-join-alias-expre.patchapplication/octet-stream; name=v2-0002-Fix-PlaceHolderVar-placement-for-join-alias-expre.patchDownload+351-12