mirror of
https://github.com/postgres/postgres.git
synced 2025-06-29 10:41:53 +03:00
Make the upper part of the planner work by generating and comparing Paths.
I've been saying we needed to do this for more than five years, and here it finally is. This patch removes the ever-growing tangle of spaghetti logic that grouping_planner() used to use to try to identify the best plan for post-scan/join query steps. Now, there is (nearly) independent consideration of each execution step, and entirely separate construction of Paths to represent each of the possible ways to do that step. We choose the best Path or set of Paths using the same add_path() logic that's been used inside query_planner() for years. In addition, this patch removes the old restriction that subquery_planner() could return only a single Plan. It now returns a RelOptInfo containing a set of Paths, just as query_planner() does, and the parent query level can use each of those Paths as the basis of a SubqueryScanPath at its level. This allows finding some optimizations that we missed before, wherein a subquery was capable of returning presorted data and thereby avoiding a sort in the parent level, making the overall cost cheaper even though delivering sorted output was not the cheapest plan for the subquery in isolation. (A couple of regression test outputs change in consequence of that. However, there is very little change in visible planner behavior overall, because the point of this patch is not to get immediate planning benefits but to create the infrastructure for future improvements.) There is a great deal left to do here. This patch unblocks a lot of planner work that was basically impractical in the old code structure, such as allowing FDWs to implement remote aggregation, or rewriting plan_set_operations() to allow consideration of multiple implementation orders for set operations. (The latter will likely require a full rewrite of plan_set_operations(); what I've done here is only to fix it to return Paths not Plans.) I have also left unfinished some localized refactoring in createplan.c and planner.c, because it was not necessary to get this patch to a working state. Thanks to Robert Haas, David Rowley, and Amit Kapila for review.
This commit is contained in:
@ -304,8 +304,8 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing)
|
||||
* in our query level. In this case apply
|
||||
* flatten_unplanned_rtes.
|
||||
*
|
||||
* If it was planned but the plan is dummy, we assume that it
|
||||
* has been omitted from our plan tree (see
|
||||
* If it was planned but the result rel is dummy, we assume
|
||||
* that it has been omitted from our plan tree (see
|
||||
* set_subquery_pathlist), and recurse to pull up its RTEs.
|
||||
*
|
||||
* Otherwise, it should be represented by a SubqueryScan node
|
||||
@ -313,17 +313,16 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing)
|
||||
* we process that plan node.
|
||||
*
|
||||
* However, if we're recursing, then we should pull up RTEs
|
||||
* whether the subplan is dummy or not, because we've found
|
||||
* whether the subquery is dummy or not, because we've found
|
||||
* that some upper query level is treating this one as dummy,
|
||||
* and so we won't scan this level's plan tree at all.
|
||||
*/
|
||||
if (rel->subplan == NULL)
|
||||
if (rel->subroot == NULL)
|
||||
flatten_unplanned_rtes(glob, rte);
|
||||
else if (recursing || is_dummy_plan(rel->subplan))
|
||||
{
|
||||
Assert(rel->subroot != NULL);
|
||||
else if (recursing ||
|
||||
IS_DUMMY_REL(fetch_upper_rel(rel->subroot,
|
||||
UPPERREL_FINAL, NULL)))
|
||||
add_rtes_to_flat_rtable(rel->subroot, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
rti++;
|
||||
@ -979,7 +978,6 @@ set_subqueryscan_references(PlannerInfo *root,
|
||||
|
||||
/* Need to look up the subquery's RelOptInfo, since we need its subroot */
|
||||
rel = find_base_rel(root, plan->scan.scanrelid);
|
||||
Assert(rel->subplan == plan->subplan);
|
||||
|
||||
/* Recursively process the subplan */
|
||||
plan->subplan = set_plan_references(rel->subroot, plan->subplan);
|
||||
@ -1386,6 +1384,7 @@ fix_param_node(PlannerInfo *root, Param *p)
|
||||
*
|
||||
* This consists of incrementing all Vars' varnos by rtoffset,
|
||||
* replacing PARAM_MULTIEXPR Params, expanding PlaceHolderVars,
|
||||
* replacing Aggref nodes that should be replaced by initplan output Params,
|
||||
* looking up operator opcode info for OpExpr and related nodes,
|
||||
* and adding OIDs from regclass Const nodes into root->glob->relationOids.
|
||||
*/
|
||||
@ -1399,7 +1398,8 @@ fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset)
|
||||
|
||||
if (rtoffset != 0 ||
|
||||
root->multiexpr_params != NIL ||
|
||||
root->glob->lastPHId != 0)
|
||||
root->glob->lastPHId != 0 ||
|
||||
root->minmax_aggs != NIL)
|
||||
{
|
||||
return fix_scan_expr_mutator(node, &context);
|
||||
}
|
||||
@ -1409,7 +1409,8 @@ fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset)
|
||||
* If rtoffset == 0, we don't need to change any Vars, and if there
|
||||
* are no MULTIEXPR subqueries then we don't need to replace
|
||||
* PARAM_MULTIEXPR Params, and if there are no placeholders anywhere
|
||||
* we won't need to remove them. Then it's OK to just scribble on the
|
||||
* we won't need to remove them, and if there are no minmax Aggrefs we
|
||||
* won't need to replace them. Then it's OK to just scribble on the
|
||||
* input node tree instead of copying (since the only change, filling
|
||||
* in any unset opfuncid fields, is harmless). This saves just enough
|
||||
* cycles to be noticeable on trivial queries.
|
||||
@ -1444,6 +1445,28 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
|
||||
}
|
||||
if (IsA(node, Param))
|
||||
return fix_param_node(context->root, (Param *) node);
|
||||
if (IsA(node, Aggref))
|
||||
{
|
||||
Aggref *aggref = (Aggref *) node;
|
||||
|
||||
/* See if the Aggref should be replaced by a Param */
|
||||
if (context->root->minmax_aggs != NIL &&
|
||||
list_length(aggref->args) == 1)
|
||||
{
|
||||
TargetEntry *curTarget = (TargetEntry *) linitial(aggref->args);
|
||||
ListCell *lc;
|
||||
|
||||
foreach(lc, context->root->minmax_aggs)
|
||||
{
|
||||
MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
|
||||
|
||||
if (mminfo->aggfnoid == aggref->aggfnoid &&
|
||||
equal(mminfo->target, curTarget->expr))
|
||||
return (Node *) copyObject(mminfo->param);
|
||||
}
|
||||
}
|
||||
/* If no match, just fall through to process it normally */
|
||||
}
|
||||
if (IsA(node, CurrentOfExpr))
|
||||
{
|
||||
CurrentOfExpr *cexpr = (CurrentOfExpr *) copyObject(node);
|
||||
@ -2091,8 +2114,9 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
|
||||
/*
|
||||
* fix_upper_expr
|
||||
* Modifies an expression tree so that all Var nodes reference outputs
|
||||
* of a subplan. Also performs opcode lookup, and adds regclass OIDs to
|
||||
* root->glob->relationOids.
|
||||
* of a subplan. Also looks for Aggref nodes that should be replaced
|
||||
* by initplan output Params. Also performs opcode lookup, and adds
|
||||
* regclass OIDs to root->glob->relationOids.
|
||||
*
|
||||
* This is used to fix up target and qual expressions of non-join upper-level
|
||||
* plan nodes, as well as index-only scan nodes.
|
||||
@ -2169,6 +2193,28 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
|
||||
}
|
||||
if (IsA(node, Param))
|
||||
return fix_param_node(context->root, (Param *) node);
|
||||
if (IsA(node, Aggref))
|
||||
{
|
||||
Aggref *aggref = (Aggref *) node;
|
||||
|
||||
/* See if the Aggref should be replaced by a Param */
|
||||
if (context->root->minmax_aggs != NIL &&
|
||||
list_length(aggref->args) == 1)
|
||||
{
|
||||
TargetEntry *curTarget = (TargetEntry *) linitial(aggref->args);
|
||||
ListCell *lc;
|
||||
|
||||
foreach(lc, context->root->minmax_aggs)
|
||||
{
|
||||
MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
|
||||
|
||||
if (mminfo->aggfnoid == aggref->aggfnoid &&
|
||||
equal(mminfo->target, curTarget->expr))
|
||||
return (Node *) copyObject(mminfo->param);
|
||||
}
|
||||
}
|
||||
/* If no match, just fall through to process it normally */
|
||||
}
|
||||
/* Try matching more complex expressions too, if tlist has any */
|
||||
if (context->subplan_itlist->has_non_vars)
|
||||
{
|
||||
|
Reference in New Issue
Block a user