Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 8edd0e7

Browse files
committed
Suppress Append and MergeAppend plan nodes that have a single child.
If there's only one child relation, the Append or MergeAppend isn't doing anything useful, and can be elided. It does have a purpose during planning though, which is to serve as a buffer between parent and child Var numbering. Therefore we keep it all the way through to setrefs.c, and get rid of it only after fixing references in the plan level(s) above it. This works largely the same as setrefs.c's ancient hack to get rid of no-op SubqueryScan nodes, and can even share some code with that. Note the change to make setrefs.c use apply_tlist_labeling rather than ad-hoc code. This has the effect of propagating the child's resjunk and ressortgroupref labels, which formerly weren't propagated when removing a SubqueryScan. Doing that is demonstrably necessary for the [Merge]Append cases, and seems harmless for SubqueryScan, if only because trivial_subqueryscan is afraid to collapse cases where the resjunk marking differs. (I suspect that restriction could now be removed, though it's unclear that it'd make any new matches possible, since the outer query can't have references to a child resjunk column.) David Rowley, reviewed by Alvaro Herrera and Tomas Vondra Discussion: https://postgr.es/m/CAKJS1f_7u8ATyJ1JGTMHFoKDvZdeF-iEBhs+sM_SXowOr9cArg@mail.gmail.com
1 parent f21668f commit 8edd0e7

File tree

12 files changed

+598
-496
lines changed

12 files changed

+598
-496
lines changed

contrib/postgres_fdw/expected/postgres_fdw.out

+9-10
Original file line numberDiff line numberDiff line change
@@ -8427,17 +8427,16 @@ SELECT t1.a,t2.b,t3.c FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.a = t2.b) INNER J
84278427
400 | 400 | 0008
84288428
(4 rows)
84298429

8430-
-- left outer join + nullable clasue
8431-
EXPLAIN (COSTS OFF)
8430+
-- left outer join + nullable clause
8431+
EXPLAIN (VERBOSE, COSTS OFF)
84328432
SELECT t1.a,t2.b,t2.c FROM fprt1 t1 LEFT JOIN (SELECT * FROM fprt2 WHERE a < 10) t2 ON (t1.a = t2.b and t1.b = t2.a) WHERE t1.a < 10 ORDER BY 1,2,3;
8433-
QUERY PLAN
8434-
-----------------------------------------------------------------------------------
8435-
Sort
8436-
Sort Key: t1.a, ftprt2_p1.b, ftprt2_p1.c
8437-
-> Append
8438-
-> Foreign Scan
8439-
Relations: (public.ftprt1_p1 t1) LEFT JOIN (public.ftprt2_p1 fprt2)
8440-
(5 rows)
8433+
QUERY PLAN
8434+
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
8435+
Foreign Scan
8436+
Output: t1.a, ftprt2_p1.b, ftprt2_p1.c
8437+
Relations: (public.ftprt1_p1 t1) LEFT JOIN (public.ftprt2_p1 fprt2)
8438+
Remote SQL: SELECT r6.a, r9.b, r9.c FROM (public.fprt1_p1 r6 LEFT JOIN public.fprt2_p1 r9 ON (((r6.a = r9.b)) AND ((r6.b = r9.a)) AND ((r9.a < 10)))) WHERE ((r6.a < 10)) ORDER BY r6.a ASC NULLS LAST, r9.b ASC NULLS LAST, r9.c ASC NULLS LAST
8439+
(4 rows)
84418440

84428441
SELECT t1.a,t2.b,t2.c FROM fprt1 t1 LEFT JOIN (SELECT * FROM fprt2 WHERE a < 10) t2 ON (t1.a = t2.b and t1.b = t2.a) WHERE t1.a < 10 ORDER BY 1,2,3;
84438442
a | b | c

contrib/postgres_fdw/sql/postgres_fdw.sql

+2-2
Original file line numberDiff line numberDiff line change
@@ -2309,8 +2309,8 @@ EXPLAIN (COSTS OFF)
23092309
SELECT t1.a,t2.b,t3.c FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.a = t2.b) INNER JOIN fprt1 t3 ON (t2.b = t3.a) WHERE t1.a % 25 =0 ORDER BY 1,2,3;
23102310
SELECT t1.a,t2.b,t3.c FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.a = t2.b) INNER JOIN fprt1 t3 ON (t2.b = t3.a) WHERE t1.a % 25 =0 ORDER BY 1,2,3;
23112311

2312-
-- left outer join + nullable clasue
2313-
EXPLAIN (COSTS OFF)
2312+
-- left outer join + nullable clause
2313+
EXPLAIN (VERBOSE, COSTS OFF)
23142314
SELECT t1.a,t2.b,t2.c FROM fprt1 t1 LEFT JOIN (SELECT * FROM fprt2 WHERE a < 10) t2 ON (t1.a = t2.b and t1.b = t2.a) WHERE t1.a < 10 ORDER BY 1,2,3;
23152315
SELECT t1.a,t2.b,t2.c FROM fprt1 t1 LEFT JOIN (SELECT * FROM fprt2 WHERE a < 10) t2 ON (t1.a = t2.b and t1.b = t2.a) WHERE t1.a < 10 ORDER BY 1,2,3;
23162316

src/backend/executor/execAmi.c

+30
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,36 @@ ExecSupportsMarkRestore(Path *pathnode)
447447
return false; /* childless Result */
448448
}
449449

450+
case T_Append:
451+
{
452+
AppendPath *appendPath = castNode(AppendPath, pathnode);
453+
454+
/*
455+
* If there's exactly one child, then there will be no Append
456+
* in the final plan, so we can handle mark/restore if the
457+
* child plan node can.
458+
*/
459+
if (list_length(appendPath->subpaths) == 1)
460+
return ExecSupportsMarkRestore((Path *) linitial(appendPath->subpaths));
461+
/* Otherwise, Append can't handle it */
462+
return false;
463+
}
464+
465+
case T_MergeAppend:
466+
{
467+
MergeAppendPath *mapath = castNode(MergeAppendPath, pathnode);
468+
469+
/*
470+
* Like the Append case above, single-subpath MergeAppends
471+
* won't be in the final plan, so just return the child's
472+
* mark/restore ability.
473+
*/
474+
if (list_length(mapath->subpaths) == 1)
475+
return ExecSupportsMarkRestore((Path *) linitial(mapath->subpaths));
476+
/* Otherwise, MergeAppend can't handle it */
477+
return false;
478+
}
479+
450480
default:
451481
break;
452482
}

src/backend/optimizer/path/allpaths.c

+34-2
Original file line numberDiff line numberDiff line change
@@ -1044,8 +1044,8 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
10441044
/*
10451045
* We have to copy the parent's targetlist and quals to the child,
10461046
* with appropriate substitution of variables. If any constant false
1047-
* or NULL clauses turn up, we can disregard the child right away.
1048-
* If not, we can apply constraint exclusion with just the
1047+
* or NULL clauses turn up, we can disregard the child right away. If
1048+
* not, we can apply constraint exclusion with just the
10491049
* baserestrictinfo quals.
10501050
*/
10511051
if (!apply_child_basequals(root, rel, childrel, childRTE, appinfo))
@@ -1708,6 +1708,38 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
17081708
required_outer, 0, false,
17091709
partitioned_rels, -1));
17101710
}
1711+
1712+
/*
1713+
* When there is only a single child relation, the Append path can inherit
1714+
* any ordering available for the child rel's path, so that it's useful to
1715+
* consider ordered partial paths. Above we only considered the cheapest
1716+
* partial path for each child, but let's also make paths using any
1717+
* partial paths that have pathkeys.
1718+
*/
1719+
if (list_length(live_childrels) == 1)
1720+
{
1721+
RelOptInfo *childrel = (RelOptInfo *) linitial(live_childrels);
1722+
1723+
foreach(l, childrel->partial_pathlist)
1724+
{
1725+
Path *path = (Path *) lfirst(l);
1726+
AppendPath *appendpath;
1727+
1728+
/*
1729+
* Skip paths with no pathkeys. Also skip the cheapest partial
1730+
* path, since we already used that above.
1731+
*/
1732+
if (path->pathkeys == NIL ||
1733+
path == linitial(childrel->partial_pathlist))
1734+
continue;
1735+
1736+
appendpath = create_append_path(root, rel, NIL, list_make1(path),
1737+
NULL, path->parallel_workers,
1738+
true,
1739+
partitioned_rels, partial_rows);
1740+
add_partial_path(rel, (Path *) appendpath);
1741+
}
1742+
}
17111743
}
17121744

17131745
/*

src/backend/optimizer/plan/createplan.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -1134,10 +1134,10 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
11341134
}
11351135

11361136
/*
1137-
* XXX ideally, if there's just one child, we'd not bother to generate an
1138-
* Append node but just return the single child. At the moment this does
1139-
* not work because the varno of the child scan plan won't match the
1140-
* parent-rel Vars it'll be asked to emit.
1137+
* And build the Append plan. Note that if there's just one child, the
1138+
* Append is pretty useless; but we wait till setrefs.c to get rid of it.
1139+
* Doing so here doesn't work because the varno of the child scan plan
1140+
* won't match the parent-rel Vars it'll be asked to emit.
11411141
*/
11421142

11431143
plan = make_append(subplans, best_path->first_partial_path,

0 commit comments

Comments
 (0)