Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 947d0c8

Browse files
committed
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.
1 parent 543d22f commit 947d0c8

File tree

3 files changed

+121
-12
lines changed

3 files changed

+121
-12
lines changed

src/backend/optimizer/plan/planner.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,12 +341,21 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
341341
inline_set_returning_functions(root);
342342

343343
/*
344-
* Check to see if any subqueries in the rangetable can be merged into
344+
* Check to see if any subqueries in the jointree can be merged into
345345
* this query.
346346
*/
347347
parse->jointree = (FromExpr *)
348348
pull_up_subqueries(root, (Node *) parse->jointree, NULL, NULL);
349349

350+
/*
351+
* If this is a simple UNION ALL query, flatten it into an appendrel.
352+
* We do this now because it requires applying pull_up_subqueries to the
353+
* leaf queries of the UNION ALL, which weren't touched above because they
354+
* weren't referenced by the jointree (they will be after we do this).
355+
*/
356+
if (parse->setOperations)
357+
flatten_simple_union_all(root);
358+
350359
/*
351360
* Detect whether any rangetable entries are RTE_JOIN kind; if not, we can
352361
* avoid the expense of doing flatten_join_alias_vars(). Also check for

src/backend/optimizer/prep/prepjointree.c

Lines changed: 110 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* pull_up_sublinks
88
* inline_set_returning_functions
99
* pull_up_subqueries
10+
* flatten_simple_union_all
1011
* do expression preprocessing (including flattening JOIN alias vars)
1112
* reduce_outer_joins
1213
*
@@ -868,11 +869,6 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
868869
int rtoffset;
869870
List *rtable;
870871

871-
/*
872-
* Append the subquery rtable entries to upper query.
873-
*/
874-
rtoffset = list_length(root->parse->rtable);
875-
876872
/*
877873
* Append child RTEs to parent rtable.
878874
*
@@ -881,14 +877,15 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
881877
* because any such vars must refer to stuff above the level of the query
882878
* we are pulling into.
883879
*/
880+
rtoffset = list_length(root->parse->rtable);
884881
rtable = copyObject(subquery->rtable);
885882
IncrementVarSublevelsUp_rtable(rtable, -1, 1);
886883
root->parse->rtable = list_concat(root->parse->rtable, rtable);
887884

888885
/*
889886
* Recursively scan the subquery's setOperations tree and add
890887
* AppendRelInfo nodes for leaf subqueries to the parent's
891-
* append_rel_list.
888+
* append_rel_list. Also apply pull_up_subqueries to the leaf subqueries.
892889
*/
893890
Assert(subquery->setOperations);
894891
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)
905902
/*
906903
* pull_up_union_leaf_queries -- recursive guts of pull_up_simple_union_all
907904
*
908-
* Note that setOpQuery is the Query containing the setOp node, whose rtable
909-
* is where to look up the RTE if setOp is a RangeTblRef. This is *not* the
910-
* same as root->parse, which is the top-level Query we are pulling up into.
905+
* Build an AppendRelInfo for each leaf query in the setop tree, and then
906+
* apply pull_up_subqueries to the leaf query.
907+
*
908+
* Note that setOpQuery is the Query containing the setOp node, whose tlist
909+
* contains references to all the setop output columns. When called from
910+
* pull_up_simple_union_all, this is *not* the same as root->parse, which is
911+
* the parent Query we are pulling up into.
911912
*
912913
* parentRTindex is the appendrel parent's index in root->parse->rtable.
913914
*
914-
* The child RTEs have already been copied to the parent. childRToffset
915-
* tells us where in the parent's range table they were copied.
915+
* The child RTEs have already been copied to the parent. childRToffset
916+
* tells us where in the parent's range table they were copied. When called
917+
* from flatten_simple_union_all, childRToffset is 0 since the child RTEs
918+
* were already in root->parse->rtable and no RT index adjustment is needed.
916919
*/
917920
static void
918921
pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex,
@@ -1418,6 +1421,102 @@ pullup_replace_vars_callback(Var *var,
14181421
return newnode;
14191422
}
14201423

1424+
1425+
/*
1426+
* flatten_simple_union_all
1427+
* Try to optimize top-level UNION ALL structure into an appendrel
1428+
*
1429+
* If a query's setOperations tree consists entirely of simple UNION ALL
1430+
* operations, flatten it into an append relation, which we can process more
1431+
* intelligently than the general setops case. Otherwise, do nothing.
1432+
*
1433+
* In most cases, this can succeed only for a top-level query, because for a
1434+
* subquery in FROM, the parent query's invocation of pull_up_subqueries would
1435+
* already have flattened the UNION via pull_up_simple_union_all. But there
1436+
* are a few cases we can support here but not in that code path, for example
1437+
* when the subquery also contains ORDER BY.
1438+
*/
1439+
void
1440+
flatten_simple_union_all(PlannerInfo *root)
1441+
{
1442+
Query *parse = root->parse;
1443+
SetOperationStmt *topop;
1444+
Node *leftmostjtnode;
1445+
int leftmostRTI;
1446+
RangeTblEntry *leftmostRTE;
1447+
int childRTI;
1448+
RangeTblEntry *childRTE;
1449+
RangeTblRef *rtr;
1450+
1451+
/* Shouldn't be called unless query has setops */
1452+
topop = (SetOperationStmt *) parse->setOperations;
1453+
Assert(topop && IsA(topop, SetOperationStmt));
1454+
1455+
/* Can't optimize away a recursive UNION */
1456+
if (root->hasRecursion)
1457+
return;
1458+
1459+
/*
1460+
* Recursively check the tree of set operations. If not all UNION ALL
1461+
* with identical column types, punt.
1462+
*/
1463+
if (!is_simple_union_all_recurse((Node *) topop, parse, topop->colTypes))
1464+
return;
1465+
1466+
/*
1467+
* Locate the leftmost leaf query in the setops tree. The upper query's
1468+
* Vars all refer to this RTE (see transformSetOperationStmt).
1469+
*/
1470+
leftmostjtnode = topop->larg;
1471+
while (leftmostjtnode && IsA(leftmostjtnode, SetOperationStmt))
1472+
leftmostjtnode = ((SetOperationStmt *) leftmostjtnode)->larg;
1473+
Assert(leftmostjtnode && IsA(leftmostjtnode, RangeTblRef));
1474+
leftmostRTI = ((RangeTblRef *) leftmostjtnode)->rtindex;
1475+
leftmostRTE = rt_fetch(leftmostRTI, parse->rtable);
1476+
Assert(leftmostRTE->rtekind == RTE_SUBQUERY);
1477+
1478+
/*
1479+
* Make a copy of the leftmost RTE and add it to the rtable. This copy
1480+
* will represent the leftmost leaf query in its capacity as a member
1481+
* of the appendrel. The original will represent the appendrel as a
1482+
* whole. (We must do things this way because the upper query's Vars
1483+
* have to be seen as referring to the whole appendrel.)
1484+
*/
1485+
childRTE = copyObject(leftmostRTE);
1486+
parse->rtable = lappend(parse->rtable, childRTE);
1487+
childRTI = list_length(parse->rtable);
1488+
1489+
/* Modify the setops tree to reference the child copy */
1490+
((RangeTblRef *) leftmostjtnode)->rtindex = childRTI;
1491+
1492+
/* Modify the formerly-leftmost RTE to mark it as an appendrel parent */
1493+
leftmostRTE->inh = true;
1494+
1495+
/*
1496+
* Form a RangeTblRef for the appendrel, and insert it into FROM. The top
1497+
* Query of a setops tree should have had an empty FromClause initially.
1498+
*/
1499+
rtr = makeNode(RangeTblRef);
1500+
rtr->rtindex = leftmostRTI;
1501+
Assert(parse->jointree->fromlist == NIL);
1502+
parse->jointree->fromlist = list_make1(rtr);
1503+
1504+
/*
1505+
* Now pretend the query has no setops. We must do this before trying
1506+
* to do subquery pullup, because of Assert in pull_up_simple_subquery.
1507+
*/
1508+
parse->setOperations = NULL;
1509+
1510+
/*
1511+
* Build AppendRelInfo information, and apply pull_up_subqueries to the
1512+
* leaf queries of the UNION ALL. (We must do that now because they
1513+
* weren't previously referenced by the jointree, and so were missed by
1514+
* the main invocation of pull_up_subqueries.)
1515+
*/
1516+
pull_up_union_leaf_queries((Node *) topop, root, leftmostRTI, parse, 0);
1517+
}
1518+
1519+
14211520
/*
14221521
* reduce_outer_joins
14231522
* Attempt to reduce outer joins to plain inner joins.

src/include/optimizer/prep.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ extern void inline_set_returning_functions(PlannerInfo *root);
2626
extern Node *pull_up_subqueries(PlannerInfo *root, Node *jtnode,
2727
JoinExpr *lowest_outer_join,
2828
AppendRelInfo *containing_appendrel);
29+
extern void flatten_simple_union_all(PlannerInfo *root);
2930
extern void reduce_outer_joins(PlannerInfo *root);
3031
extern Relids get_relids_in_jointree(Node *jtnode, bool include_joins);
3132
extern Relids get_relids_for_join(PlannerInfo *root, int joinrelid);

0 commit comments

Comments
 (0)