diff options
Diffstat (limited to 'src/backend/optimizer/util/pathnode.c')
-rw-r--r-- | src/backend/optimizer/util/pathnode.c | 56 |
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); } |