BUG #18933: PostgreSQL fails to handle JOIN operation involving Empty Table - Mailing list pgsql-bugs

From PG Bug reporting form
Subject BUG #18933: PostgreSQL fails to handle JOIN operation involving Empty Table
Date
Msg-id 18933-85d850c7526716f6@postgresql.org
Whole thread Raw
List pgsql-bugs
The following bug has been logged on the website:

Bug reference:      18933
Logged by:          jinhui lai
Email address:      jinhuilai@email.ncu.edu.cn
PostgreSQL version: 17.5
Operating system:   ubuntu 22.04
Description:

Hi, PostgreSQL Developers,
Hi, PostgreSQL Developers,
Please consider such queries:
SELECT * FROM empty_table CROSS  JOIN not_empty_table;
SELECT * FROM not_empty_table CROSS  JOIN empty_table;
SELECT * FROM empty_table, not_empty_table;
SELECT * FROM not_empty_table, empty_table;
...
Obviously, the above queries always returns an empty set. I think it should
return an empty set quickly. However, it waste much time. PostgreSQL fails
to optimize queries containing JOIN operations when involving Empty Table.
This leads to unnecessary memory consumption and query cancellation, even
though the result should be deterministically empty.
I think this is a common case in actual production scenarios. It's important
to clarify that users might not intentionally performJOIN operations on
empty tables. Rather, they may be unaware that a table is empty . For
example, when data has been deleted by another user or process. If
PostgreSQL can address this performance bug, it would significantly improve
query efficiency and save users valuable time in such cases.
Thank you for your valuable time, looking forward to your reply!
Best regard,
Jinhui Lai
You can reproduce the bug as follow steps:
psql -U postgres -c "CREATE DATABASE testdb;"
pgbench -U postgres -i -s 1000 testdb
psql -U postgres
\timing on
\c testdb;
testdb=# SELECT * FROM  pgbench_accounts AS a1 CROSS JOIN pgbench_accounts
AS a2  CROSS JOIN empty_table;
^CCancel request sent
ERROR:  canceling statement due to user request
Time: 13009160.244 ms (03:36:49.160)
testdb=# SELECT * FROM pgbench_accounts CROSS JOIN empty_table;
 aid | bid | abalance | filler | aid | bid | abalance | filler
-----+-----+----------+--------+-----+-----+----------+--------
(0 rows)
Time: 9341.104 ms (00:09.341)
testdb=# explain SELECT * FROM pgbench_accounts CROSS JOIN empty_table;
                                     QUERY PLAN
-------------------------------------------------------------------------------------
 Nested Loop  (cost=0.00..265139357.62 rows=21000000000 width=449)
   ->  Seq Scan on pgbench_accounts  (cost=0.00..2639345.00 rows=100000000
width=97)
   ->  Materialize  (cost=0.00..13.15 rows=210 width=352)
         ->  Seq Scan on empty_table  (cost=0.00..12.10 rows=210 width=352)
 JIT:
   Functions: 3
   Options: Inlining true, Optimization true, Expressions true, Deforming
true
(7 rows)
Time: 1.241 ms
testdb=# SELECT * FROM pgbench_accounts, empty_table;
 aid | bid | abalance | filler | aid | bid | abalance | filler
-----+-----+----------+--------+-----+-----+----------+--------
(0 rows)
Time: 9499.105 ms (00:09.499)
testdb=# explain SELECT * FROM pgbench_accounts, empty_table;
                                     QUERY PLAN
-------------------------------------------------------------------------------------
 Nested Loop  (cost=0.00..265139357.62 rows=21000000000 width=449)
   ->  Seq Scan on pgbench_accounts  (cost=0.00..2639345.00 rows=100000000
width=97)
   ->  Materialize  (cost=0.00..13.15 rows=210 width=352)
         ->  Seq Scan on empty_table  (cost=0.00..12.10 rows=210 width=352)
 JIT:
   Functions: 3
   Options: Inlining true, Optimization true, Expressions true, Deforming
true
(7 rows)
Time: 1.147 ms
testdb=# SELECT * FROM empty_table CROSS JOIN pgbench_accounts;
 aid | bid | abalance | filler | aid | bid | abalance | filler
-----+-----+----------+--------+-----+-----+----------+--------
(0 rows)
Time: 9182.863 ms (00:09.183)
testdb=# explain SELECT * FROM empty_table CROSS JOIN pgbench_accounts;
                                     QUERY PLAN
-------------------------------------------------------------------------------------
 Nested Loop  (cost=0.00..265139357.62 rows=21000000000 width=449)
   ->  Seq Scan on pgbench_accounts  (cost=0.00..2639345.00 rows=100000000
width=97)
   ->  Materialize  (cost=0.00..13.15 rows=210 width=352)
         ->  Seq Scan on empty_table  (cost=0.00..12.10 rows=210 width=352)
 JIT:
   Functions: 3
   Options: Inlining true, Optimization true, Expressions true, Deforming
true
(7 rows)
Time: 1.118 ms
testdb=# SELECT * FROM  pgbench_accounts AS a1 INNER JOIN pgbench_accounts
AS a2 ON a1.aid =a2.aid CROSS JOIN empty_table;
 aid | bid | abalance | filler | aid | bid | abalance | filler | aid | bid |
abalance | filler
-----+-----+----------+--------+-----+-----+----------+--------+-----+-----+----------+--------
(0 rows)
Time: 34645.057 ms (00:34.645)


pgsql-bugs by date:

Previous
From: 萧鸿骏
Date:
Subject: Re: Re: BUG #18927: Poor query plan - HashAggregate is more than twice as fast but at a higher cost
Next
From: "David G. Johnston"
Date:
Subject: Re: BUG #18932: PostgreSQL fails to handle EXCEPT operation when the left branch is an Empty Table