1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-29 10:41:53 +03:00

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
This commit is contained in:
Tom Lane
2019-03-25 15:42:35 -04:00
parent f21668f328
commit 8edd0e7946
12 changed files with 598 additions and 496 deletions

View File

@ -94,12 +94,19 @@ static Plan *set_subqueryscan_references(PlannerInfo *root,
SubqueryScan *plan,
int rtoffset);
static bool trivial_subqueryscan(SubqueryScan *plan);
static Plan *clean_up_removed_plan_level(Plan *parent, Plan *child);
static void set_foreignscan_references(PlannerInfo *root,
ForeignScan *fscan,
int rtoffset);
static void set_customscan_references(PlannerInfo *root,
CustomScan *cscan,
int rtoffset);
static Plan *set_append_references(PlannerInfo *root,
Append *aplan,
int rtoffset);
static Plan *set_mergeappend_references(PlannerInfo *root,
MergeAppend *mplan,
int rtoffset);
static Node *fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset);
static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context);
static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context);
@ -181,19 +188,22 @@ static List *set_returning_clause_references(PlannerInfo *root,
* 8. We assign every plan node in the tree a unique ID.
*
* We also perform one final optimization step, which is to delete
* SubqueryScan plan nodes that aren't doing anything useful (ie, have
* no qual and a no-op targetlist). The reason for doing this last is that
* SubqueryScan, Append, and MergeAppend plan nodes that aren't doing
* anything useful. The reason for doing this last is that
* it can't readily be done before set_plan_references, because it would
* break set_upper_references: the Vars in the subquery's top tlist
* wouldn't match up with the Vars in the outer plan tree. The SubqueryScan
* break set_upper_references: the Vars in the child plan's top tlist
* wouldn't match up with the Vars in the outer plan tree. A SubqueryScan
* serves a necessary function as a buffer between outer query and subquery
* variable numbering ... but after we've flattened the rangetable this is
* no longer a problem, since then there's only one rtindex namespace.
* Likewise, Append and MergeAppend buffer between the parent and child vars
* of an appendrel, but we don't need to worry about that once we've done
* set_plan_references.
*
* set_plan_references recursively traverses the whole plan tree.
*
* The return value is normally the same Plan node passed in, but can be
* different when the passed-in Plan is a SubqueryScan we decide isn't needed.
* different when the passed-in Plan is a node we decide isn't needed.
*
* The flattened rangetable entries are appended to root->glob->finalrtable.
* Also, rowmarks entries are appended to root->glob->finalrowmarks, and the
@ -897,71 +907,15 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
}
break;
case T_Append:
{
Append *splan = (Append *) plan;
/*
* Append, like Sort et al, doesn't actually evaluate its
* targetlist or check quals.
*/
set_dummy_tlist_references(plan, rtoffset);
Assert(splan->plan.qual == NIL);
foreach(l, splan->appendplans)
{
lfirst(l) = set_plan_refs(root,
(Plan *) lfirst(l),
rtoffset);
}
if (splan->part_prune_info)
{
foreach(l, splan->part_prune_info->prune_infos)
{
List *prune_infos = lfirst(l);
ListCell *l2;
foreach(l2, prune_infos)
{
PartitionedRelPruneInfo *pinfo = lfirst(l2);
pinfo->rtindex += rtoffset;
}
}
}
}
break;
/* Needs special treatment, see comments below */
return set_append_references(root,
(Append *) plan,
rtoffset);
case T_MergeAppend:
{
MergeAppend *splan = (MergeAppend *) plan;
/*
* MergeAppend, like Sort et al, doesn't actually evaluate its
* targetlist or check quals.
*/
set_dummy_tlist_references(plan, rtoffset);
Assert(splan->plan.qual == NIL);
foreach(l, splan->mergeplans)
{
lfirst(l) = set_plan_refs(root,
(Plan *) lfirst(l),
/* Needs special treatment, see comments below */
return set_mergeappend_references(root,
(MergeAppend *) plan,
rtoffset);
}
if (splan->part_prune_info)
{
foreach(l, splan->part_prune_info->prune_infos)
{
List *prune_infos = lfirst(l);
ListCell *l2;
foreach(l2, prune_infos)
{
PartitionedRelPruneInfo *pinfo = lfirst(l2);
pinfo->rtindex += rtoffset;
}
}
}
}
break;
case T_RecursiveUnion:
/* This doesn't evaluate targetlist or check quals either */
set_dummy_tlist_references(plan, rtoffset);
@ -1086,30 +1040,7 @@ set_subqueryscan_references(PlannerInfo *root,
/*
* We can omit the SubqueryScan node and just pull up the subplan.
*/
ListCell *lp,
*lc;
result = plan->subplan;
/* We have to be sure we don't lose any initplans */
result->initPlan = list_concat(plan->scan.plan.initPlan,
result->initPlan);
/*
* We also have to transfer the SubqueryScan's result-column names
* into the subplan, else columns sent to client will be improperly
* labeled if this is the topmost plan level. Copy the "source
* column" information too.
*/
forboth(lp, plan->scan.plan.targetlist, lc, result->targetlist)
{
TargetEntry *ptle = (TargetEntry *) lfirst(lp);
TargetEntry *ctle = (TargetEntry *) lfirst(lc);
ctle->resname = ptle->resname;
ctle->resorigtbl = ptle->resorigtbl;
ctle->resorigcol = ptle->resorigcol;
}
result = clean_up_removed_plan_level((Plan *) plan, plan->subplan);
}
else
{
@ -1190,6 +1121,30 @@ trivial_subqueryscan(SubqueryScan *plan)
return true;
}
/*
* clean_up_removed_plan_level
* Do necessary cleanup when we strip out a SubqueryScan, Append, etc
*
* We are dropping the "parent" plan in favor of returning just its "child".
* A few small tweaks are needed.
*/
static Plan *
clean_up_removed_plan_level(Plan *parent, Plan *child)
{
/* We have to be sure we don't lose any initplans */
child->initPlan = list_concat(parent->initPlan,
child->initPlan);
/*
* We also have to transfer the parent's column labeling info into the
* child, else columns sent to client will be improperly labeled if this
* is the topmost plan level. resjunk and so on may be important too.
*/
apply_tlist_labeling(child->targetlist, parent->targetlist);
return child;
}
/*
* set_foreignscan_references
* Do set_plan_references processing on a ForeignScan
@ -1340,6 +1295,131 @@ set_customscan_references(PlannerInfo *root,
}
}
/*
* set_append_references
* Do set_plan_references processing on an Append
*
* We try to strip out the Append entirely; if we can't, we have
* to do the normal processing on it.
*/
static Plan *
set_append_references(PlannerInfo *root,
Append *aplan,
int rtoffset)
{
ListCell *l;
/*
* Append, like Sort et al, doesn't actually evaluate its targetlist or
* check quals. If it's got exactly one child plan, then it's not doing
* anything useful at all, and we can strip it out.
*/
Assert(aplan->plan.qual == NIL);
/* First, we gotta recurse on the children */
foreach(l, aplan->appendplans)
{
lfirst(l) = set_plan_refs(root, (Plan *) lfirst(l), rtoffset);
}
/* Now, if there's just one, forget the Append and return that child */
if (list_length(aplan->appendplans) == 1)
return clean_up_removed_plan_level((Plan *) aplan,
(Plan *) linitial(aplan->appendplans));
/*
* Otherwise, clean up the Append as needed. It's okay to do this after
* recursing to the children, because set_dummy_tlist_references doesn't
* look at those.
*/
set_dummy_tlist_references((Plan *) aplan, rtoffset);
if (aplan->part_prune_info)
{
foreach(l, aplan->part_prune_info->prune_infos)
{
List *prune_infos = lfirst(l);
ListCell *l2;
foreach(l2, prune_infos)
{
PartitionedRelPruneInfo *pinfo = lfirst(l2);
pinfo->rtindex += rtoffset;
}
}
}
/* We don't need to recurse to lefttree or righttree ... */
Assert(aplan->plan.lefttree == NULL);
Assert(aplan->plan.righttree == NULL);
return (Plan *) aplan;
}
/*
* set_mergeappend_references
* Do set_plan_references processing on a MergeAppend
*
* We try to strip out the MergeAppend entirely; if we can't, we have
* to do the normal processing on it.
*/
static Plan *
set_mergeappend_references(PlannerInfo *root,
MergeAppend *mplan,
int rtoffset)
{
ListCell *l;
/*
* MergeAppend, like Sort et al, doesn't actually evaluate its targetlist
* or check quals. If it's got exactly one child plan, then it's not
* doing anything useful at all, and we can strip it out.
*/
Assert(mplan->plan.qual == NIL);
/* First, we gotta recurse on the children */
foreach(l, mplan->mergeplans)
{
lfirst(l) = set_plan_refs(root, (Plan *) lfirst(l), rtoffset);
}
/* Now, if there's just one, forget the MergeAppend and return that child */
if (list_length(mplan->mergeplans) == 1)
return clean_up_removed_plan_level((Plan *) mplan,
(Plan *) linitial(mplan->mergeplans));
/*
* Otherwise, clean up the MergeAppend as needed. It's okay to do this
* after recursing to the children, because set_dummy_tlist_references
* doesn't look at those.
*/
set_dummy_tlist_references((Plan *) mplan, rtoffset);
if (mplan->part_prune_info)
{
foreach(l, mplan->part_prune_info->prune_infos)
{
List *prune_infos = lfirst(l);
ListCell *l2;
foreach(l2, prune_infos)
{
PartitionedRelPruneInfo *pinfo = lfirst(l2);
pinfo->rtindex += rtoffset;
}
}
}
/* We don't need to recurse to lefttree or righttree ... */
Assert(mplan->plan.lefttree == NULL);
Assert(mplan->plan.righttree == NULL);
return (Plan *) mplan;
}
/*
* copyVar
* Copy a Var node.