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)
868869int rtoffset ;
869870List * 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 );
884881rtable = copyObject (subquery -> rtable );
885882IncrementVarSublevelsUp_rtable (rtable ,-1 ,1 );
886883root -> 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 */
893890Assert (subquery -> setOperations );
894891pull_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 */
917920static void
918921pull_up_union_leaf_queries (Node * setOp ,PlannerInfo * root ,int parentRTindex ,
@@ -1418,6 +1421,102 @@ pullup_replace_vars_callback(Var *var,
14181421return 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.