mirror of
https://github.com/postgres/postgres.git
synced 2025-07-07 00:36:50 +03:00
Use appendrel planning logic for top-level UNION ALL structures.
Formerly, we could convert a UNION ALL structure inside a subquery-in-FROM into an appendrel, as a side effect of pulling up the subquery into its parent; but top-level UNION ALL always caused use of plan_set_operations(). That didn't matter too much because you got an Append-based plan either way. However, now that the appendrel code can do things with MergeAppend, it's worthwhile to hack up the top-level case so it also uses appendrels. This is a bit of a stopgap; but going much further than this will require a major rewrite of the planner's set-operations support, which I'm not prepared to undertake now. For the moment let's grab the low-hanging fruit.
This commit is contained in:
@ -341,12 +341,21 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
|
|||||||
inline_set_returning_functions(root);
|
inline_set_returning_functions(root);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check to see if any subqueries in the rangetable can be merged into
|
* Check to see if any subqueries in the jointree can be merged into
|
||||||
* this query.
|
* this query.
|
||||||
*/
|
*/
|
||||||
parse->jointree = (FromExpr *)
|
parse->jointree = (FromExpr *)
|
||||||
pull_up_subqueries(root, (Node *) parse->jointree, NULL, NULL);
|
pull_up_subqueries(root, (Node *) parse->jointree, NULL, NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If this is a simple UNION ALL query, flatten it into an appendrel.
|
||||||
|
* We do this now because it requires applying pull_up_subqueries to the
|
||||||
|
* leaf queries of the UNION ALL, which weren't touched above because they
|
||||||
|
* weren't referenced by the jointree (they will be after we do this).
|
||||||
|
*/
|
||||||
|
if (parse->setOperations)
|
||||||
|
flatten_simple_union_all(root);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Detect whether any rangetable entries are RTE_JOIN kind; if not, we can
|
* Detect whether any rangetable entries are RTE_JOIN kind; if not, we can
|
||||||
* avoid the expense of doing flatten_join_alias_vars(). Also check for
|
* avoid the expense of doing flatten_join_alias_vars(). Also check for
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
* pull_up_sublinks
|
* pull_up_sublinks
|
||||||
* inline_set_returning_functions
|
* inline_set_returning_functions
|
||||||
* pull_up_subqueries
|
* pull_up_subqueries
|
||||||
|
* flatten_simple_union_all
|
||||||
* do expression preprocessing (including flattening JOIN alias vars)
|
* do expression preprocessing (including flattening JOIN alias vars)
|
||||||
* reduce_outer_joins
|
* reduce_outer_joins
|
||||||
*
|
*
|
||||||
@ -868,11 +869,6 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
|
|||||||
int rtoffset;
|
int rtoffset;
|
||||||
List *rtable;
|
List *rtable;
|
||||||
|
|
||||||
/*
|
|
||||||
* Append the subquery rtable entries to upper query.
|
|
||||||
*/
|
|
||||||
rtoffset = list_length(root->parse->rtable);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Append child RTEs to parent rtable.
|
* Append child RTEs to parent rtable.
|
||||||
*
|
*
|
||||||
@ -881,6 +877,7 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
|
|||||||
* because any such vars must refer to stuff above the level of the query
|
* because any such vars must refer to stuff above the level of the query
|
||||||
* we are pulling into.
|
* we are pulling into.
|
||||||
*/
|
*/
|
||||||
|
rtoffset = list_length(root->parse->rtable);
|
||||||
rtable = copyObject(subquery->rtable);
|
rtable = copyObject(subquery->rtable);
|
||||||
IncrementVarSublevelsUp_rtable(rtable, -1, 1);
|
IncrementVarSublevelsUp_rtable(rtable, -1, 1);
|
||||||
root->parse->rtable = list_concat(root->parse->rtable, rtable);
|
root->parse->rtable = list_concat(root->parse->rtable, rtable);
|
||||||
@ -888,7 +885,7 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
|
|||||||
/*
|
/*
|
||||||
* Recursively scan the subquery's setOperations tree and add
|
* Recursively scan the subquery's setOperations tree and add
|
||||||
* AppendRelInfo nodes for leaf subqueries to the parent's
|
* AppendRelInfo nodes for leaf subqueries to the parent's
|
||||||
* append_rel_list.
|
* append_rel_list. Also apply pull_up_subqueries to the leaf subqueries.
|
||||||
*/
|
*/
|
||||||
Assert(subquery->setOperations);
|
Assert(subquery->setOperations);
|
||||||
pull_up_union_leaf_queries(subquery->setOperations, root, varno, subquery,
|
pull_up_union_leaf_queries(subquery->setOperations, root, varno, subquery,
|
||||||
@ -905,14 +902,20 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
|
|||||||
/*
|
/*
|
||||||
* pull_up_union_leaf_queries -- recursive guts of pull_up_simple_union_all
|
* pull_up_union_leaf_queries -- recursive guts of pull_up_simple_union_all
|
||||||
*
|
*
|
||||||
* Note that setOpQuery is the Query containing the setOp node, whose rtable
|
* Build an AppendRelInfo for each leaf query in the setop tree, and then
|
||||||
* is where to look up the RTE if setOp is a RangeTblRef. This is *not* the
|
* apply pull_up_subqueries to the leaf query.
|
||||||
* same as root->parse, which is the top-level Query we are pulling up into.
|
*
|
||||||
|
* Note that setOpQuery is the Query containing the setOp node, whose tlist
|
||||||
|
* contains references to all the setop output columns. When called from
|
||||||
|
* pull_up_simple_union_all, this is *not* the same as root->parse, which is
|
||||||
|
* the parent Query we are pulling up into.
|
||||||
*
|
*
|
||||||
* parentRTindex is the appendrel parent's index in root->parse->rtable.
|
* parentRTindex is the appendrel parent's index in root->parse->rtable.
|
||||||
*
|
*
|
||||||
* The child RTEs have already been copied to the parent. childRToffset
|
* The child RTEs have already been copied to the parent. childRToffset
|
||||||
* tells us where in the parent's range table they were copied.
|
* tells us where in the parent's range table they were copied. When called
|
||||||
|
* from flatten_simple_union_all, childRToffset is 0 since the child RTEs
|
||||||
|
* were already in root->parse->rtable and no RT index adjustment is needed.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex,
|
pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex,
|
||||||
@ -1418,6 +1421,102 @@ pullup_replace_vars_callback(Var *var,
|
|||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* flatten_simple_union_all
|
||||||
|
* Try to optimize top-level UNION ALL structure into an appendrel
|
||||||
|
*
|
||||||
|
* If a query's setOperations tree consists entirely of simple UNION ALL
|
||||||
|
* operations, flatten it into an append relation, which we can process more
|
||||||
|
* intelligently than the general setops case. Otherwise, do nothing.
|
||||||
|
*
|
||||||
|
* In most cases, this can succeed only for a top-level query, because for a
|
||||||
|
* subquery in FROM, the parent query's invocation of pull_up_subqueries would
|
||||||
|
* already have flattened the UNION via pull_up_simple_union_all. But there
|
||||||
|
* are a few cases we can support here but not in that code path, for example
|
||||||
|
* when the subquery also contains ORDER BY.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
flatten_simple_union_all(PlannerInfo *root)
|
||||||
|
{
|
||||||
|
Query *parse = root->parse;
|
||||||
|
SetOperationStmt *topop;
|
||||||
|
Node *leftmostjtnode;
|
||||||
|
int leftmostRTI;
|
||||||
|
RangeTblEntry *leftmostRTE;
|
||||||
|
int childRTI;
|
||||||
|
RangeTblEntry *childRTE;
|
||||||
|
RangeTblRef *rtr;
|
||||||
|
|
||||||
|
/* Shouldn't be called unless query has setops */
|
||||||
|
topop = (SetOperationStmt *) parse->setOperations;
|
||||||
|
Assert(topop && IsA(topop, SetOperationStmt));
|
||||||
|
|
||||||
|
/* Can't optimize away a recursive UNION */
|
||||||
|
if (root->hasRecursion)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Recursively check the tree of set operations. If not all UNION ALL
|
||||||
|
* with identical column types, punt.
|
||||||
|
*/
|
||||||
|
if (!is_simple_union_all_recurse((Node *) topop, parse, topop->colTypes))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Locate the leftmost leaf query in the setops tree. The upper query's
|
||||||
|
* Vars all refer to this RTE (see transformSetOperationStmt).
|
||||||
|
*/
|
||||||
|
leftmostjtnode = topop->larg;
|
||||||
|
while (leftmostjtnode && IsA(leftmostjtnode, SetOperationStmt))
|
||||||
|
leftmostjtnode = ((SetOperationStmt *) leftmostjtnode)->larg;
|
||||||
|
Assert(leftmostjtnode && IsA(leftmostjtnode, RangeTblRef));
|
||||||
|
leftmostRTI = ((RangeTblRef *) leftmostjtnode)->rtindex;
|
||||||
|
leftmostRTE = rt_fetch(leftmostRTI, parse->rtable);
|
||||||
|
Assert(leftmostRTE->rtekind == RTE_SUBQUERY);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make a copy of the leftmost RTE and add it to the rtable. This copy
|
||||||
|
* will represent the leftmost leaf query in its capacity as a member
|
||||||
|
* of the appendrel. The original will represent the appendrel as a
|
||||||
|
* whole. (We must do things this way because the upper query's Vars
|
||||||
|
* have to be seen as referring to the whole appendrel.)
|
||||||
|
*/
|
||||||
|
childRTE = copyObject(leftmostRTE);
|
||||||
|
parse->rtable = lappend(parse->rtable, childRTE);
|
||||||
|
childRTI = list_length(parse->rtable);
|
||||||
|
|
||||||
|
/* Modify the setops tree to reference the child copy */
|
||||||
|
((RangeTblRef *) leftmostjtnode)->rtindex = childRTI;
|
||||||
|
|
||||||
|
/* Modify the formerly-leftmost RTE to mark it as an appendrel parent */
|
||||||
|
leftmostRTE->inh = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Form a RangeTblRef for the appendrel, and insert it into FROM. The top
|
||||||
|
* Query of a setops tree should have had an empty FromClause initially.
|
||||||
|
*/
|
||||||
|
rtr = makeNode(RangeTblRef);
|
||||||
|
rtr->rtindex = leftmostRTI;
|
||||||
|
Assert(parse->jointree->fromlist == NIL);
|
||||||
|
parse->jointree->fromlist = list_make1(rtr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now pretend the query has no setops. We must do this before trying
|
||||||
|
* to do subquery pullup, because of Assert in pull_up_simple_subquery.
|
||||||
|
*/
|
||||||
|
parse->setOperations = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build AppendRelInfo information, and apply pull_up_subqueries to the
|
||||||
|
* leaf queries of the UNION ALL. (We must do that now because they
|
||||||
|
* weren't previously referenced by the jointree, and so were missed by
|
||||||
|
* the main invocation of pull_up_subqueries.)
|
||||||
|
*/
|
||||||
|
pull_up_union_leaf_queries((Node *) topop, root, leftmostRTI, parse, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* reduce_outer_joins
|
* reduce_outer_joins
|
||||||
* Attempt to reduce outer joins to plain inner joins.
|
* Attempt to reduce outer joins to plain inner joins.
|
||||||
|
@ -26,6 +26,7 @@ extern void inline_set_returning_functions(PlannerInfo *root);
|
|||||||
extern Node *pull_up_subqueries(PlannerInfo *root, Node *jtnode,
|
extern Node *pull_up_subqueries(PlannerInfo *root, Node *jtnode,
|
||||||
JoinExpr *lowest_outer_join,
|
JoinExpr *lowest_outer_join,
|
||||||
AppendRelInfo *containing_appendrel);
|
AppendRelInfo *containing_appendrel);
|
||||||
|
extern void flatten_simple_union_all(PlannerInfo *root);
|
||||||
extern void reduce_outer_joins(PlannerInfo *root);
|
extern void reduce_outer_joins(PlannerInfo *root);
|
||||||
extern Relids get_relids_in_jointree(Node *jtnode, bool include_joins);
|
extern Relids get_relids_in_jointree(Node *jtnode, bool include_joins);
|
||||||
extern Relids get_relids_for_join(PlannerInfo *root, int joinrelid);
|
extern Relids get_relids_for_join(PlannerInfo *root, int joinrelid);
|
||||||
|
Reference in New Issue
Block a user