1
0
mirror of https://github.com/postgres/postgres.git synced 2025-09-02 04:21:28 +03:00

Simplify COALESCE() with one surviving argument.

If, after removal of useless null-constant arguments, a CoalesceExpr
has exactly one remaining argument, we can just take that argument as
the result, without bothering to wrap a new CoalesceExpr around it.
This isn't likely to produce any great improvement in runtime per se,
but it can lead to better plans since the planner no longer has to
treat the expression as non-strict.

However, there were a few regression test cases that intentionally
wrote COALESCE(x) as a shorthand way of creating a non-strict
subexpression.  To avoid ruining the intent of those tests, write
COALESCE(x,x) instead.  (If anyone ever proposes de-duplicating
COALESCE arguments, we'll need another iteration of this arms race.
But it seems pretty unlikely that such an optimization would be
worthwhile.)

Author: Maksim Milyutin <maksim.milyutin@tantorlabs.ru>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/8e8573c3-1411-448d-877e-53258b7b2be0@tantorlabs.ru
This commit is contained in:
Tom Lane
2025-07-03 17:39:53 -04:00
parent fc896821c4
commit 931766aaec
5 changed files with 55 additions and 48 deletions

View File

@@ -3333,6 +3333,13 @@ eval_const_expressions_mutator(Node *node,
-1,
coalesceexpr->coalescecollid);
/*
* If there's exactly one surviving argument, we no longer
* need COALESCE at all: the result is that argument
*/
if (list_length(newargs) == 1)
return (Node *) linitial(newargs);
newcoalesce = makeNode(CoalesceExpr);
newcoalesce->coalescetype = coalesceexpr->coalescetype;
newcoalesce->coalescecollid = coalesceexpr->coalescecollid;

View File

@@ -5626,14 +5626,14 @@ select * from
(select 1 as id) as xx
left join
(tenk1 as a1 full join (select 1 as id) as yy on (a1.unique1 = yy.id))
on (xx.id = coalesce(yy.id));
QUERY PLAN
---------------------------------------
on (xx.id = coalesce(yy.id, yy.id));
QUERY PLAN
------------------------------------------
Nested Loop Left Join
-> Result
-> Hash Full Join
Hash Cond: (a1.unique1 = (1))
Filter: (1 = COALESCE((1)))
Filter: (1 = COALESCE((1), (1)))
-> Seq Scan on tenk1 a1
-> Hash
-> Result
@@ -5643,7 +5643,7 @@ select * from
(select 1 as id) as xx
left join
(tenk1 as a1 full join (select 1 as id) as yy on (a1.unique1 = yy.id))
on (xx.id = coalesce(yy.id));
on (xx.id = coalesce(yy.id, yy.id));
id | unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 | id
----+---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------+----
1 | 1 | 2838 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 2 | 3 | BAAAAA | EFEAAA | OOOOxx | 1
@@ -8411,20 +8411,20 @@ select * from int4_tbl i left join
explain (verbose, costs off)
select * from int4_tbl i left join
lateral (select coalesce(i) from int2_tbl j where i.f1 = j.f1) k on true;
QUERY PLAN
-------------------------------------
lateral (select coalesce(i, i) from int2_tbl j where i.f1 = j.f1) k on true;
QUERY PLAN
------------------------------------------
Nested Loop Left Join
Output: i.f1, (COALESCE(i.*))
Output: i.f1, (COALESCE(i.*, i.*))
-> Seq Scan on public.int4_tbl i
Output: i.f1, i.*
-> Seq Scan on public.int2_tbl j
Output: j.f1, COALESCE(i.*)
Output: j.f1, COALESCE(i.*, i.*)
Filter: (i.f1 = j.f1)
(7 rows)
select * from int4_tbl i left join
lateral (select coalesce(i) from int2_tbl j where i.f1 = j.f1) k on true;
lateral (select coalesce(i, i) from int2_tbl j where i.f1 = j.f1) k on true;
f1 | coalesce
-------------+----------
0 | (0)
@@ -9593,14 +9593,14 @@ CREATE STATISTICS group_tbl_stat (ndistinct) ON a, b FROM group_tbl;
ANALYZE group_tbl;
EXPLAIN (COSTS OFF)
SELECT 1 FROM group_tbl t1
LEFT JOIN (SELECT a c1, COALESCE(a) c2 FROM group_tbl t2) s ON TRUE
LEFT JOIN (SELECT a c1, COALESCE(a, a) c2 FROM group_tbl t2) s ON TRUE
GROUP BY s.c1, s.c2;
QUERY PLAN
--------------------------------------------
QUERY PLAN
------------------------------------------------
Group
Group Key: t2.a, (COALESCE(t2.a))
Group Key: t2.a, (COALESCE(t2.a, t2.a))
-> Sort
Sort Key: t2.a, (COALESCE(t2.a))
Sort Key: t2.a, (COALESCE(t2.a, t2.a))
-> Nested Loop Left Join
-> Seq Scan on group_tbl t1
-> Seq Scan on group_tbl t2

View File

@@ -2127,30 +2127,30 @@ explain (verbose, costs off)
select ss2.* from
int8_tbl t1 left join
(int8_tbl t2 left join
(select coalesce(q1) as x, * from int8_tbl t3) ss1 on t2.q1 = ss1.q2 inner join
(select coalesce(q1, q1) as x, * from int8_tbl t3) ss1 on t2.q1 = ss1.q2 inner join
lateral (select ss1.x as y, * from int8_tbl t4) ss2 on t2.q2 = ss2.q1)
on t1.q2 = ss2.q1
order by 1, 2, 3;
QUERY PLAN
----------------------------------------------------------------
QUERY PLAN
-----------------------------------------------------------------------
Sort
Output: (COALESCE(t3.q1)), t4.q1, t4.q2
Sort Key: (COALESCE(t3.q1)), t4.q1, t4.q2
Output: (COALESCE(t3.q1, t3.q1)), t4.q1, t4.q2
Sort Key: (COALESCE(t3.q1, t3.q1)), t4.q1, t4.q2
-> Hash Right Join
Output: (COALESCE(t3.q1)), t4.q1, t4.q2
Output: (COALESCE(t3.q1, t3.q1)), t4.q1, t4.q2
Hash Cond: (t4.q1 = t1.q2)
-> Hash Join
Output: (COALESCE(t3.q1)), t4.q1, t4.q2
Output: (COALESCE(t3.q1, t3.q1)), t4.q1, t4.q2
Hash Cond: (t2.q2 = t4.q1)
-> Hash Left Join
Output: t2.q2, (COALESCE(t3.q1))
Output: t2.q2, (COALESCE(t3.q1, t3.q1))
Hash Cond: (t2.q1 = t3.q2)
-> Seq Scan on public.int8_tbl t2
Output: t2.q1, t2.q2
-> Hash
Output: t3.q2, (COALESCE(t3.q1))
Output: t3.q2, (COALESCE(t3.q1, t3.q1))
-> Seq Scan on public.int8_tbl t3
Output: t3.q2, COALESCE(t3.q1)
Output: t3.q2, COALESCE(t3.q1, t3.q1)
-> Hash
Output: t4.q1, t4.q2
-> Seq Scan on public.int8_tbl t4
@@ -2164,7 +2164,7 @@ order by 1, 2, 3;
select ss2.* from
int8_tbl t1 left join
(int8_tbl t2 left join
(select coalesce(q1) as x, * from int8_tbl t3) ss1 on t2.q1 = ss1.q2 inner join
(select coalesce(q1, q1) as x, * from int8_tbl t3) ss1 on t2.q1 = ss1.q2 inner join
lateral (select ss1.x as y, * from int8_tbl t4) ss2 on t2.q2 = ss2.q1)
on t1.q2 = ss2.q1
order by 1, 2, 3;
@@ -2201,32 +2201,32 @@ explain (verbose, costs off)
select ss2.* from
int8_tbl t1 left join
(int8_tbl t2 left join
(select coalesce(q1) as x, * from int8_tbl t3) ss1 on t2.q1 = ss1.q2 left join
(select coalesce(q1, q1) as x, * from int8_tbl t3) ss1 on t2.q1 = ss1.q2 left join
lateral (select ss1.x as y, * from int8_tbl t4) ss2 on t2.q2 = ss2.q1)
on t1.q2 = ss2.q1
order by 1, 2, 3;
QUERY PLAN
----------------------------------------------------------------
QUERY PLAN
-----------------------------------------------------------------------
Sort
Output: ((COALESCE(t3.q1))), t4.q1, t4.q2
Sort Key: ((COALESCE(t3.q1))), t4.q1, t4.q2
Output: ((COALESCE(t3.q1, t3.q1))), t4.q1, t4.q2
Sort Key: ((COALESCE(t3.q1, t3.q1))), t4.q1, t4.q2
-> Hash Right Join
Output: ((COALESCE(t3.q1))), t4.q1, t4.q2
Output: ((COALESCE(t3.q1, t3.q1))), t4.q1, t4.q2
Hash Cond: (t4.q1 = t1.q2)
-> Nested Loop
Output: t4.q1, t4.q2, ((COALESCE(t3.q1)))
Output: t4.q1, t4.q2, ((COALESCE(t3.q1, t3.q1)))
Join Filter: (t2.q2 = t4.q1)
-> Hash Left Join
Output: t2.q2, (COALESCE(t3.q1))
Output: t2.q2, (COALESCE(t3.q1, t3.q1))
Hash Cond: (t2.q1 = t3.q2)
-> Seq Scan on public.int8_tbl t2
Output: t2.q1, t2.q2
-> Hash
Output: t3.q2, (COALESCE(t3.q1))
Output: t3.q2, (COALESCE(t3.q1, t3.q1))
-> Seq Scan on public.int8_tbl t3
Output: t3.q2, COALESCE(t3.q1)
Output: t3.q2, COALESCE(t3.q1, t3.q1)
-> Seq Scan on public.int8_tbl t4
Output: t4.q1, t4.q2, (COALESCE(t3.q1))
Output: t4.q1, t4.q2, (COALESCE(t3.q1, t3.q1))
-> Hash
Output: t1.q2
-> Seq Scan on public.int8_tbl t1
@@ -2236,7 +2236,7 @@ order by 1, 2, 3;
select ss2.* from
int8_tbl t1 left join
(int8_tbl t2 left join
(select coalesce(q1) as x, * from int8_tbl t3) ss1 on t2.q1 = ss1.q2 left join
(select coalesce(q1, q1) as x, * from int8_tbl t3) ss1 on t2.q1 = ss1.q2 left join
lateral (select ss1.x as y, * from int8_tbl t4) ss2 on t2.q2 = ss2.q1)
on t1.q2 = ss2.q1
order by 1, 2, 3;

View File

@@ -1977,13 +1977,13 @@ select * from
(select 1 as id) as xx
left join
(tenk1 as a1 full join (select 1 as id) as yy on (a1.unique1 = yy.id))
on (xx.id = coalesce(yy.id));
on (xx.id = coalesce(yy.id, yy.id));
select * from
(select 1 as id) as xx
left join
(tenk1 as a1 full join (select 1 as id) as yy on (a1.unique1 = yy.id))
on (xx.id = coalesce(yy.id));
on (xx.id = coalesce(yy.id, yy.id));
--
-- test ability to push constants through outer join clauses
@@ -3169,9 +3169,9 @@ select * from int4_tbl i left join
lateral (select * from int2_tbl j where i.f1 = j.f1) k on true;
explain (verbose, costs off)
select * from int4_tbl i left join
lateral (select coalesce(i) from int2_tbl j where i.f1 = j.f1) k on true;
lateral (select coalesce(i, i) from int2_tbl j where i.f1 = j.f1) k on true;
select * from int4_tbl i left join
lateral (select coalesce(i) from int2_tbl j where i.f1 = j.f1) k on true;
lateral (select coalesce(i, i) from int2_tbl j where i.f1 = j.f1) k on true;
explain (verbose, costs off)
select * from int4_tbl a,
lateral (
@@ -3637,7 +3637,7 @@ ANALYZE group_tbl;
EXPLAIN (COSTS OFF)
SELECT 1 FROM group_tbl t1
LEFT JOIN (SELECT a c1, COALESCE(a) c2 FROM group_tbl t2) s ON TRUE
LEFT JOIN (SELECT a c1, COALESCE(a, a) c2 FROM group_tbl t2) s ON TRUE
GROUP BY s.c1, s.c2;
DROP TABLE group_tbl;

View File

@@ -1041,7 +1041,7 @@ explain (verbose, costs off)
select ss2.* from
int8_tbl t1 left join
(int8_tbl t2 left join
(select coalesce(q1) as x, * from int8_tbl t3) ss1 on t2.q1 = ss1.q2 inner join
(select coalesce(q1, q1) as x, * from int8_tbl t3) ss1 on t2.q1 = ss1.q2 inner join
lateral (select ss1.x as y, * from int8_tbl t4) ss2 on t2.q2 = ss2.q1)
on t1.q2 = ss2.q1
order by 1, 2, 3;
@@ -1049,7 +1049,7 @@ order by 1, 2, 3;
select ss2.* from
int8_tbl t1 left join
(int8_tbl t2 left join
(select coalesce(q1) as x, * from int8_tbl t3) ss1 on t2.q1 = ss1.q2 inner join
(select coalesce(q1, q1) as x, * from int8_tbl t3) ss1 on t2.q1 = ss1.q2 inner join
lateral (select ss1.x as y, * from int8_tbl t4) ss2 on t2.q2 = ss2.q1)
on t1.q2 = ss2.q1
order by 1, 2, 3;
@@ -1059,7 +1059,7 @@ explain (verbose, costs off)
select ss2.* from
int8_tbl t1 left join
(int8_tbl t2 left join
(select coalesce(q1) as x, * from int8_tbl t3) ss1 on t2.q1 = ss1.q2 left join
(select coalesce(q1, q1) as x, * from int8_tbl t3) ss1 on t2.q1 = ss1.q2 left join
lateral (select ss1.x as y, * from int8_tbl t4) ss2 on t2.q2 = ss2.q1)
on t1.q2 = ss2.q1
order by 1, 2, 3;
@@ -1067,7 +1067,7 @@ order by 1, 2, 3;
select ss2.* from
int8_tbl t1 left join
(int8_tbl t2 left join
(select coalesce(q1) as x, * from int8_tbl t3) ss1 on t2.q1 = ss1.q2 left join
(select coalesce(q1, q1) as x, * from int8_tbl t3) ss1 on t2.q1 = ss1.q2 left join
lateral (select ss1.x as y, * from int8_tbl t4) ss2 on t2.q2 = ss2.q1)
on t1.q2 = ss2.q1
order by 1, 2, 3;