| From: | g l <orangegrove(at)live(dot)com> |
|---|---|
| To: | "pgsql-hackers(at)postgresql(dot)org" <pgsql-hackers(at)postgresql(dot)org> |
| Subject: | [BUG] Planner bug causes incorrect query results in PG14 and PG15 |
| Date: | 2026-11-01 17:41:17 |
| Message-ID: | SY7P300MB0879A3CB0796C7DA8606E344DA6B2@SY7P300MB0879.AUSP300.PROD.OUTLOOK.COM |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
Hi hackers:
I encounter a bug that result in wrong query result. This bug is present throughout pg14 and pg15, even in 15.19, the latest minor version for pg 15, this bug remains. The query below can reproduce it:
opt=# select version();
version
---------------------------------------------------------------------------------------------------
PostgreSQL 15.19 on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 15.2.0-16ubuntu1) 15.2.0, 64-bit
(1 row)
we create 3 tables and populate 1 row to each table:
opt=# create table c (c1 int, c2 int, c3 int, c4 int);
CREATE TABLE
opt=# create table d (d1 int, d2 int, d3 int, d4 int);
CREATE TABLE
opt=# create table e (e1 int, e2 int, e3 int, e4 int);
CREATE TABLE
opt=# insert into c values (40,100,88,88);
INSERT 0 1
opt=# insert into d values (70,1,88,88);
INSERT 0 1
opt=# insert into e values (3,5,0,0);
INSERT 0 1
we then create 2 nonstrict function:
opt=# create function lax1 (a integer,b integer,c integer) returns integer
as $$
select case
when a+b+c is null then 1000
else 2
end
$$
LANGUAGE SQL;
CREATE FUNCTION
opt=# create function lax2 (a integer,b integer,c integer) returns integer
as $$
select case
when a+b+c is null then 1000
else 3
end
$$
LANGUAGE SQL;
CREATE FUNCTION
Now execute the query:
opt=# explain select * from c left join (d join lateral(select sum(e2) x,e3,e4 from e where e1<>c1 group by e3,e4)i on d1>x) on c2>d2+coalesce(e3)
where lax1(c4,d4,e4)=lax2(c3,d3,e3);
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------
Nested Loop Left Join (cost=46.93..22402728.38 rows=380277 width=48)
Filter: (CASE WHEN (((c.c4 + d.d4) + e.e4) IS NULL) THEN 1000 ELSE 2 END = CASE WHEN (((c.c3 + d.d3) + e.e3) IS NULL) THEN 1000 ELSE 3 END)
-> Seq Scan on c (cost=0.00..28.50 rows=1850 width=16)
-> Nested Loop (cost=46.93..12104.93 rows=206 width=32)
Join Filter: ((d.d1 > (sum(e.e2))) AND (c.c2 > (d.d2 + COALESCE(e.e3))) AND (CASE WHEN (((c.c4 + d.d4) + e.e4) IS NULL) THEN 1000 ELSE 2 END = CASE WHEN (((c.
c3 + d.d3) + e.e3) IS NULL) THEN 1000 ELSE 3 END))
-> Seq Scan on d (cost=0.00..28.50 rows=1850 width=16)
-> Materialize (cost=46.93..51.93 rows=200 width=16)
-> HashAggregate (cost=46.93..48.93 rows=200 width=16)
Group Key: e.e3, e.e4
-> Seq Scan on e (cost=0.00..33.12 rows=1841 width=12)
Filter: (e1 <> c.c1)
(11 rows)
opt=# select * from c left join (d join lateral(select sum(e2) x,e3,e4 from e where e1<>c1 group by e3,e4)i on d1>x) on c2>d2+coalesce(e3)
where lax1(c4,d4,e4)=lax2(c3,d3,e3);
c1 | c2 | c3 | c4 | d1 | d2 | d3 | d4 | x | e3 | e4
----+-----+----+----+----+----+----+----+---+----+----
40 | 100 | 88 | 88 | | | | | | |
(1 row)
switching to version 20devel, creating the same dataset and functions, then perform the same query, I get a different result:
opt=# select version();
version
-----------------------------------------------------------------------------------------------------
PostgreSQL 20devel on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 15.2.0-16ubuntu1) 15.2.0, 64-bit
(1 row)
opt=# explain select * from c left join (d join lateral(select sum(e2) x,e3,e4 from e where e1<>c1 group by e3,e4)i on d1>x) on c2>d2+coalesce(e3)
where lax1(c4,d4,e4)=lax2(c3,d3,e3);
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------
Nested Loop Left Join (cost=46.93..15545449.00 rows=380277 width=48)
Filter: (CASE WHEN (((c.c4 + d.d4) + e.e4) IS NULL) THEN 1000 ELSE 2 END = CASE WHEN (((c.c3 + d.d3) + e.e3) IS NULL) THEN 1000 ELSE 3 END)
-> Seq Scan on c (cost=0.00..28.50 rows=1850 width=16)
-> Nested Loop (cost=46.93..7477.93 rows=41111 width=32)
Join Filter: ((d.d1 > (sum(e.e2))) AND (c.c2 > (d.d2 + e.e3)))
-> Seq Scan on d (cost=0.00..28.50 rows=1850 width=16)
-> Materialize (cost=46.93..49.93 rows=200 width=16)
-> HashAggregate (cost=46.93..48.93 rows=200 width=16)
Group Key: e.e3, e.e4
-> Seq Scan on e (cost=0.00..33.12 rows=1841 width=12)
Filter: (e1 <> c.c1)
(11 rows)
opt=# select * from c left join (d join lateral(select sum(e2) x,e3,e4 from e where e1<>c1 group by e3,e4)i on d1>x) on c2>d2+coalesce(e3)
where lax1(c4,d4,e4)=lax2(c3,d3,e3);
c1 | c2 | c3 | c4 | d1 | d2 | d3 | d4 | x | e3 | e4
----+----+----+----+----+----+----+----+---+----+----
(0 rows)
The cause of error in pg14/15 is that the path for i is parameterized by table c. the path for joinrel (DI) is still parameterized by table c. When creating the nestloop join path for joinrel (DI), the clause
lax1(c4,d4,e4)=lax2(c3,d3,e3) is deduced from equivalence class in line 1463 of function get_joinrel_parampathinfo (in terms of pg15.19 source code). although this clause can't pass the join_clause_is_movable_into(rinfo, joinrel->relids, join_and_req) test, it is 'pushed' to the parameterized path of joinrel (DI) anyway, prematurely filtering out the row of (DI). But simply discard EC deduced clauses that can't pass the join_clause_is_movable_into test doesn't seem a good idea, because such clauses don't necessarily cause problem. for example, consider this query:
select * from c ,d left join lateral(select c4*2,* from e)i on d1=e1 where c2=coalesce(d2,e2,0);
this query push down EC deduced clause "c2=coalesce(d2,e2,0)" that can't pass the join_clause_is_movable_into test to the parameterized path of joinrel (DI) too, but there's no problem, becaused it doesn't give rise to premature filtering out of the nullable side of a outer join.
I would especially appreciate advice on how to solve this issue in pg14/15.
Thanks, Geng Lei
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Etsuro Fujita | 2026-11-01 17:44:52 | Re: Asynchronous MergeAppend |
| Previous Message | Hannu Krosing | 2026-11-01 17:27:35 | Re: [PATCH] Refactor pgbench to make future improvements easier |