LCOV - code coverage report
Current view: top level - src/backend/optimizer/prep - prepunion.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 300 332 90.4 %
Date: 2019-08-24 06:06:56 Functions: 12 12 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * prepunion.c
       4             :  *    Routines to plan set-operation queries.  The filename is a leftover
       5             :  *    from a time when only UNIONs were implemented.
       6             :  *
       7             :  * There are two code paths in the planner for set-operation queries.
       8             :  * If a subquery consists entirely of simple UNION ALL operations, it
       9             :  * is converted into an "append relation".  Otherwise, it is handled
      10             :  * by the general code in this module (plan_set_operations and its
      11             :  * subroutines).  There is some support code here for the append-relation
      12             :  * case, but most of the heavy lifting for that is done elsewhere,
      13             :  * notably in prepjointree.c and allpaths.c.
      14             :  *
      15             :  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
      16             :  * Portions Copyright (c) 1994, Regents of the University of California
      17             :  *
      18             :  *
      19             :  * IDENTIFICATION
      20             :  *    src/backend/optimizer/prep/prepunion.c
      21             :  *
      22             :  *-------------------------------------------------------------------------
      23             :  */
      24             : #include "postgres.h"
      25             : 
      26             : #include "access/htup_details.h"
      27             : #include "access/sysattr.h"
      28             : #include "catalog/partition.h"
      29             : #include "catalog/pg_inherits.h"
      30             : #include "catalog/pg_type.h"
      31             : #include "miscadmin.h"
      32             : #include "nodes/makefuncs.h"
      33             : #include "nodes/nodeFuncs.h"
      34             : #include "optimizer/cost.h"
      35             : #include "optimizer/pathnode.h"
      36             : #include "optimizer/paths.h"
      37             : #include "optimizer/planmain.h"
      38             : #include "optimizer/planner.h"
      39             : #include "optimizer/prep.h"
      40             : #include "optimizer/tlist.h"
      41             : #include "parser/parse_coerce.h"
      42             : #include "parser/parsetree.h"
      43             : #include "utils/lsyscache.h"
      44             : #include "utils/rel.h"
      45             : #include "utils/selfuncs.h"
      46             : #include "utils/syscache.h"
      47             : 
      48             : 
      49             : static RelOptInfo *recurse_set_operations(Node *setOp, PlannerInfo *root,
      50             :                                           List *colTypes, List *colCollations,
      51             :                                           bool junkOK,
      52             :                                           int flag, List *refnames_tlist,
      53             :                                           List **pTargetList,
      54             :                                           double *pNumGroups);
      55             : static RelOptInfo *generate_recursion_path(SetOperationStmt *setOp,
      56             :                                            PlannerInfo *root,
      57             :                                            List *refnames_tlist,
      58             :                                            List **pTargetList);
      59             : static RelOptInfo *generate_union_paths(SetOperationStmt *op, PlannerInfo *root,
      60             :                                         List *refnames_tlist,
      61             :                                         List **pTargetList);
      62             : static RelOptInfo *generate_nonunion_paths(SetOperationStmt *op, PlannerInfo *root,
      63             :                                            List *refnames_tlist,
      64             :                                            List **pTargetList);
      65             : static List *plan_union_children(PlannerInfo *root,
      66             :                                  SetOperationStmt *top_union,
      67             :                                  List *refnames_tlist,
      68             :                                  List **tlist_list);
      69             : static Path *make_union_unique(SetOperationStmt *op, Path *path, List *tlist,
      70             :                                PlannerInfo *root);
      71             : static void postprocess_setop_rel(PlannerInfo *root, RelOptInfo *rel);
      72             : static bool choose_hashed_setop(PlannerInfo *root, List *groupClauses,
      73             :                                 Path *input_path,
      74             :                                 double dNumGroups, double dNumOutputRows,
      75             :                                 const char *construct);
      76             : static List *generate_setop_tlist(List *colTypes, List *colCollations,
      77             :                                   int flag,
      78             :                                   Index varno,
      79             :                                   bool hack_constants,
      80             :                                   List *input_tlist,
      81             :                                   List *refnames_tlist);
      82             : static List *generate_append_tlist(List *colTypes, List *colCollations,
      83             :                                    bool flag,
      84             :                                    List *input_tlists,
      85             :                                    List *refnames_tlist);
      86             : static List *generate_setop_grouplist(SetOperationStmt *op, List *targetlist);
      87             : 
      88             : 
      89             : /*
      90             :  * plan_set_operations
      91             :  *
      92             :  *    Plans the queries for a tree of set operations (UNION/INTERSECT/EXCEPT)
      93             :  *
      94             :  * This routine only deals with the setOperations tree of the given query.
      95             :  * Any top-level ORDER BY requested in root->parse->sortClause will be handled
      96             :  * when we return to grouping_planner; likewise for LIMIT.
      97             :  *
      98             :  * What we return is an "upperrel" RelOptInfo containing at least one Path
      99             :  * that implements the set-operation tree.  In addition, root->processed_tlist
     100             :  * receives a targetlist representing the output of the topmost setop node.
     101             :  */
     102             : RelOptInfo *
     103        1246 : plan_set_operations(PlannerInfo *root)
     104             : {
     105        1246 :     Query      *parse = root->parse;
     106        1246 :     SetOperationStmt *topop = castNode(SetOperationStmt, parse->setOperations);
     107             :     Node       *node;
     108             :     RangeTblEntry *leftmostRTE;
     109             :     Query      *leftmostQuery;
     110             :     RelOptInfo *setop_rel;
     111             :     List       *top_tlist;
     112             : 
     113             :     Assert(topop);
     114             : 
     115             :     /* check for unsupported stuff */
     116             :     Assert(parse->jointree->fromlist == NIL);
     117             :     Assert(parse->jointree->quals == NULL);
     118             :     Assert(parse->groupClause == NIL);
     119             :     Assert(parse->havingQual == NULL);
     120             :     Assert(parse->windowClause == NIL);
     121             :     Assert(parse->distinctClause == NIL);
     122             : 
     123             :     /*
     124             :      * In the outer query level, we won't have any true equivalences to deal
     125             :      * with; but we do want to be able to make pathkeys, which will require
     126             :      * single-member EquivalenceClasses.  Indicate that EC merging is complete
     127             :      * so that pathkeys.c won't complain.
     128             :      */
     129             :     Assert(root->eq_classes == NIL);
     130        1246 :     root->ec_merging_done = true;
     131             : 
     132             :     /*
     133             :      * We'll need to build RelOptInfos for each of the leaf subqueries, which
     134             :      * are RTE_SUBQUERY rangetable entries in this Query.  Prepare the index
     135             :      * arrays for those, and for AppendRelInfos in case they're needed.
     136             :      */
     137        1246 :     setup_simple_rel_arrays(root);
     138             : 
     139             :     /*
     140             :      * Find the leftmost component Query.  We need to use its column names for
     141             :      * all generated tlists (else SELECT INTO won't work right).
     142             :      */
     143        1246 :     node = topop->larg;
     144        2710 :     while (node && IsA(node, SetOperationStmt))
     145         218 :         node = ((SetOperationStmt *) node)->larg;
     146             :     Assert(node && IsA(node, RangeTblRef));
     147        1246 :     leftmostRTE = root->simple_rte_array[((RangeTblRef *) node)->rtindex];
     148        1246 :     leftmostQuery = leftmostRTE->subquery;
     149             :     Assert(leftmostQuery != NULL);
     150             : 
     151             :     /*
     152             :      * If the topmost node is a recursive union, it needs special processing.
     153             :      */
     154        1246 :     if (root->hasRecursion)
     155             :     {
     156         320 :         setop_rel = generate_recursion_path(topop, root,
     157             :                                             leftmostQuery->targetList,
     158             :                                             &top_tlist);
     159             :     }
     160             :     else
     161             :     {
     162             :         /*
     163             :          * Recurse on setOperations tree to generate paths for set ops. The
     164             :          * final output paths should have just the column types shown as the
     165             :          * output from the top-level node, plus possibly resjunk working
     166             :          * columns (we can rely on upper-level nodes to deal with that).
     167             :          */
     168         926 :         setop_rel = recurse_set_operations((Node *) topop, root,
     169             :                                            topop->colTypes, topop->colCollations,
     170             :                                            true, -1,
     171             :                                            leftmostQuery->targetList,
     172             :                                            &top_tlist,
     173             :                                            NULL);
     174             :     }
     175             : 
     176             :     /* Must return the built tlist into root->processed_tlist. */
     177        1246 :     root->processed_tlist = top_tlist;
     178             : 
     179        1246 :     return setop_rel;
     180             : }
     181             : 
     182             : /*
     183             :  * recurse_set_operations
     184             :  *    Recursively handle one step in a tree of set operations
     185             :  *
     186             :  * colTypes: OID list of set-op's result column datatypes
     187             :  * colCollations: OID list of set-op's result column collations
     188             :  * junkOK: if true, child resjunk columns may be left in the result
     189             :  * flag: if >= 0, add a resjunk output column indicating value of flag
     190             :  * refnames_tlist: targetlist to take column names from
     191             :  *
     192             :  * Returns a RelOptInfo for the subtree, as well as these output parameters:
     193             :  * *pTargetList: receives the fully-fledged tlist for the subtree's top plan
     194             :  * *pNumGroups: if not NULL, we estimate the number of distinct groups
     195             :  *      in the result, and store it there
     196             :  *
     197             :  * The pTargetList output parameter is mostly redundant with the pathtarget
     198             :  * of the returned RelOptInfo, but for the moment we need it because much of
     199             :  * the logic in this file depends on flag columns being marked resjunk.
     200             :  * Pending a redesign of how that works, this is the easy way out.
     201             :  *
     202             :  * We don't have to care about typmods here: the only allowed difference
     203             :  * between set-op input and output typmods is input is a specific typmod
     204             :  * and output is -1, and that does not require a coercion.
     205             :  */
     206             : static RelOptInfo *
     207        3696 : recurse_set_operations(Node *setOp, PlannerInfo *root,
     208             :                        List *colTypes, List *colCollations,
     209             :                        bool junkOK,
     210             :                        int flag, List *refnames_tlist,
     211             :                        List **pTargetList,
     212             :                        double *pNumGroups)
     213             : {
     214        3696 :     RelOptInfo *rel = NULL;     /* keep compiler quiet */
     215             : 
     216             :     /* Guard against stack overflow due to overly complex setop nests */
     217        3696 :     check_stack_depth();
     218             : 
     219        3696 :     if (IsA(setOp, RangeTblRef))
     220             :     {
     221        2722 :         RangeTblRef *rtr = (RangeTblRef *) setOp;
     222        2722 :         RangeTblEntry *rte = root->simple_rte_array[rtr->rtindex];
     223        2722 :         Query      *subquery = rte->subquery;
     224             :         PlannerInfo *subroot;
     225             :         RelOptInfo *final_rel;
     226             :         Path       *subpath;
     227             :         Path       *path;
     228             :         List       *tlist;
     229             : 
     230             :         Assert(subquery != NULL);
     231             : 
     232             :         /* Build a RelOptInfo for this leaf subquery. */
     233        2722 :         rel = build_simple_rel(root, rtr->rtindex, NULL);
     234             : 
     235             :         /* plan_params should not be in use in current query level */
     236             :         Assert(root->plan_params == NIL);
     237             : 
     238             :         /* Generate a subroot and Paths for the subquery */
     239        2722 :         subroot = rel->subroot = subquery_planner(root->glob, subquery,
     240             :                                                   root,
     241             :                                                   false,
     242             :                                                   root->tuple_fraction);
     243             : 
     244             :         /*
     245             :          * It should not be possible for the primitive query to contain any
     246             :          * cross-references to other primitive queries in the setop tree.
     247             :          */
     248        2722 :         if (root->plan_params)
     249           0 :             elog(ERROR, "unexpected outer reference in set operation subquery");
     250             : 
     251             :         /* Figure out the appropriate target list for this subquery. */
     252        5444 :         tlist = generate_setop_tlist(colTypes, colCollations,
     253             :                                      flag,
     254        2722 :                                      rtr->rtindex,
     255             :                                      true,
     256             :                                      subroot->processed_tlist,
     257             :                                      refnames_tlist);
     258        2722 :         rel->reltarget = create_pathtarget(root, tlist);
     259             : 
     260             :         /* Return the fully-fledged tlist to caller, too */
     261        2722 :         *pTargetList = tlist;
     262             : 
     263             :         /*
     264             :          * Mark rel with estimated output rows, width, etc.  Note that we have
     265             :          * to do this before generating outer-query paths, else
     266             :          * cost_subqueryscan is not happy.
     267             :          */
     268        2722 :         set_subquery_size_estimates(root, rel);
     269             : 
     270             :         /*
     271             :          * Since we may want to add a partial path to this relation, we must
     272             :          * set its consider_parallel flag correctly.
     273             :          */
     274        2722 :         final_rel = fetch_upper_rel(subroot, UPPERREL_FINAL, NULL);
     275        2722 :         rel->consider_parallel = final_rel->consider_parallel;
     276             : 
     277             :         /*
     278             :          * For the moment, we consider only a single Path for the subquery.
     279             :          * This should change soon (make it look more like
     280             :          * set_subquery_pathlist).
     281             :          */
     282        2722 :         subpath = get_cheapest_fractional_path(final_rel,
     283             :                                                root->tuple_fraction);
     284             : 
     285             :         /*
     286             :          * Stick a SubqueryScanPath atop that.
     287             :          *
     288             :          * We don't bother to determine the subquery's output ordering since
     289             :          * it won't be reflected in the set-op result anyhow; so just label
     290             :          * the SubqueryScanPath with nil pathkeys.  (XXX that should change
     291             :          * soon too, likely.)
     292             :          */
     293        2722 :         path = (Path *) create_subqueryscan_path(root, rel, subpath,
     294             :                                                  NIL, NULL);
     295             : 
     296        2722 :         add_path(rel, path);
     297             : 
     298             :         /*
     299             :          * If we have a partial path for the child relation, we can use that
     300             :          * to build a partial path for this relation.  But there's no point in
     301             :          * considering any path but the cheapest.
     302             :          */
     303        3830 :         if (rel->consider_parallel && bms_is_empty(rel->lateral_relids) &&
     304        1108 :             final_rel->partial_pathlist != NIL)
     305             :         {
     306             :             Path       *partial_subpath;
     307             :             Path       *partial_path;
     308             : 
     309           0 :             partial_subpath = linitial(final_rel->partial_pathlist);
     310           0 :             partial_path = (Path *)
     311             :                 create_subqueryscan_path(root, rel, partial_subpath,
     312             :                                          NIL, NULL);
     313           0 :             add_partial_path(rel, partial_path);
     314             :         }
     315             : 
     316             :         /*
     317             :          * Estimate number of groups if caller wants it.  If the subquery used
     318             :          * grouping or aggregation, its output is probably mostly unique
     319             :          * anyway; otherwise do statistical estimation.
     320             :          *
     321             :          * XXX you don't really want to know about this: we do the estimation
     322             :          * using the subquery's original targetlist expressions, not the
     323             :          * subroot->processed_tlist which might seem more appropriate.  The
     324             :          * reason is that if the subquery is itself a setop, it may return a
     325             :          * processed_tlist containing "varno 0" Vars generated by
     326             :          * generate_append_tlist, and those would confuse estimate_num_groups
     327             :          * mightily.  We ought to get rid of the "varno 0" hack, but that
     328             :          * requires a redesign of the parsetree representation of setops, so
     329             :          * that there can be an RTE corresponding to each setop's output.
     330             :          */
     331        2722 :         if (pNumGroups)
     332             :         {
     333         784 :             if (subquery->groupClause || subquery->groupingSets ||
     334         776 :                 subquery->distinctClause ||
     335         768 :                 subroot->hasHavingQual || subquery->hasAggs)
     336           8 :                 *pNumGroups = subpath->rows;
     337             :             else
     338         384 :                 *pNumGroups = estimate_num_groups(subroot,
     339             :                                                   get_tlist_exprs(subquery->targetList, false),
     340             :                                                   subpath->rows,
     341             :                                                   NULL);
     342             :         }
     343             :     }
     344         974 :     else if (IsA(setOp, SetOperationStmt))
     345             :     {
     346         974 :         SetOperationStmt *op = (SetOperationStmt *) setOp;
     347             : 
     348             :         /* UNIONs are much different from INTERSECT/EXCEPT */
     349         974 :         if (op->op == SETOP_UNION)
     350         766 :             rel = generate_union_paths(op, root,
     351             :                                        refnames_tlist,
     352             :                                        pTargetList);
     353             :         else
     354         208 :             rel = generate_nonunion_paths(op, root,
     355             :                                           refnames_tlist,
     356             :                                           pTargetList);
     357         974 :         if (pNumGroups)
     358          24 :             *pNumGroups = rel->rows;
     359             : 
     360             :         /*
     361             :          * If necessary, add a Result node to project the caller-requested
     362             :          * output columns.
     363             :          *
     364             :          * XXX you don't really want to know about this: setrefs.c will apply
     365             :          * fix_upper_expr() to the Result node's tlist. This would fail if the
     366             :          * Vars generated by generate_setop_tlist() were not exactly equal()
     367             :          * to the corresponding tlist entries of the subplan. However, since
     368             :          * the subplan was generated by generate_union_paths() or
     369             :          * generate_nonunion_paths(), and hence its tlist was generated by
     370             :          * generate_append_tlist(), this will work.  We just tell
     371             :          * generate_setop_tlist() to use varno 0.
     372             :          */
     373        1924 :         if (flag >= 0 ||
     374        1888 :             !tlist_same_datatypes(*pTargetList, colTypes, junkOK) ||
     375         938 :             !tlist_same_collations(*pTargetList, colCollations, junkOK))
     376             :         {
     377             :             PathTarget *target;
     378             :             ListCell   *lc;
     379             : 
     380          36 :             *pTargetList = generate_setop_tlist(colTypes, colCollations,
     381             :                                                 flag,
     382             :                                                 0,
     383             :                                                 false,
     384             :                                                 *pTargetList,
     385             :                                                 refnames_tlist);
     386          36 :             target = create_pathtarget(root, *pTargetList);
     387             : 
     388             :             /* Apply projection to each path */
     389          72 :             foreach(lc, rel->pathlist)
     390             :             {
     391          36 :                 Path       *subpath = (Path *) lfirst(lc);
     392             :                 Path       *path;
     393             : 
     394             :                 Assert(subpath->param_info == NULL);
     395          36 :                 path = apply_projection_to_path(root, subpath->parent,
     396             :                                                 subpath, target);
     397             :                 /* If we had to add a Result, path is different from subpath */
     398          36 :                 if (path != subpath)
     399          36 :                     lfirst(lc) = path;
     400             :             }
     401             : 
     402             :             /* Apply projection to each partial path */
     403          36 :             foreach(lc, rel->partial_pathlist)
     404             :             {
     405           0 :                 Path       *subpath = (Path *) lfirst(lc);
     406             :                 Path       *path;
     407             : 
     408             :                 Assert(subpath->param_info == NULL);
     409             : 
     410             :                 /* avoid apply_projection_to_path, in case of multiple refs */
     411           0 :                 path = (Path *) create_projection_path(root, subpath->parent,
     412             :                                                        subpath, target);
     413           0 :                 lfirst(lc) = path;
     414             :             }
     415             :         }
     416             :     }
     417             :     else
     418             :     {
     419           0 :         elog(ERROR, "unrecognized node type: %d",
     420             :              (int) nodeTag(setOp));
     421             :         *pTargetList = NIL;
     422             :     }
     423             : 
     424        3696 :     postprocess_setop_rel(root, rel);
     425             : 
     426        3696 :     return rel;
     427             : }
     428             : 
     429             : /*
     430             :  * Generate paths for a recursive UNION node
     431             :  */
     432             : static RelOptInfo *
     433         320 : generate_recursion_path(SetOperationStmt *setOp, PlannerInfo *root,
     434             :                         List *refnames_tlist,
     435             :                         List **pTargetList)
     436             : {
     437             :     RelOptInfo *result_rel;
     438             :     Path       *path;
     439             :     RelOptInfo *lrel,
     440             :                *rrel;
     441             :     Path       *lpath;
     442             :     Path       *rpath;
     443             :     List       *lpath_tlist;
     444             :     List       *rpath_tlist;
     445             :     List       *tlist;
     446             :     List       *groupList;
     447             :     double      dNumGroups;
     448             : 
     449             :     /* Parser should have rejected other cases */
     450         320 :     if (setOp->op != SETOP_UNION)
     451           0 :         elog(ERROR, "only UNION queries can be recursive");
     452             :     /* Worktable ID should be assigned */
     453             :     Assert(root->wt_param_id >= 0);
     454             : 
     455             :     /*
     456             :      * Unlike a regular UNION node, process the left and right inputs
     457             :      * separately without any intention of combining them into one Append.
     458             :      */
     459         320 :     lrel = recurse_set_operations(setOp->larg, root,
     460             :                                   setOp->colTypes, setOp->colCollations,
     461             :                                   false, -1,
     462             :                                   refnames_tlist,
     463             :                                   &lpath_tlist,
     464             :                                   NULL);
     465         320 :     lpath = lrel->cheapest_total_path;
     466             :     /* The right path will want to look at the left one ... */
     467         320 :     root->non_recursive_path = lpath;
     468         320 :     rrel = recurse_set_operations(setOp->rarg, root,
     469             :                                   setOp->colTypes, setOp->colCollations,
     470             :                                   false, -1,
     471             :                                   refnames_tlist,
     472             :                                   &rpath_tlist,
     473             :                                   NULL);
     474         320 :     rpath = rrel->cheapest_total_path;
     475         320 :     root->non_recursive_path = NULL;
     476             : 
     477             :     /*
     478             :      * Generate tlist for RecursiveUnion path node --- same as in Append cases
     479             :      */
     480         320 :     tlist = generate_append_tlist(setOp->colTypes, setOp->colCollations, false,
     481         320 :                                   list_make2(lpath_tlist, rpath_tlist),
     482             :                                   refnames_tlist);
     483             : 
     484         320 :     *pTargetList = tlist;
     485             : 
     486             :     /* Build result relation. */
     487         320 :     result_rel = fetch_upper_rel(root, UPPERREL_SETOP,
     488         320 :                                  bms_union(lrel->relids, rrel->relids));
     489         320 :     result_rel->reltarget = create_pathtarget(root, tlist);
     490             : 
     491             :     /*
     492             :      * If UNION, identify the grouping operators
     493             :      */
     494         320 :     if (setOp->all)
     495             :     {
     496         168 :         groupList = NIL;
     497         168 :         dNumGroups = 0;
     498             :     }
     499             :     else
     500             :     {
     501             :         /* Identify the grouping semantics */
     502         152 :         groupList = generate_setop_grouplist(setOp, tlist);
     503             : 
     504             :         /* We only support hashing here */
     505         152 :         if (!grouping_is_hashable(groupList))
     506           0 :             ereport(ERROR,
     507             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     508             :                      errmsg("could not implement recursive UNION"),
     509             :                      errdetail("All column datatypes must be hashable.")));
     510             : 
     511             :         /*
     512             :          * For the moment, take the number of distinct groups as equal to the
     513             :          * total input size, ie, the worst case.
     514             :          */
     515         152 :         dNumGroups = lpath->rows + rpath->rows * 10;
     516             :     }
     517             : 
     518             :     /*
     519             :      * And make the path node.
     520             :      */
     521         640 :     path = (Path *) create_recursiveunion_path(root,
     522             :                                                result_rel,
     523             :                                                lpath,
     524             :                                                rpath,
     525         320 :                                                result_rel->reltarget,
     526             :                                                groupList,
     527             :                                                root->wt_param_id,
     528             :                                                dNumGroups);
     529             : 
     530         320 :     add_path(result_rel, path);
     531         320 :     postprocess_setop_rel(root, result_rel);
     532         320 :     return result_rel;
     533             : }
     534             : 
     535             : /*
     536             :  * Generate paths for a UNION or UNION ALL node
     537             :  */
     538             : static RelOptInfo *
     539         766 : generate_union_paths(SetOperationStmt *op, PlannerInfo *root,
     540             :                      List *refnames_tlist,
     541             :                      List **pTargetList)
     542             : {
     543         766 :     Relids      relids = NULL;
     544             :     RelOptInfo *result_rel;
     545         766 :     double      save_fraction = root->tuple_fraction;
     546             :     ListCell   *lc;
     547         766 :     List       *pathlist = NIL;
     548         766 :     List       *partial_pathlist = NIL;
     549         766 :     bool        partial_paths_valid = true;
     550         766 :     bool        consider_parallel = true;
     551             :     List       *rellist;
     552             :     List       *tlist_list;
     553             :     List       *tlist;
     554             :     Path       *path;
     555             : 
     556             :     /*
     557             :      * If plain UNION, tell children to fetch all tuples.
     558             :      *
     559             :      * Note: in UNION ALL, we pass the top-level tuple_fraction unmodified to
     560             :      * each arm of the UNION ALL.  One could make a case for reducing the
     561             :      * tuple fraction for later arms (discounting by the expected size of the
     562             :      * earlier arms' results) but it seems not worth the trouble. The normal
     563             :      * case where tuple_fraction isn't already zero is a LIMIT at top level,
     564             :      * and passing it down as-is is usually enough to get the desired result
     565             :      * of preferring fast-start plans.
     566             :      */
     567         766 :     if (!op->all)
     568         550 :         root->tuple_fraction = 0.0;
     569             : 
     570             :     /*
     571             :      * If any of my children are identical UNION nodes (same op, all-flag, and
     572             :      * colTypes) then they can be merged into this node so that we generate
     573             :      * only one Append and unique-ification for the lot.  Recurse to find such
     574             :      * nodes and compute their children's paths.
     575             :      */
     576         766 :     rellist = plan_union_children(root, op, refnames_tlist, &tlist_list);
     577             : 
     578             :     /*
     579             :      * Generate tlist for Append plan node.
     580             :      *
     581             :      * The tlist for an Append plan isn't important as far as the Append is
     582             :      * concerned, but we must make it look real anyway for the benefit of the
     583             :      * next plan level up.
     584             :      */
     585         766 :     tlist = generate_append_tlist(op->colTypes, op->colCollations, false,
     586             :                                   tlist_list, refnames_tlist);
     587             : 
     588         766 :     *pTargetList = tlist;
     589             : 
     590             :     /* Build path lists and relid set. */
     591        2480 :     foreach(lc, rellist)
     592             :     {
     593        1714 :         RelOptInfo *rel = lfirst(lc);
     594             : 
     595        1714 :         pathlist = lappend(pathlist, rel->cheapest_total_path);
     596             : 
     597        1714 :         if (consider_parallel)
     598             :         {
     599        1170 :             if (!rel->consider_parallel)
     600             :             {
     601         526 :                 consider_parallel = false;
     602         526 :                 partial_paths_valid = false;
     603             :             }
     604         644 :             else if (rel->partial_pathlist == NIL)
     605         644 :                 partial_paths_valid = false;
     606             :             else
     607           0 :                 partial_pathlist = lappend(partial_pathlist,
     608           0 :                                            linitial(rel->partial_pathlist));
     609             :         }
     610             : 
     611        1714 :         relids = bms_union(relids, rel->relids);
     612             :     }
     613             : 
     614             :     /* Build result relation. */
     615         766 :     result_rel = fetch_upper_rel(root, UPPERREL_SETOP, relids);
     616         766 :     result_rel->reltarget = create_pathtarget(root, tlist);
     617         766 :     result_rel->consider_parallel = consider_parallel;
     618             : 
     619             :     /*
     620             :      * Append the child results together.
     621             :      */
     622         766 :     path = (Path *) create_append_path(root, result_rel, pathlist, NIL,
     623             :                                        NIL, NULL, 0, false, NIL, -1);
     624             : 
     625             :     /*
     626             :      * For UNION ALL, we just need the Append path.  For UNION, need to add
     627             :      * node(s) to remove duplicates.
     628             :      */
     629         766 :     if (!op->all)
     630         550 :         path = make_union_unique(op, path, tlist, root);
     631             : 
     632         766 :     add_path(result_rel, path);
     633             : 
     634             :     /*
     635             :      * Estimate number of groups.  For now we just assume the output is unique
     636             :      * --- this is certainly true for the UNION case, and we want worst-case
     637             :      * estimates anyway.
     638             :      */
     639         766 :     result_rel->rows = path->rows;
     640             : 
     641             :     /*
     642             :      * Now consider doing the same thing using the partial paths plus Append
     643             :      * plus Gather.
     644             :      */
     645         766 :     if (partial_paths_valid)
     646             :     {
     647             :         Path       *ppath;
     648             :         ListCell   *lc;
     649           0 :         int         parallel_workers = 0;
     650             : 
     651             :         /* Find the highest number of workers requested for any subpath. */
     652           0 :         foreach(lc, partial_pathlist)
     653             :         {
     654           0 :             Path       *path = lfirst(lc);
     655             : 
     656           0 :             parallel_workers = Max(parallel_workers, path->parallel_workers);
     657             :         }
     658             :         Assert(parallel_workers > 0);
     659             : 
     660             :         /*
     661             :          * If the use of parallel append is permitted, always request at least
     662             :          * log2(# of children) paths.  We assume it can be useful to have
     663             :          * extra workers in this case because they will be spread out across
     664             :          * the children.  The precise formula is just a guess; see
     665             :          * add_paths_to_append_rel.
     666             :          */
     667           0 :         if (enable_parallel_append)
     668             :         {
     669           0 :             parallel_workers = Max(parallel_workers,
     670             :                                    fls(list_length(partial_pathlist)));
     671           0 :             parallel_workers = Min(parallel_workers,
     672             :                                    max_parallel_workers_per_gather);
     673             :         }
     674             :         Assert(parallel_workers > 0);
     675             : 
     676           0 :         ppath = (Path *)
     677           0 :             create_append_path(root, result_rel, NIL, partial_pathlist,
     678             :                                NIL, NULL,
     679             :                                parallel_workers, enable_parallel_append,
     680             :                                NIL, -1);
     681           0 :         ppath = (Path *)
     682             :             create_gather_path(root, result_rel, ppath,
     683           0 :                                result_rel->reltarget, NULL, NULL);
     684           0 :         if (!op->all)
     685           0 :             ppath = make_union_unique(op, ppath, tlist, root);
     686           0 :         add_path(result_rel, ppath);
     687             :     }
     688             : 
     689             :     /* Undo effects of possibly forcing tuple_fraction to 0 */
     690         766 :     root->tuple_fraction = save_fraction;
     691             : 
     692         766 :     return result_rel;
     693             : }
     694             : 
     695             : /*
     696             :  * Generate paths for an INTERSECT, INTERSECT ALL, EXCEPT, or EXCEPT ALL node
     697             :  */
     698             : static RelOptInfo *
     699         208 : generate_nonunion_paths(SetOperationStmt *op, PlannerInfo *root,
     700             :                         List *refnames_tlist,
     701             :                         List **pTargetList)
     702             : {
     703             :     RelOptInfo *result_rel;
     704             :     RelOptInfo *lrel,
     705             :                *rrel;
     706         208 :     double      save_fraction = root->tuple_fraction;
     707             :     Path       *lpath,
     708             :                *rpath,
     709             :                *path;
     710             :     List       *lpath_tlist,
     711             :                *rpath_tlist,
     712             :                *tlist_list,
     713             :                *tlist,
     714             :                *groupList,
     715             :                *pathlist;
     716             :     double      dLeftGroups,
     717             :                 dRightGroups,
     718             :                 dNumGroups,
     719             :                 dNumOutputRows;
     720             :     bool        use_hash;
     721             :     SetOpCmd    cmd;
     722             :     int         firstFlag;
     723             : 
     724             :     /*
     725             :      * Tell children to fetch all tuples.
     726             :      */
     727         208 :     root->tuple_fraction = 0.0;
     728             : 
     729             :     /* Recurse on children, ensuring their outputs are marked */
     730         208 :     lrel = recurse_set_operations(op->larg, root,
     731             :                                   op->colTypes, op->colCollations,
     732             :                                   false, 0,
     733             :                                   refnames_tlist,
     734             :                                   &lpath_tlist,
     735             :                                   &dLeftGroups);
     736         208 :     lpath = lrel->cheapest_total_path;
     737         208 :     rrel = recurse_set_operations(op->rarg, root,
     738             :                                   op->colTypes, op->colCollations,
     739             :                                   false, 1,
     740             :                                   refnames_tlist,
     741             :                                   &rpath_tlist,
     742             :                                   &dRightGroups);
     743         208 :     rpath = rrel->cheapest_total_path;
     744             : 
     745             :     /* Undo effects of forcing tuple_fraction to 0 */
     746         208 :     root->tuple_fraction = save_fraction;
     747             : 
     748             :     /*
     749             :      * For EXCEPT, we must put the left input first.  For INTERSECT, either
     750             :      * order should give the same results, and we prefer to put the smaller
     751             :      * input first in order to minimize the size of the hash table in the
     752             :      * hashing case.  "Smaller" means the one with the fewer groups.
     753             :      */
     754         208 :     if (op->op == SETOP_EXCEPT || dLeftGroups <= dRightGroups)
     755             :     {
     756         188 :         pathlist = list_make2(lpath, rpath);
     757         188 :         tlist_list = list_make2(lpath_tlist, rpath_tlist);
     758         188 :         firstFlag = 0;
     759             :     }
     760             :     else
     761             :     {
     762          20 :         pathlist = list_make2(rpath, lpath);
     763          20 :         tlist_list = list_make2(rpath_tlist, lpath_tlist);
     764          20 :         firstFlag = 1;
     765             :     }
     766             : 
     767             :     /*
     768             :      * Generate tlist for Append plan node.
     769             :      *
     770             :      * The tlist for an Append plan isn't important as far as the Append is
     771             :      * concerned, but we must make it look real anyway for the benefit of the
     772             :      * next plan level up.  In fact, it has to be real enough that the flag
     773             :      * column is shown as a variable not a constant, else setrefs.c will get
     774             :      * confused.
     775             :      */
     776         208 :     tlist = generate_append_tlist(op->colTypes, op->colCollations, true,
     777             :                                   tlist_list, refnames_tlist);
     778             : 
     779         208 :     *pTargetList = tlist;
     780             : 
     781             :     /* Build result relation. */
     782         208 :     result_rel = fetch_upper_rel(root, UPPERREL_SETOP,
     783         208 :                                  bms_union(lrel->relids, rrel->relids));
     784         208 :     result_rel->reltarget = create_pathtarget(root, tlist);
     785             : 
     786             :     /*
     787             :      * Append the child results together.
     788             :      */
     789         208 :     path = (Path *) create_append_path(root, result_rel, pathlist, NIL,
     790             :                                        NIL, NULL, 0, false, NIL, -1);
     791             : 
     792             :     /* Identify the grouping semantics */
     793         208 :     groupList = generate_setop_grouplist(op, tlist);
     794             : 
     795             :     /*
     796             :      * Estimate number of distinct groups that we'll need hashtable entries
     797             :      * for; this is the size of the left-hand input for EXCEPT, or the smaller
     798             :      * input for INTERSECT.  Also estimate the number of eventual output rows.
     799             :      * In non-ALL cases, we estimate each group produces one output row; in
     800             :      * ALL cases use the relevant relation size.  These are worst-case
     801             :      * estimates, of course, but we need to be conservative.
     802             :      */
     803         208 :     if (op->op == SETOP_EXCEPT)
     804             :     {
     805         120 :         dNumGroups = dLeftGroups;
     806         120 :         dNumOutputRows = op->all ? lpath->rows : dNumGroups;
     807             :     }
     808             :     else
     809             :     {
     810          88 :         dNumGroups = Min(dLeftGroups, dRightGroups);
     811          88 :         dNumOutputRows = op->all ? Min(lpath->rows, rpath->rows) : dNumGroups;
     812             :     }
     813             : 
     814             :     /*
     815             :      * Decide whether to hash or sort, and add a sort node if needed.
     816             :      */
     817         208 :     use_hash = choose_hashed_setop(root, groupList, path,
     818             :                                    dNumGroups, dNumOutputRows,
     819         208 :                                    (op->op == SETOP_INTERSECT) ? "INTERSECT" : "EXCEPT");
     820             : 
     821         208 :     if (groupList && !use_hash)
     822          20 :         path = (Path *) create_sort_path(root,
     823             :                                          result_rel,
     824             :                                          path,
     825             :                                          make_pathkeys_for_sortclauses(root,
     826             :                                                                        groupList,
     827             :                                                                        tlist),
     828             :                                          -1.0);
     829             : 
     830             :     /*
     831             :      * Finally, add a SetOp path node to generate the correct output.
     832             :      */
     833         208 :     switch (op->op)
     834             :     {
     835             :         case SETOP_INTERSECT:
     836          88 :             cmd = op->all ? SETOPCMD_INTERSECT_ALL : SETOPCMD_INTERSECT;
     837          88 :             break;
     838             :         case SETOP_EXCEPT:
     839         120 :             cmd = op->all ? SETOPCMD_EXCEPT_ALL : SETOPCMD_EXCEPT;
     840         120 :             break;
     841             :         default:
     842           0 :             elog(ERROR, "unrecognized set op: %d", (int) op->op);
     843             :             cmd = SETOPCMD_INTERSECT;   /* keep compiler quiet */
     844             :             break;
     845             :     }
     846         416 :     path = (Path *) create_setop_path(root,
     847             :                                       result_rel,
     848             :                                       path,
     849             :                                       cmd,
     850             :                                       use_hash ? SETOP_HASHED : SETOP_SORTED,
     851             :                                       groupList,
     852         208 :                                       list_length(op->colTypes) + 1,
     853             :                                       use_hash ? firstFlag : -1,
     854             :                                       dNumGroups,
     855             :                                       dNumOutputRows);
     856             : 
     857         208 :     result_rel->rows = path->rows;
     858         208 :     add_path(result_rel, path);
     859         208 :     return result_rel;
     860             : }
     861             : 
     862             : /*
     863             :  * Pull up children of a UNION node that are identically-propertied UNIONs.
     864             :  *
     865             :  * NOTE: we can also pull a UNION ALL up into a UNION, since the distinct
     866             :  * output rows will be lost anyway.
     867             :  *
     868             :  * NOTE: currently, we ignore collations while determining if a child has
     869             :  * the same properties.  This is semantically sound only so long as all
     870             :  * collations have the same notion of equality.  It is valid from an
     871             :  * implementation standpoint because we don't care about the ordering of
     872             :  * a UNION child's result: UNION ALL results are always unordered, and
     873             :  * generate_union_paths will force a fresh sort if the top level is a UNION.
     874             :  */
     875             : static List *
     876         766 : plan_union_children(PlannerInfo *root,
     877             :                     SetOperationStmt *top_union,
     878             :                     List *refnames_tlist,
     879             :                     List **tlist_list)
     880             : {
     881         766 :     List       *pending_rels = list_make1(top_union);
     882         766 :     List       *result = NIL;
     883             :     List       *child_tlist;
     884             : 
     885         766 :     *tlist_list = NIL;
     886             : 
     887        4194 :     while (pending_rels != NIL)
     888             :     {
     889        2662 :         Node       *setOp = linitial(pending_rels);
     890             : 
     891        2662 :         pending_rels = list_delete_first(pending_rels);
     892             : 
     893        2662 :         if (IsA(setOp, SetOperationStmt))
     894             :         {
     895         968 :             SetOperationStmt *op = (SetOperationStmt *) setOp;
     896             : 
     897        1932 :             if (op->op == top_union->op &&
     898        1932 :                 (op->all == top_union->all || op->all) &&
     899         956 :                 equal(op->colTypes, top_union->colTypes))
     900             :             {
     901             :                 /* Same UNION, so fold children into parent */
     902         948 :                 pending_rels = lcons(op->rarg, pending_rels);
     903         948 :                 pending_rels = lcons(op->larg, pending_rels);
     904         948 :                 continue;
     905             :             }
     906             :         }
     907             : 
     908             :         /*
     909             :          * Not same, so plan this child separately.
     910             :          *
     911             :          * Note we disallow any resjunk columns in child results.  This is
     912             :          * necessary since the Append node that implements the union won't do
     913             :          * any projection, and upper levels will get confused if some of our
     914             :          * output tuples have junk and some don't.  This case only arises when
     915             :          * we have an EXCEPT or INTERSECT as child, else there won't be
     916             :          * resjunk anyway.
     917             :          */
     918        1714 :         result = lappend(result, recurse_set_operations(setOp, root,
     919             :                                                         top_union->colTypes,
     920             :                                                         top_union->colCollations,
     921             :                                                         false, -1,
     922             :                                                         refnames_tlist,
     923             :                                                         &child_tlist,
     924             :                                                         NULL));
     925        1714 :         *tlist_list = lappend(*tlist_list, child_tlist);
     926             :     }
     927             : 
     928         766 :     return result;
     929             : }
     930             : 
     931             : /*
     932             :  * Add nodes to the given path tree to unique-ify the result of a UNION.
     933             :  */
     934             : static Path *
     935         550 : make_union_unique(SetOperationStmt *op, Path *path, List *tlist,
     936             :                   PlannerInfo *root)
     937             : {
     938         550 :     RelOptInfo *result_rel = fetch_upper_rel(root, UPPERREL_SETOP, NULL);
     939             :     List       *groupList;
     940             :     double      dNumGroups;
     941             : 
     942             :     /* Identify the grouping semantics */
     943         550 :     groupList = generate_setop_grouplist(op, tlist);
     944             : 
     945             :     /*
     946             :      * XXX for the moment, take the number of distinct groups as equal to the
     947             :      * total input size, ie, the worst case.  This is too conservative, but we
     948             :      * don't want to risk having the hashtable overrun memory; also, it's not
     949             :      * clear how to get a decent estimate of the true size.  One should note
     950             :      * as well the propensity of novices to write UNION rather than UNION ALL
     951             :      * even when they don't expect any duplicates...
     952             :      */
     953         550 :     dNumGroups = path->rows;
     954             : 
     955             :     /* Decide whether to hash or sort */
     956         550 :     if (choose_hashed_setop(root, groupList, path,
     957             :                             dNumGroups, dNumGroups,
     958             :                             "UNION"))
     959             :     {
     960             :         /* Hashed aggregate plan --- no sort needed */
     961         434 :         path = (Path *) create_agg_path(root,
     962             :                                         result_rel,
     963             :                                         path,
     964             :                                         create_pathtarget(root, tlist),
     965             :                                         AGG_HASHED,
     966             :                                         AGGSPLIT_SIMPLE,
     967             :                                         groupList,
     968             :                                         NIL,
     969             :                                         NULL,
     970             :                                         dNumGroups);
     971             :     }
     972             :     else
     973             :     {
     974             :         /* Sort and Unique */
     975         116 :         if (groupList)
     976         104 :             path = (Path *)
     977         104 :                 create_sort_path(root,
     978             :                                  result_rel,
     979             :                                  path,
     980             :                                  make_pathkeys_for_sortclauses(root,
     981             :                                                                groupList,
     982             :                                                                tlist),
     983             :                                  -1.0);
     984         116 :         path = (Path *) create_upper_unique_path(root,
     985             :                                                  result_rel,
     986             :                                                  path,
     987         116 :                                                  list_length(path->pathkeys),
     988             :                                                  dNumGroups);
     989             :     }
     990             : 
     991         550 :     return path;
     992             : }
     993             : 
     994             : /*
     995             :  * postprocess_setop_rel - perform steps required after adding paths
     996             :  */
     997             : static void
     998        4016 : postprocess_setop_rel(PlannerInfo *root, RelOptInfo *rel)
     999             : {
    1000             :     /*
    1001             :      * We don't currently worry about allowing FDWs to contribute paths to
    1002             :      * this relation, but give extensions a chance.
    1003             :      */
    1004        4016 :     if (create_upper_paths_hook)
    1005           0 :         (*create_upper_paths_hook) (root, UPPERREL_SETOP,
    1006             :                                     NULL, rel, NULL);
    1007             : 
    1008             :     /* Select cheapest path */
    1009        4016 :     set_cheapest(rel);
    1010        4016 : }
    1011             : 
    1012             : /*
    1013             :  * choose_hashed_setop - should we use hashing for a set operation?
    1014             :  */
    1015             : static bool
    1016         758 : choose_hashed_setop(PlannerInfo *root, List *groupClauses,
    1017             :                     Path *input_path,
    1018             :                     double dNumGroups, double dNumOutputRows,
    1019             :                     const char *construct)
    1020             : {
    1021         758 :     int         numGroupCols = list_length(groupClauses);
    1022             :     bool        can_sort;
    1023             :     bool        can_hash;
    1024             :     Size        hashentrysize;
    1025             :     Path        hashed_p;
    1026             :     Path        sorted_p;
    1027             :     double      tuple_fraction;
    1028             : 
    1029             :     /* Check whether the operators support sorting or hashing */
    1030         758 :     can_sort = grouping_is_sortable(groupClauses);
    1031         758 :     can_hash = grouping_is_hashable(groupClauses);
    1032         758 :     if (can_hash && can_sort)
    1033             :     {
    1034             :         /* we have a meaningful choice to make, continue ... */
    1035             :     }
    1036         324 :     else if (can_hash)
    1037         320 :         return true;
    1038           4 :     else if (can_sort)
    1039           4 :         return false;
    1040             :     else
    1041           0 :         ereport(ERROR,
    1042             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1043             :         /* translator: %s is UNION, INTERSECT, or EXCEPT */
    1044             :                  errmsg("could not implement %s", construct),
    1045             :                  errdetail("Some of the datatypes only support hashing, while others only support sorting.")));
    1046             : 
    1047             :     /* Prefer sorting when enable_hashagg is off */
    1048         434 :     if (!enable_hashagg)
    1049          44 :         return false;
    1050             : 
    1051             :     /*
    1052             :      * Don't do it if it doesn't look like the hashtable will fit into
    1053             :      * work_mem.
    1054             :      */
    1055         390 :     hashentrysize = MAXALIGN(input_path->pathtarget->width) + MAXALIGN(SizeofMinimalTupleHeader);
    1056             : 
    1057         390 :     if (hashentrysize * dNumGroups > work_mem * 1024L)
    1058           0 :         return false;
    1059             : 
    1060             :     /*
    1061             :      * See if the estimated cost is no more than doing it the other way.
    1062             :      *
    1063             :      * We need to consider input_plan + hashagg versus input_plan + sort +
    1064             :      * group.  Note that the actual result plan might involve a SetOp or
    1065             :      * Unique node, not Agg or Group, but the cost estimates for Agg and Group
    1066             :      * should be close enough for our purposes here.
    1067             :      *
    1068             :      * These path variables are dummies that just hold cost fields; we don't
    1069             :      * make actual Paths for these steps.
    1070             :      */
    1071         390 :     cost_agg(&hashed_p, root, AGG_HASHED, NULL,
    1072             :              numGroupCols, dNumGroups,
    1073             :              NIL,
    1074             :              input_path->startup_cost, input_path->total_cost,
    1075             :              input_path->rows);
    1076             : 
    1077             :     /*
    1078             :      * Now for the sorted case.  Note that the input is *always* unsorted,
    1079             :      * since it was made by appending unrelated sub-relations together.
    1080             :      */
    1081         390 :     sorted_p.startup_cost = input_path->startup_cost;
    1082         390 :     sorted_p.total_cost = input_path->total_cost;
    1083             :     /* XXX cost_sort doesn't actually look at pathkeys, so just pass NIL */
    1084         780 :     cost_sort(&sorted_p, root, NIL, sorted_p.total_cost,
    1085         390 :               input_path->rows, input_path->pathtarget->width,
    1086             :               0.0, work_mem, -1.0);
    1087         390 :     cost_group(&sorted_p, root, numGroupCols, dNumGroups,
    1088             :                NIL,
    1089             :                sorted_p.startup_cost, sorted_p.total_cost,
    1090             :                input_path->rows);
    1091             : 
    1092             :     /*
    1093             :      * Now make the decision using the top-level tuple fraction.  First we
    1094             :      * have to convert an absolute count (LIMIT) into fractional form.
    1095             :      */
    1096         390 :     tuple_fraction = root->tuple_fraction;
    1097         390 :     if (tuple_fraction >= 1.0)
    1098           0 :         tuple_fraction /= dNumOutputRows;
    1099             : 
    1100         390 :     if (compare_fractional_path_costs(&hashed_p, &sorted_p,
    1101             :                                       tuple_fraction) < 0)
    1102             :     {
    1103             :         /* Hashed is cheaper, so use it */
    1104         282 :         return true;
    1105             :     }
    1106         108 :     return false;
    1107             : }
    1108             : 
    1109             : /*
    1110             :  * Generate targetlist for a set-operation plan node
    1111             :  *
    1112             :  * colTypes: OID list of set-op's result column datatypes
    1113             :  * colCollations: OID list of set-op's result column collations
    1114             :  * flag: -1 if no flag column needed, 0 or 1 to create a const flag column
    1115             :  * varno: varno to use in generated Vars
    1116             :  * hack_constants: true to copy up constants (see comments in code)
    1117             :  * input_tlist: targetlist of this node's input node
    1118             :  * refnames_tlist: targetlist to take column names from
    1119             :  */
    1120             : static List *
    1121        2758 : generate_setop_tlist(List *colTypes, List *colCollations,
    1122             :                      int flag,
    1123             :                      Index varno,
    1124             :                      bool hack_constants,
    1125             :                      List *input_tlist,
    1126             :                      List *refnames_tlist)
    1127             : {
    1128        2758 :     List       *tlist = NIL;
    1129        2758 :     int         resno = 1;
    1130             :     ListCell   *ctlc,
    1131             :                *cclc,
    1132             :                *itlc,
    1133             :                *rtlc;
    1134             :     TargetEntry *tle;
    1135             :     Node       *expr;
    1136             : 
    1137        8572 :     forfour(ctlc, colTypes, cclc, colCollations,
    1138             :             itlc, input_tlist, rtlc, refnames_tlist)
    1139             :     {
    1140        5814 :         Oid         colType = lfirst_oid(ctlc);
    1141        5814 :         Oid         colColl = lfirst_oid(cclc);
    1142        5814 :         TargetEntry *inputtle = (TargetEntry *) lfirst(itlc);
    1143        5814 :         TargetEntry *reftle = (TargetEntry *) lfirst(rtlc);
    1144             : 
    1145             :         Assert(inputtle->resno == resno);
    1146             :         Assert(reftle->resno == resno);
    1147             :         Assert(!inputtle->resjunk);
    1148             :         Assert(!reftle->resjunk);
    1149             : 
    1150             :         /*
    1151             :          * Generate columns referencing input columns and having appropriate
    1152             :          * data types and column names.  Insert datatype coercions where
    1153             :          * necessary.
    1154             :          *
    1155             :          * HACK: constants in the input's targetlist are copied up as-is
    1156             :          * rather than being referenced as subquery outputs.  This is mainly
    1157             :          * to ensure that when we try to coerce them to the output column's
    1158             :          * datatype, the right things happen for UNKNOWN constants.  But do
    1159             :          * this only at the first level of subquery-scan plans; we don't want
    1160             :          * phony constants appearing in the output tlists of upper-level
    1161             :          * nodes!
    1162             :          */
    1163        5814 :         if (hack_constants && inputtle->expr && IsA(inputtle->expr, Const))
    1164        1316 :             expr = (Node *) inputtle->expr;
    1165             :         else
    1166       17992 :             expr = (Node *) makeVar(varno,
    1167        4498 :                                     inputtle->resno,
    1168        4498 :                                     exprType((Node *) inputtle->expr),
    1169        4498 :                                     exprTypmod((Node *) inputtle->expr),
    1170        4498 :                                     exprCollation((Node *) inputtle->expr),
    1171             :                                     0);
    1172             : 
    1173        5814 :         if (exprType(expr) != colType)
    1174             :         {
    1175             :             /*
    1176             :              * Note: it's not really cool to be applying coerce_to_common_type
    1177             :              * here; one notable point is that assign_expr_collations never
    1178             :              * gets run on any generated nodes.  For the moment that's not a
    1179             :              * problem because we force the correct exposed collation below.
    1180             :              * It would likely be best to make the parser generate the correct
    1181             :              * output tlist for every set-op to begin with, though.
    1182             :              */
    1183         424 :             expr = coerce_to_common_type(NULL,  /* no UNKNOWNs here */
    1184             :                                          expr,
    1185             :                                          colType,
    1186             :                                          "UNION/INTERSECT/EXCEPT");
    1187             :         }
    1188             : 
    1189             :         /*
    1190             :          * Ensure the tlist entry's exposed collation matches the set-op. This
    1191             :          * is necessary because plan_set_operations() reports the result
    1192             :          * ordering as a list of SortGroupClauses, which don't carry collation
    1193             :          * themselves but just refer to tlist entries.  If we don't show the
    1194             :          * right collation then planner.c might do the wrong thing in
    1195             :          * higher-level queries.
    1196             :          *
    1197             :          * Note we use RelabelType, not CollateExpr, since this expression
    1198             :          * will reach the executor without any further processing.
    1199             :          */
    1200        5814 :         if (exprCollation(expr) != colColl)
    1201             :         {
    1202          24 :             expr = (Node *) makeRelabelType((Expr *) expr,
    1203             :                                             exprType(expr),
    1204             :                                             exprTypmod(expr),
    1205             :                                             colColl,
    1206             :                                             COERCE_IMPLICIT_CAST);
    1207             :         }
    1208             : 
    1209       11628 :         tle = makeTargetEntry((Expr *) expr,
    1210        5814 :                               (AttrNumber) resno++,
    1211        5814 :                               pstrdup(reftle->resname),
    1212             :                               false);
    1213             : 
    1214             :         /*
    1215             :          * By convention, all non-resjunk columns in a setop tree have
    1216             :          * ressortgroupref equal to their resno.  In some cases the ref isn't
    1217             :          * needed, but this is a cleaner way than modifying the tlist later.
    1218             :          */
    1219        5814 :         tle->ressortgroupref = tle->resno;
    1220             : 
    1221        5814 :         tlist = lappend(tlist, tle);
    1222             :     }
    1223             : 
    1224        2758 :     if (flag >= 0)
    1225             :     {
    1226             :         /* Add a resjunk flag column */
    1227             :         /* flag value is the given constant */
    1228         416 :         expr = (Node *) makeConst(INT4OID,
    1229             :                                   -1,
    1230             :                                   InvalidOid,
    1231             :                                   sizeof(int32),
    1232             :                                   Int32GetDatum(flag),
    1233             :                                   false,
    1234             :                                   true);
    1235         832 :         tle = makeTargetEntry((Expr *) expr,
    1236         416 :                               (AttrNumber) resno++,
    1237             :                               pstrdup("flag"),
    1238             :                               true);
    1239         416 :         tlist = lappend(tlist, tle);
    1240             :     }
    1241             : 
    1242        2758 :     return tlist;
    1243             : }
    1244             : 
    1245             : /*
    1246             :  * Generate targetlist for a set-operation Append node
    1247             :  *
    1248             :  * colTypes: OID list of set-op's result column datatypes
    1249             :  * colCollations: OID list of set-op's result column collations
    1250             :  * flag: true to create a flag column copied up from subplans
    1251             :  * input_tlists: list of tlists for sub-plans of the Append
    1252             :  * refnames_tlist: targetlist to take column names from
    1253             :  *
    1254             :  * The entries in the Append's targetlist should always be simple Vars;
    1255             :  * we just have to make sure they have the right datatypes/typmods/collations.
    1256             :  * The Vars are always generated with varno 0.
    1257             :  *
    1258             :  * XXX a problem with the varno-zero approach is that set_pathtarget_cost_width
    1259             :  * cannot figure out a realistic width for the tlist we make here.  But we
    1260             :  * ought to refactor this code to produce a PathTarget directly, anyway.
    1261             :  */
    1262             : static List *
    1263        1294 : generate_append_tlist(List *colTypes, List *colCollations,
    1264             :                       bool flag,
    1265             :                       List *input_tlists,
    1266             :                       List *refnames_tlist)
    1267             : {
    1268        1294 :     List       *tlist = NIL;
    1269        1294 :     int         resno = 1;
    1270             :     ListCell   *curColType;
    1271             :     ListCell   *curColCollation;
    1272             :     ListCell   *ref_tl_item;
    1273             :     int         colindex;
    1274             :     TargetEntry *tle;
    1275             :     Node       *expr;
    1276             :     ListCell   *tlistl;
    1277             :     int32      *colTypmods;
    1278             : 
    1279             :     /*
    1280             :      * First extract typmods to use.
    1281             :      *
    1282             :      * If the inputs all agree on type and typmod of a particular column, use
    1283             :      * that typmod; else use -1.
    1284             :      */
    1285        1294 :     colTypmods = (int32 *) palloc(list_length(colTypes) * sizeof(int32));
    1286             : 
    1287        4064 :     foreach(tlistl, input_tlists)
    1288             :     {
    1289        2770 :         List       *subtlist = (List *) lfirst(tlistl);
    1290             :         ListCell   *subtlistl;
    1291             : 
    1292        2770 :         curColType = list_head(colTypes);
    1293        2770 :         colindex = 0;
    1294        9020 :         foreach(subtlistl, subtlist)
    1295             :         {
    1296        6250 :             TargetEntry *subtle = (TargetEntry *) lfirst(subtlistl);
    1297             : 
    1298        6250 :             if (subtle->resjunk)
    1299         416 :                 continue;
    1300             :             Assert(curColType != NULL);
    1301        5834 :             if (exprType((Node *) subtle->expr) == lfirst_oid(curColType))
    1302             :             {
    1303             :                 /* If first subplan, copy the typmod; else compare */
    1304        5834 :                 int32       subtypmod = exprTypmod((Node *) subtle->expr);
    1305             : 
    1306        5834 :                 if (tlistl == list_head(input_tlists))
    1307        2512 :                     colTypmods[colindex] = subtypmod;
    1308        3322 :                 else if (subtypmod != colTypmods[colindex])
    1309           8 :                     colTypmods[colindex] = -1;
    1310             :             }
    1311             :             else
    1312             :             {
    1313             :                 /* types disagree, so force typmod to -1 */
    1314           0 :                 colTypmods[colindex] = -1;
    1315             :             }
    1316        5834 :             curColType = lnext(colTypes, curColType);
    1317        5834 :             colindex++;
    1318             :         }
    1319             :         Assert(curColType == NULL);
    1320             :     }
    1321             : 
    1322             :     /*
    1323             :      * Now we can build the tlist for the Append.
    1324             :      */
    1325        1294 :     colindex = 0;
    1326        3806 :     forthree(curColType, colTypes, curColCollation, colCollations,
    1327             :              ref_tl_item, refnames_tlist)
    1328             :     {
    1329        2512 :         Oid         colType = lfirst_oid(curColType);
    1330        2512 :         int32       colTypmod = colTypmods[colindex++];
    1331        2512 :         Oid         colColl = lfirst_oid(curColCollation);
    1332        2512 :         TargetEntry *reftle = (TargetEntry *) lfirst(ref_tl_item);
    1333             : 
    1334             :         Assert(reftle->resno == resno);
    1335             :         Assert(!reftle->resjunk);
    1336        2512 :         expr = (Node *) makeVar(0,
    1337             :                                 resno,
    1338             :                                 colType,
    1339             :                                 colTypmod,
    1340             :                                 colColl,
    1341             :                                 0);
    1342        5024 :         tle = makeTargetEntry((Expr *) expr,
    1343        2512 :                               (AttrNumber) resno++,
    1344        2512 :                               pstrdup(reftle->resname),
    1345             :                               false);
    1346             : 
    1347             :         /*
    1348             :          * By convention, all non-resjunk columns in a setop tree have
    1349             :          * ressortgroupref equal to their resno.  In some cases the ref isn't
    1350             :          * needed, but this is a cleaner way than modifying the tlist later.
    1351             :          */
    1352        2512 :         tle->ressortgroupref = tle->resno;
    1353             : 
    1354        2512 :         tlist = lappend(tlist, tle);
    1355             :     }
    1356             : 
    1357        1294 :     if (flag)
    1358             :     {
    1359             :         /* Add a resjunk flag column */
    1360             :         /* flag value is shown as copied up from subplan */
    1361         208 :         expr = (Node *) makeVar(0,
    1362             :                                 resno,
    1363             :                                 INT4OID,
    1364             :                                 -1,
    1365             :                                 InvalidOid,
    1366             :                                 0);
    1367         416 :         tle = makeTargetEntry((Expr *) expr,
    1368         208 :                               (AttrNumber) resno++,
    1369             :                               pstrdup("flag"),
    1370             :                               true);
    1371         208 :         tlist = lappend(tlist, tle);
    1372             :     }
    1373             : 
    1374        1294 :     pfree(colTypmods);
    1375             : 
    1376        1294 :     return tlist;
    1377             : }
    1378             : 
    1379             : /*
    1380             :  * generate_setop_grouplist
    1381             :  *      Build a SortGroupClause list defining the sort/grouping properties
    1382             :  *      of the setop's output columns.
    1383             :  *
    1384             :  * Parse analysis already determined the properties and built a suitable
    1385             :  * list, except that the entries do not have sortgrouprefs set because
    1386             :  * the parser output representation doesn't include a tlist for each
    1387             :  * setop.  So what we need to do here is copy that list and install
    1388             :  * proper sortgrouprefs into it (copying those from the targetlist).
    1389             :  */
    1390             : static List *
    1391         910 : generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
    1392             : {
    1393         910 :     List       *grouplist = copyObject(op->groupClauses);
    1394             :     ListCell   *lg;
    1395             :     ListCell   *lt;
    1396             : 
    1397         910 :     lg = list_head(grouplist);
    1398        2450 :     foreach(lt, targetlist)
    1399             :     {
    1400        1540 :         TargetEntry *tle = (TargetEntry *) lfirst(lt);
    1401             :         SortGroupClause *sgc;
    1402             : 
    1403        1540 :         if (tle->resjunk)
    1404             :         {
    1405             :             /* resjunk columns should not have sortgrouprefs */
    1406             :             Assert(tle->ressortgroupref == 0);
    1407         208 :             continue;           /* ignore resjunk columns */
    1408             :         }
    1409             : 
    1410             :         /* non-resjunk columns should have sortgroupref = resno */
    1411             :         Assert(tle->ressortgroupref == tle->resno);
    1412             : 
    1413             :         /* non-resjunk columns should have grouping clauses */
    1414             :         Assert(lg != NULL);
    1415        1332 :         sgc = (SortGroupClause *) lfirst(lg);
    1416        1332 :         lg = lnext(grouplist, lg);
    1417             :         Assert(sgc->tleSortGroupRef == 0);
    1418             : 
    1419        1332 :         sgc->tleSortGroupRef = tle->ressortgroupref;
    1420             :     }
    1421             :     Assert(lg == NULL);
    1422         910 :     return grouplist;
    1423             : }

Generated by: LCOV version 1.13