7
7
* pull_up_sublinks
8
8
* inline_set_returning_functions
9
9
* pull_up_subqueries
10
+ * flatten_simple_union_all
10
11
* do expression preprocessing (including flattening JOIN alias vars)
11
12
* reduce_outer_joins
12
13
*
@@ -868,11 +869,6 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
868
869
int rtoffset ;
869
870
List * rtable ;
870
871
871
- /*
872
- * Append the subquery rtable entries to upper query.
873
- */
874
- rtoffset = list_length (root -> parse -> rtable );
875
-
876
872
/*
877
873
* Append child RTEs to parent rtable.
878
874
*
@@ -881,14 +877,15 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
881
877
* because any such vars must refer to stuff above the level of the query
882
878
* we are pulling into.
883
879
*/
880
+ rtoffset = list_length (root -> parse -> rtable );
884
881
rtable = copyObject (subquery -> rtable );
885
882
IncrementVarSublevelsUp_rtable (rtable , -1 , 1 );
886
883
root -> parse -> rtable = list_concat (root -> parse -> rtable , rtable );
887
884
888
885
/*
889
886
* Recursively scan the subquery's setOperations tree and add
890
887
* 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.
892
889
*/
893
890
Assert (subquery -> setOperations );
894
891
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)
905
902
/*
906
903
* pull_up_union_leaf_queries -- recursive guts of pull_up_simple_union_all
907
904
*
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.
911
912
*
912
913
* parentRTindex is the appendrel parent's index in root->parse->rtable.
913
914
*
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.
916
919
*/
917
920
static void
918
921
pull_up_union_leaf_queries (Node * setOp , PlannerInfo * root , int parentRTindex ,
@@ -1418,6 +1421,102 @@ pullup_replace_vars_callback(Var *var,
1418
1421
return newnode ;
1419
1422
}
1420
1423
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
+
1421
1520
/*
1422
1521
* reduce_outer_joins
1423
1522
* Attempt to reduce outer joins to plain inner joins.
0 commit comments