summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/util/pathnode.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/util/pathnode.c')
-rw-r--r--src/backend/optimizer/util/pathnode.c56
1 files changed, 44 insertions, 12 deletions
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 483c4f41373..dd64b460865 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -1326,19 +1326,28 @@ create_append_path(PlannerInfo *root,
Assert(!parallel_aware || pathnode->path.parallel_safe);
/*
- * If there's exactly one child path, the Append is a no-op and will be
- * discarded later (in setrefs.c); therefore, we can inherit the child's
- * size and cost, as well as its pathkeys if any (overriding whatever the
- * caller might've said). Otherwise, we must do the normal costsize
+ * If there's exactly one child path then the output of the Append is
+ * necessarily ordered the same as the child's, so we can inherit the
+ * child's pathkeys if any, overriding whatever the caller might've said.
+ * Furthermore, if the child's parallel awareness matches the Append's,
+ * then the Append is a no-op and will be discarded later (in setrefs.c).
+ * Then we can inherit the child's size and cost too, effectively charging
+ * zero for the Append. Otherwise, we must do the normal costsize
* calculation.
*/
if (list_length(pathnode->subpaths) == 1)
{
Path *child = (Path *) linitial(pathnode->subpaths);
- pathnode->path.rows = child->rows;
- pathnode->path.startup_cost = child->startup_cost;
- pathnode->path.total_cost = child->total_cost;
+ if (child->parallel_aware == parallel_aware)
+ {
+ pathnode->path.rows = child->rows;
+ pathnode->path.startup_cost = child->startup_cost;
+ pathnode->path.total_cost = child->total_cost;
+ }
+ else
+ cost_append(pathnode, root);
+ /* Must do this last, else cost_append complains */
pathnode->path.pathkeys = child->pathkeys;
}
else
@@ -1476,10 +1485,13 @@ create_merge_append_path(PlannerInfo *root,
/*
* Now we can compute total costs of the MergeAppend. If there's exactly
- * one child path, the MergeAppend is a no-op and will be discarded later
- * (in setrefs.c); otherwise we do the normal cost calculation.
+ * one child path and its parallel awareness matches that of the
+ * MergeAppend, then the MergeAppend is a no-op and will be discarded
+ * later (in setrefs.c); otherwise we do the normal cost calculation.
*/
- if (list_length(subpaths) == 1)
+ if (list_length(subpaths) == 1 &&
+ ((Path *) linitial(subpaths))->parallel_aware ==
+ pathnode->path.parallel_aware)
{
pathnode->path.startup_cost = input_startup_cost;
pathnode->path.total_cost = input_total_cost;
@@ -1986,9 +1998,15 @@ create_gather_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
* create_subqueryscan_path
* Creates a path corresponding to a scan of a subquery,
* returning the pathnode.
+ *
+ * Caller must pass trivial_pathtarget = true if it believes rel->reltarget to
+ * be trivial, ie just a fetch of all the subquery output columns in order.
+ * While we could determine that here, the caller can usually do it more
+ * efficiently (or at least amortize it over multiple calls).
*/
SubqueryScanPath *
create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
+ bool trivial_pathtarget,
List *pathkeys, Relids required_outer)
{
SubqueryScanPath *pathnode = makeNode(SubqueryScanPath);
@@ -2005,7 +2023,8 @@ create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
pathnode->path.pathkeys = pathkeys;
pathnode->subpath = subpath;
- cost_subqueryscan(pathnode, root, rel, pathnode->path.param_info);
+ cost_subqueryscan(pathnode, root, rel, pathnode->path.param_info,
+ trivial_pathtarget);
return pathnode;
}
@@ -3901,10 +3920,23 @@ reparameterize_path(PlannerInfo *root, Path *path,
case T_SubqueryScan:
{
SubqueryScanPath *spath = (SubqueryScanPath *) path;
+ Path *subpath = spath->subpath;
+ bool trivial_pathtarget;
+
+ /*
+ * If existing node has zero extra cost, we must have decided
+ * its target is trivial. (The converse is not true, because
+ * it might have a trivial target but quals to enforce; but in
+ * that case the new node will too, so it doesn't matter
+ * whether we get the right answer here.)
+ */
+ trivial_pathtarget =
+ (subpath->total_cost == spath->path.total_cost);
return (Path *) create_subqueryscan_path(root,
rel,
- spath->subpath,
+ subpath,
+ trivial_pathtarget,
spath->path.pathkeys,
required_outer);
}