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
-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;
(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
(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
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)
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
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
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;
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
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;
(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
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 (
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;
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;
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;
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;
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;