Rewrite recurse_union_children to iterate, rather than recurse.
authorRobert Haas <rhaas@postgresql.org>
Fri, 23 Feb 2018 17:13:46 +0000 (12:13 -0500)
committerRobert Haas <rhaas@postgresql.org>
Thu, 8 Mar 2018 19:39:53 +0000 (14:39 -0500)
Also, rename it to plan_union_chidren, so the old name wasn't
very descriptive.  This results in a small net reduction in code,
seems at least to me to be easier to understand, and saves
space on the process stack.

Patch by me, reviewed by Ashutosh Bapat.

src/backend/optimizer/prep/prepunion.c

index b586f941a86e5357c78be43935d9e8abc4d16b24..f3873872891cde9bc3240237c905431c83745ead 100644 (file)
@@ -78,10 +78,10 @@ static Path *generate_nonunion_path(SetOperationStmt *op, PlannerInfo *root,
                                           List *refnames_tlist,
                                           List **pTargetList,
                                           double *pNumGroups);
-static List *recurse_union_children(Node *setOp, PlannerInfo *root,
-                                          SetOperationStmt *top_union,
-                                          List *refnames_tlist,
-                                          List **tlist_list);
+static List *plan_union_children(PlannerInfo *root,
+                                       SetOperationStmt *top_union,
+                                       List *refnames_tlist,
+                                       List **tlist_list);
 static Path *make_union_unique(SetOperationStmt *op, Path *path, List *tlist,
                                  PlannerInfo *root);
 static bool choose_hashed_setop(PlannerInfo *root, List *groupClauses,
@@ -545,8 +545,6 @@ generate_union_path(SetOperationStmt *op, PlannerInfo *root,
        RelOptInfo *result_rel = fetch_upper_rel(root, UPPERREL_SETOP, NULL);
        double          save_fraction = root->tuple_fraction;
        List       *pathlist;
-       List       *child_tlists1;
-       List       *child_tlists2;
        List       *tlist_list;
        List       *tlist;
        Path       *path;
@@ -571,13 +569,7 @@ generate_union_path(SetOperationStmt *op, PlannerInfo *root,
         * only one Append and unique-ification for the lot.  Recurse to find such
         * nodes and compute their children's paths.
         */
-       pathlist = list_concat(recurse_union_children(op->larg, root,
-                                                                                                 op, refnames_tlist,
-                                                                                                 &child_tlists1),
-                                                  recurse_union_children(op->rarg, root,
-                                                                                                 op, refnames_tlist,
-                                                                                                 &child_tlists2));
-       tlist_list = list_concat(child_tlists1, child_tlists2);
+       pathlist = plan_union_children(root, op, refnames_tlist, &tlist_list);
 
        /*
         * Generate tlist for Append plan node.
@@ -797,56 +789,58 @@ generate_nonunion_path(SetOperationStmt *op, PlannerInfo *root,
  * generate_union_path will force a fresh sort if the top level is a UNION.
  */
 static List *
-recurse_union_children(Node *setOp, PlannerInfo *root,
-                                          SetOperationStmt *top_union,
-                                          List *refnames_tlist,
-                                          List **tlist_list)
+plan_union_children(PlannerInfo *root,
+                                       SetOperationStmt *top_union,
+                                       List *refnames_tlist,
+                                       List **tlist_list)
 {
-       List       *result;
+       List       *pending_rels = list_make1(top_union);
+       List       *result = NIL;
        List       *child_tlist;
 
-       if (IsA(setOp, SetOperationStmt))
+       *tlist_list = NIL;
+
+       while (pending_rels != NIL)
        {
-               SetOperationStmt *op = (SetOperationStmt *) setOp;
+               Node       *setOp = linitial(pending_rels);
+
+               pending_rels = list_delete_first(pending_rels);
 
-               if (op->op == top_union->op &&
-                       (op->all == top_union->all || op->all) &&
-                       equal(op->colTypes, top_union->colTypes))
+               if (IsA(setOp, SetOperationStmt))
                {
-                       /* Same UNION, so fold children into parent's subpath list */
-                       List       *child_tlists1;
-                       List       *child_tlists2;
+                       SetOperationStmt *op = (SetOperationStmt *) setOp;
 
-                       result = list_concat(recurse_union_children(op->larg, root,
-                                                                                                               top_union,
-                                                                                                               refnames_tlist,
-                                                                                                               &child_tlists1),
-                                                                recurse_union_children(op->rarg, root,
-                                                                                                               top_union,
-                                                                                                               refnames_tlist,
-                                                                                                               &child_tlists2));
-                       *tlist_list = list_concat(child_tlists1, child_tlists2);
-                       return result;
+                       if (op->op == top_union->op &&
+                               (op->all == top_union->all || op->all) &&
+                               equal(op->colTypes, top_union->colTypes))
+                       {
+                               /* Same UNION, so fold children into parent */
+                               pending_rels = lcons(op->rarg, pending_rels);
+                               pending_rels = lcons(op->larg, pending_rels);
+                               continue;
+                       }
                }
+
+               /*
+                * Not same, so plan this child separately.
+                *
+                * Note we disallow any resjunk columns in child results.  This is
+                * necessary since the Append node that implements the union won't do
+                * any projection, and upper levels will get confused if some of our
+                * output tuples have junk and some don't.  This case only arises when
+                * we have an EXCEPT or INTERSECT as child, else there won't be
+                * resjunk anyway.
+                */
+               result = lappend(result, recurse_set_operations(setOp, root,
+                                                                                                               top_union->colTypes,
+                                                                                                               top_union->colCollations,
+                                                                                                               false, -1,
+                                                                                                               refnames_tlist,
+                                                                                                               &child_tlist,
+                                                                                                               NULL));
+               *tlist_list = lappend(*tlist_list, child_tlist);
        }
 
-       /*
-        * Not same, so plan this child separately.
-        *
-        * Note we disallow any resjunk columns in child results.  This is
-        * necessary since the Append node that implements the union won't do any
-        * projection, and upper levels will get confused if some of our output
-        * tuples have junk and some don't.  This case only arises when we have an
-        * EXCEPT or INTERSECT as child, else there won't be resjunk anyway.
-        */
-       result = list_make1(recurse_set_operations(setOp, root,
-                                                                                          top_union->colTypes,
-                                                                                          top_union->colCollations,
-                                                                                          false, -1,
-                                                                                          refnames_tlist,
-                                                                                          &child_tlist,
-                                                                                          NULL));
-       *tlist_list = list_make1(child_tlist);
        return result;
 }