LCOV - code coverage report
Current view: top level - src/backend/optimizer/prep - prepunion.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 419 431 97.2 %
Date: 2025-10-13 23:18:36 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-2025, 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 <math.h>
      27             : 
      28             : #include "access/htup_details.h"
      29             : #include "catalog/pg_type.h"
      30             : #include "miscadmin.h"
      31             : #include "nodes/makefuncs.h"
      32             : #include "nodes/nodeFuncs.h"
      33             : #include "optimizer/cost.h"
      34             : #include "optimizer/pathnode.h"
      35             : #include "optimizer/paths.h"
      36             : #include "optimizer/planner.h"
      37             : #include "optimizer/prep.h"
      38             : #include "optimizer/tlist.h"
      39             : #include "parser/parse_coerce.h"
      40             : #include "utils/selfuncs.h"
      41             : 
      42             : 
      43             : static RelOptInfo *recurse_set_operations(Node *setOp, PlannerInfo *root,
      44             :                                           SetOperationStmt *parentOp,
      45             :                                           List *colTypes, List *colCollations,
      46             :                                           List *refnames_tlist,
      47             :                                           List **pTargetList,
      48             :                                           bool *istrivial_tlist);
      49             : static RelOptInfo *generate_recursion_path(SetOperationStmt *setOp,
      50             :                                            PlannerInfo *root,
      51             :                                            List *refnames_tlist,
      52             :                                            List **pTargetList);
      53             : static void build_setop_child_paths(PlannerInfo *root, RelOptInfo *rel,
      54             :                                     bool trivial_tlist, List *child_tlist,
      55             :                                     List *interesting_pathkeys,
      56             :                                     double *pNumGroups);
      57             : static RelOptInfo *generate_union_paths(SetOperationStmt *op, PlannerInfo *root,
      58             :                                         List *refnames_tlist,
      59             :                                         List **pTargetList);
      60             : static RelOptInfo *generate_nonunion_paths(SetOperationStmt *op, PlannerInfo *root,
      61             :                                            List *refnames_tlist,
      62             :                                            List **pTargetList);
      63             : static List *plan_union_children(PlannerInfo *root,
      64             :                                  SetOperationStmt *top_union,
      65             :                                  List *refnames_tlist,
      66             :                                  List **tlist_list,
      67             :                                  List **istrivial_tlist);
      68             : static void postprocess_setop_rel(PlannerInfo *root, RelOptInfo *rel);
      69             : static List *generate_setop_tlist(List *colTypes, List *colCollations,
      70             :                                   Index varno,
      71             :                                   bool hack_constants,
      72             :                                   List *input_tlist,
      73             :                                   List *refnames_tlist,
      74             :                                   bool *trivial_tlist);
      75             : static List *generate_append_tlist(List *colTypes, List *colCollations,
      76             :                                    List *input_tlists,
      77             :                                    List *refnames_tlist);
      78             : static List *generate_setop_grouplist(SetOperationStmt *op, List *targetlist);
      79             : static PathTarget *create_setop_pathtarget(PlannerInfo *root, List *tlist,
      80             :                                            List *child_pathlist);
      81             : 
      82             : 
      83             : /*
      84             :  * plan_set_operations
      85             :  *
      86             :  *    Plans the queries for a tree of set operations (UNION/INTERSECT/EXCEPT)
      87             :  *
      88             :  * This routine only deals with the setOperations tree of the given query.
      89             :  * Any top-level ORDER BY requested in root->parse->sortClause will be handled
      90             :  * when we return to grouping_planner; likewise for LIMIT.
      91             :  *
      92             :  * What we return is an "upperrel" RelOptInfo containing at least one Path
      93             :  * that implements the set-operation tree.  In addition, root->processed_tlist
      94             :  * receives a targetlist representing the output of the topmost setop node.
      95             :  */
      96             : RelOptInfo *
      97        6098 : plan_set_operations(PlannerInfo *root)
      98             : {
      99        6098 :     Query      *parse = root->parse;
     100        6098 :     SetOperationStmt *topop = castNode(SetOperationStmt, parse->setOperations);
     101             :     Node       *node;
     102             :     RangeTblEntry *leftmostRTE;
     103             :     Query      *leftmostQuery;
     104             :     RelOptInfo *setop_rel;
     105             :     List       *top_tlist;
     106             : 
     107             :     Assert(topop);
     108             : 
     109             :     /* check for unsupported stuff */
     110             :     Assert(parse->jointree->fromlist == NIL);
     111             :     Assert(parse->jointree->quals == NULL);
     112             :     Assert(parse->groupClause == NIL);
     113             :     Assert(parse->havingQual == NULL);
     114             :     Assert(parse->windowClause == NIL);
     115             :     Assert(parse->distinctClause == NIL);
     116             : 
     117             :     /*
     118             :      * In the outer query level, equivalence classes are limited to classes
     119             :      * which define that the top-level target entry is equivalent to the
     120             :      * corresponding child target entry.  There won't be any equivalence class
     121             :      * merging.  Mark that merging is complete to allow us to make pathkeys.
     122             :      */
     123             :     Assert(root->eq_classes == NIL);
     124        6098 :     root->ec_merging_done = true;
     125             : 
     126             :     /*
     127             :      * We'll need to build RelOptInfos for each of the leaf subqueries, which
     128             :      * are RTE_SUBQUERY rangetable entries in this Query.  Prepare the index
     129             :      * arrays for those, and for AppendRelInfos in case they're needed.
     130             :      */
     131        6098 :     setup_simple_rel_arrays(root);
     132             : 
     133             :     /*
     134             :      * Find the leftmost component Query.  We need to use its column names for
     135             :      * all generated tlists (else SELECT INTO won't work right).
     136             :      */
     137        6098 :     node = topop->larg;
     138        9876 :     while (node && IsA(node, SetOperationStmt))
     139        3778 :         node = ((SetOperationStmt *) node)->larg;
     140             :     Assert(node && IsA(node, RangeTblRef));
     141        6098 :     leftmostRTE = root->simple_rte_array[((RangeTblRef *) node)->rtindex];
     142        6098 :     leftmostQuery = leftmostRTE->subquery;
     143             :     Assert(leftmostQuery != NULL);
     144             : 
     145             :     /*
     146             :      * If the topmost node is a recursive union, it needs special processing.
     147             :      */
     148        6098 :     if (root->hasRecursion)
     149             :     {
     150         928 :         setop_rel = generate_recursion_path(topop, root,
     151             :                                             leftmostQuery->targetList,
     152             :                                             &top_tlist);
     153             :     }
     154             :     else
     155             :     {
     156             :         bool        trivial_tlist;
     157             : 
     158             :         /*
     159             :          * Recurse on setOperations tree to generate paths for set ops. The
     160             :          * final output paths should have just the column types shown as the
     161             :          * output from the top-level node.
     162             :          */
     163        5170 :         setop_rel = recurse_set_operations((Node *) topop, root,
     164             :                                            NULL,    /* no parent */
     165             :                                            topop->colTypes, topop->colCollations,
     166             :                                            leftmostQuery->targetList,
     167             :                                            &top_tlist,
     168             :                                            &trivial_tlist);
     169             :     }
     170             : 
     171             :     /* Must return the built tlist into root->processed_tlist. */
     172        6092 :     root->processed_tlist = top_tlist;
     173             : 
     174        6092 :     return setop_rel;
     175             : }
     176             : 
     177             : /*
     178             :  * recurse_set_operations
     179             :  *    Recursively handle one step in a tree of set operations
     180             :  *
     181             :  * setOp: current step (could be a SetOperationStmt or a leaf RangeTblRef)
     182             :  * parentOp: parent step, or NULL if none (but see below)
     183             :  * colTypes: OID list of set-op's result column datatypes
     184             :  * colCollations: OID list of set-op's result column collations
     185             :  * refnames_tlist: targetlist to take column names from
     186             :  *
     187             :  * parentOp should be passed as NULL unless that step is interested in
     188             :  * getting sorted output from this step.  ("Sorted" means "sorted according
     189             :  * to the default btree opclasses of the result column datatypes".)
     190             :  *
     191             :  * Returns a RelOptInfo for the subtree, as well as these output parameters:
     192             :  * *pTargetList: receives the fully-fledged tlist for the subtree's top plan
     193             :  * *istrivial_tlist: true if, and only if, datatypes between parent and child
     194             :  * match.
     195             :  *
     196             :  * If setOp is a leaf node, this function plans the sub-query but does
     197             :  * not populate the pathlist of the returned RelOptInfo.  The caller will
     198             :  * generate SubqueryScan paths using useful path(s) of the subquery (see
     199             :  * build_setop_child_paths).  But this function does build the paths for
     200             :  * set-operation nodes.
     201             :  *
     202             :  * The pTargetList output parameter is mostly redundant with the pathtarget
     203             :  * of the returned RelOptInfo, but for the moment we need it because much of
     204             :  * the logic in this file depends on flag columns being marked resjunk.
     205             :  * XXX Now that there are no flag columns and hence no resjunk columns, we
     206             :  * could probably refactor this file to deal only in pathtargets.
     207             :  *
     208             :  * We don't have to care about typmods here: the only allowed difference
     209             :  * between set-op input and output typmods is input is a specific typmod
     210             :  * and output is -1, and that does not require a coercion.
     211             :  */
     212             : static RelOptInfo *
     213       21366 : recurse_set_operations(Node *setOp, PlannerInfo *root,
     214             :                        SetOperationStmt *parentOp,
     215             :                        List *colTypes, List *colCollations,
     216             :                        List *refnames_tlist,
     217             :                        List **pTargetList,
     218             :                        bool *istrivial_tlist)
     219             : {
     220             :     RelOptInfo *rel;
     221             : 
     222       21366 :     *istrivial_tlist = true;    /* for now */
     223             : 
     224             :     /* Guard against stack overflow due to overly complex setop nests */
     225       21366 :     check_stack_depth();
     226             : 
     227       21366 :     if (IsA(setOp, RangeTblRef))
     228             :     {
     229       16040 :         RangeTblRef *rtr = (RangeTblRef *) setOp;
     230       16040 :         RangeTblEntry *rte = root->simple_rte_array[rtr->rtindex];
     231       16040 :         Query      *subquery = rte->subquery;
     232             :         PlannerInfo *subroot;
     233             :         List       *tlist;
     234             :         bool        trivial_tlist;
     235             :         char       *plan_name;
     236             : 
     237             :         Assert(subquery != NULL);
     238             : 
     239             :         /* Build a RelOptInfo for this leaf subquery. */
     240       16040 :         rel = build_simple_rel(root, rtr->rtindex, NULL);
     241             : 
     242             :         /* plan_params should not be in use in current query level */
     243             :         Assert(root->plan_params == NIL);
     244             : 
     245             :         /*
     246             :          * Generate a subroot and Paths for the subquery.  If we have a
     247             :          * parentOp, pass that down to encourage subquery_planner to consider
     248             :          * suitably-sorted Paths.
     249             :          */
     250       16040 :         plan_name = choose_plan_name(root->glob, "setop", true);
     251       16040 :         subroot = rel->subroot = subquery_planner(root->glob, subquery,
     252             :                                                   plan_name, root,
     253             :                                                   false, root->tuple_fraction,
     254             :                                                   parentOp);
     255             : 
     256             :         /*
     257             :          * It should not be possible for the primitive query to contain any
     258             :          * cross-references to other primitive queries in the setop tree.
     259             :          */
     260       16040 :         if (root->plan_params)
     261           0 :             elog(ERROR, "unexpected outer reference in set operation subquery");
     262             : 
     263             :         /* Figure out the appropriate target list for this subquery. */
     264       16040 :         tlist = generate_setop_tlist(colTypes, colCollations,
     265       16040 :                                      rtr->rtindex,
     266             :                                      true,
     267             :                                      subroot->processed_tlist,
     268             :                                      refnames_tlist,
     269             :                                      &trivial_tlist);
     270       16040 :         rel->reltarget = create_pathtarget(root, tlist);
     271             : 
     272             :         /* Return the fully-fledged tlist to caller, too */
     273       16040 :         *pTargetList = tlist;
     274       16040 :         *istrivial_tlist = trivial_tlist;
     275             :     }
     276        5326 :     else if (IsA(setOp, SetOperationStmt))
     277             :     {
     278        5326 :         SetOperationStmt *op = (SetOperationStmt *) setOp;
     279             : 
     280             :         /* UNIONs are much different from INTERSECT/EXCEPT */
     281        5326 :         if (op->op == SETOP_UNION)
     282        4634 :             rel = generate_union_paths(op, root,
     283             :                                        refnames_tlist,
     284             :                                        pTargetList);
     285             :         else
     286         692 :             rel = generate_nonunion_paths(op, root,
     287             :                                           refnames_tlist,
     288             :                                           pTargetList);
     289             : 
     290             :         /*
     291             :          * If necessary, add a Result node to project the caller-requested
     292             :          * output columns.
     293             :          *
     294             :          * XXX you don't really want to know about this: setrefs.c will apply
     295             :          * fix_upper_expr() to the Result node's tlist. This would fail if the
     296             :          * Vars generated by generate_setop_tlist() were not exactly equal()
     297             :          * to the corresponding tlist entries of the subplan. However, since
     298             :          * the subplan was generated by generate_union_paths() or
     299             :          * generate_nonunion_paths(), and hence its tlist was generated by
     300             :          * generate_append_tlist() or generate_setop_tlist(), this will work.
     301             :          * We just tell generate_setop_tlist() to use varno 0.
     302             :          */
     303        5326 :         if (!tlist_same_datatypes(*pTargetList, colTypes, false) ||
     304        5314 :             !tlist_same_collations(*pTargetList, colCollations, false))
     305             :         {
     306             :             PathTarget *target;
     307             :             bool        trivial_tlist;
     308             :             ListCell   *lc;
     309             : 
     310          12 :             *pTargetList = generate_setop_tlist(colTypes, colCollations,
     311             :                                                 0,
     312             :                                                 false,
     313             :                                                 *pTargetList,
     314             :                                                 refnames_tlist,
     315             :                                                 &trivial_tlist);
     316          12 :             *istrivial_tlist = trivial_tlist;
     317          12 :             target = create_pathtarget(root, *pTargetList);
     318             : 
     319             :             /* Apply projection to each path */
     320          24 :             foreach(lc, rel->pathlist)
     321             :             {
     322          12 :                 Path       *subpath = (Path *) lfirst(lc);
     323             :                 Path       *path;
     324             : 
     325             :                 Assert(subpath->param_info == NULL);
     326          12 :                 path = apply_projection_to_path(root, subpath->parent,
     327             :                                                 subpath, target);
     328             :                 /* If we had to add a Result, path is different from subpath */
     329          12 :                 if (path != subpath)
     330          12 :                     lfirst(lc) = path;
     331             :             }
     332             : 
     333             :             /* Apply projection to each partial path */
     334          12 :             foreach(lc, rel->partial_pathlist)
     335             :             {
     336           0 :                 Path       *subpath = (Path *) lfirst(lc);
     337             :                 Path       *path;
     338             : 
     339             :                 Assert(subpath->param_info == NULL);
     340             : 
     341             :                 /* avoid apply_projection_to_path, in case of multiple refs */
     342           0 :                 path = (Path *) create_projection_path(root, subpath->parent,
     343             :                                                        subpath, target);
     344           0 :                 lfirst(lc) = path;
     345             :             }
     346             :         }
     347        5326 :         postprocess_setop_rel(root, rel);
     348             :     }
     349             :     else
     350             :     {
     351           0 :         elog(ERROR, "unrecognized node type: %d",
     352             :              (int) nodeTag(setOp));
     353             :         *pTargetList = NIL;
     354             :         rel = NULL;             /* keep compiler quiet */
     355             :     }
     356             : 
     357       21366 :     return rel;
     358             : }
     359             : 
     360             : /*
     361             :  * Generate paths for a recursive UNION node
     362             :  */
     363             : static RelOptInfo *
     364         928 : generate_recursion_path(SetOperationStmt *setOp, PlannerInfo *root,
     365             :                         List *refnames_tlist,
     366             :                         List **pTargetList)
     367             : {
     368             :     RelOptInfo *result_rel;
     369             :     Path       *path;
     370             :     RelOptInfo *lrel,
     371             :                *rrel;
     372             :     Path       *lpath;
     373             :     Path       *rpath;
     374             :     List       *lpath_tlist;
     375             :     bool        lpath_trivial_tlist;
     376             :     List       *rpath_tlist;
     377             :     bool        rpath_trivial_tlist;
     378             :     List       *tlist;
     379             :     List       *groupList;
     380             :     double      dNumGroups;
     381             : 
     382             :     /* Parser should have rejected other cases */
     383         928 :     if (setOp->op != SETOP_UNION)
     384           0 :         elog(ERROR, "only UNION queries can be recursive");
     385             :     /* Worktable ID should be assigned */
     386             :     Assert(root->wt_param_id >= 0);
     387             : 
     388             :     /*
     389             :      * Unlike a regular UNION node, process the left and right inputs
     390             :      * separately without any intention of combining them into one Append.
     391             :      */
     392         928 :     lrel = recurse_set_operations(setOp->larg, root,
     393             :                                   NULL, /* no value in sorted results */
     394             :                                   setOp->colTypes, setOp->colCollations,
     395             :                                   refnames_tlist,
     396             :                                   &lpath_tlist,
     397             :                                   &lpath_trivial_tlist);
     398         928 :     if (lrel->rtekind == RTE_SUBQUERY)
     399         928 :         build_setop_child_paths(root, lrel, lpath_trivial_tlist, lpath_tlist,
     400             :                                 NIL, NULL);
     401         928 :     lpath = lrel->cheapest_total_path;
     402             :     /* The right path will want to look at the left one ... */
     403         928 :     root->non_recursive_path = lpath;
     404         928 :     rrel = recurse_set_operations(setOp->rarg, root,
     405             :                                   NULL, /* no value in sorted results */
     406             :                                   setOp->colTypes, setOp->colCollations,
     407             :                                   refnames_tlist,
     408             :                                   &rpath_tlist,
     409             :                                   &rpath_trivial_tlist);
     410         928 :     if (rrel->rtekind == RTE_SUBQUERY)
     411         922 :         build_setop_child_paths(root, rrel, rpath_trivial_tlist, rpath_tlist,
     412             :                                 NIL, NULL);
     413         928 :     rpath = rrel->cheapest_total_path;
     414         928 :     root->non_recursive_path = NULL;
     415             : 
     416             :     /*
     417             :      * Generate tlist for RecursiveUnion path node --- same as in Append cases
     418             :      */
     419         928 :     tlist = generate_append_tlist(setOp->colTypes, setOp->colCollations,
     420         928 :                                   list_make2(lpath_tlist, rpath_tlist),
     421             :                                   refnames_tlist);
     422             : 
     423         928 :     *pTargetList = tlist;
     424             : 
     425             :     /* Build result relation. */
     426         928 :     result_rel = fetch_upper_rel(root, UPPERREL_SETOP,
     427         928 :                                  bms_union(lrel->relids, rrel->relids));
     428         928 :     result_rel->reltarget = create_pathtarget(root, tlist);
     429             : 
     430             :     /*
     431             :      * If UNION, identify the grouping operators
     432             :      */
     433         928 :     if (setOp->all)
     434             :     {
     435         548 :         groupList = NIL;
     436         548 :         dNumGroups = 0;
     437             :     }
     438             :     else
     439             :     {
     440             :         /* Identify the grouping semantics */
     441         380 :         groupList = generate_setop_grouplist(setOp, tlist);
     442             : 
     443             :         /* We only support hashing here */
     444         380 :         if (!grouping_is_hashable(groupList))
     445           6 :             ereport(ERROR,
     446             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     447             :                      errmsg("could not implement recursive UNION"),
     448             :                      errdetail("All column datatypes must be hashable.")));
     449             : 
     450             :         /*
     451             :          * For the moment, take the number of distinct groups as equal to the
     452             :          * total input size, ie, the worst case.
     453             :          */
     454         374 :         dNumGroups = lpath->rows + rpath->rows * 10;
     455             :     }
     456             : 
     457             :     /*
     458             :      * And make the path node.
     459             :      */
     460         922 :     path = (Path *) create_recursiveunion_path(root,
     461             :                                                result_rel,
     462             :                                                lpath,
     463             :                                                rpath,
     464         922 :                                                result_rel->reltarget,
     465             :                                                groupList,
     466             :                                                root->wt_param_id,
     467             :                                                dNumGroups);
     468             : 
     469         922 :     add_path(result_rel, path);
     470         922 :     postprocess_setop_rel(root, result_rel);
     471         922 :     return result_rel;
     472             : }
     473             : 
     474             : /*
     475             :  * build_setop_child_paths
     476             :  *      Build paths for the set op child relation denoted by 'rel'.
     477             :  *
     478             :  * 'rel' is an RTE_SUBQUERY relation.  We have already generated paths within
     479             :  * the subquery's subroot; the task here is to create SubqueryScan paths for
     480             :  * 'rel', representing scans of the useful subquery paths.
     481             :  *
     482             :  * interesting_pathkeys: if not NIL, also include paths that suit these
     483             :  * pathkeys, sorting any unsorted paths as required.
     484             :  * *pNumGroups: if not NULL, we estimate the number of distinct groups
     485             :  * in the result, and store it there.
     486             :  */
     487             : static void
     488       16040 : build_setop_child_paths(PlannerInfo *root, RelOptInfo *rel,
     489             :                         bool trivial_tlist, List *child_tlist,
     490             :                         List *interesting_pathkeys, double *pNumGroups)
     491             : {
     492             :     RelOptInfo *final_rel;
     493       16040 :     List       *setop_pathkeys = rel->subroot->setop_pathkeys;
     494             :     ListCell   *lc;
     495             : 
     496             :     /* it can't be a set op child rel if it's not a subquery */
     497             :     Assert(rel->rtekind == RTE_SUBQUERY);
     498             : 
     499             :     /* when sorting is needed, add child rel equivalences */
     500       16040 :     if (interesting_pathkeys != NIL)
     501       12098 :         add_setop_child_rel_equivalences(root,
     502             :                                          rel,
     503             :                                          child_tlist,
     504             :                                          interesting_pathkeys);
     505             : 
     506             :     /*
     507             :      * Mark rel with estimated output rows, width, etc.  Note that we have to
     508             :      * do this before generating outer-query paths, else cost_subqueryscan is
     509             :      * not happy.
     510             :      */
     511       16040 :     set_subquery_size_estimates(root, rel);
     512             : 
     513             :     /*
     514             :      * Since we may want to add a partial path to this relation, we must set
     515             :      * its consider_parallel flag correctly.
     516             :      */
     517       16040 :     final_rel = fetch_upper_rel(rel->subroot, UPPERREL_FINAL, NULL);
     518       16040 :     rel->consider_parallel = final_rel->consider_parallel;
     519             : 
     520             :     /* Generate subquery scan paths for any interesting path in final_rel */
     521       41510 :     foreach(lc, final_rel->pathlist)
     522             :     {
     523       25470 :         Path       *subpath = (Path *) lfirst(lc);
     524             :         List       *pathkeys;
     525       25470 :         Path       *cheapest_input_path = final_rel->cheapest_total_path;
     526             :         bool        is_sorted;
     527             :         int         presorted_keys;
     528             : 
     529             :         /* If the input rel is dummy, propagate that to this query level */
     530       25470 :         if (is_dummy_rel(final_rel))
     531             :         {
     532          82 :             mark_dummy_rel(rel);
     533        4064 :             continue;
     534             :         }
     535             : 
     536             :         /*
     537             :          * Include the cheapest path as-is so that the set operation can be
     538             :          * cheaply implemented using a method which does not require the input
     539             :          * to be sorted.
     540             :          */
     541       25388 :         if (subpath == cheapest_input_path)
     542             :         {
     543             :             /* Convert subpath's pathkeys to outer representation */
     544       15958 :             pathkeys = convert_subquery_pathkeys(root, rel, subpath->pathkeys,
     545             :                                                  make_tlist_from_pathtarget(subpath->pathtarget));
     546             : 
     547             :             /* Generate outer path using this subpath */
     548       15958 :             add_path(rel, (Path *) create_subqueryscan_path(root,
     549             :                                                             rel,
     550             :                                                             subpath,
     551             :                                                             trivial_tlist,
     552             :                                                             pathkeys,
     553             :                                                             NULL));
     554             :         }
     555             : 
     556             :         /* skip dealing with sorted paths if the setop doesn't need them */
     557       25388 :         if (interesting_pathkeys == NIL)
     558        3970 :             continue;
     559             : 
     560             :         /*
     561             :          * Create paths to suit final sort order required for setop_pathkeys.
     562             :          * Here we'll sort the cheapest input path (if not sorted already) and
     563             :          * incremental sort any paths which are partially sorted.
     564             :          */
     565       21418 :         is_sorted = pathkeys_count_contained_in(setop_pathkeys,
     566             :                                                 subpath->pathkeys,
     567             :                                                 &presorted_keys);
     568             : 
     569       21418 :         if (!is_sorted)
     570             :         {
     571       14078 :             double      limittuples = rel->subroot->limit_tuples;
     572             : 
     573             :             /*
     574             :              * Try at least sorting the cheapest path and also try
     575             :              * incrementally sorting any path which is partially sorted
     576             :              * already (no need to deal with paths which have presorted keys
     577             :              * when incremental sort is disabled unless it's the cheapest
     578             :              * input path).
     579             :              */
     580       14078 :             if (subpath != cheapest_input_path &&
     581        3126 :                 (presorted_keys == 0 || !enable_incremental_sort))
     582          12 :                 continue;
     583             : 
     584             :             /*
     585             :              * We've no need to consider both a sort and incremental sort.
     586             :              * We'll just do a sort if there are no presorted keys and an
     587             :              * incremental sort when there are presorted keys.
     588             :              */
     589       14066 :             if (presorted_keys == 0 || !enable_incremental_sort)
     590       10904 :                 subpath = (Path *) create_sort_path(rel->subroot,
     591             :                                                     final_rel,
     592             :                                                     subpath,
     593             :                                                     setop_pathkeys,
     594             :                                                     limittuples);
     595             :             else
     596        3162 :                 subpath = (Path *) create_incremental_sort_path(rel->subroot,
     597             :                                                                 final_rel,
     598             :                                                                 subpath,
     599             :                                                                 setop_pathkeys,
     600             :                                                                 presorted_keys,
     601             :                                                                 limittuples);
     602             :         }
     603             : 
     604             :         /*
     605             :          * subpath is now sorted, so add it to the pathlist.  We already added
     606             :          * the cheapest_input_path above, so don't add it again unless we just
     607             :          * sorted it.
     608             :          */
     609       21406 :         if (subpath != cheapest_input_path)
     610             :         {
     611             :             /* Convert subpath's pathkeys to outer representation */
     612       20326 :             pathkeys = convert_subquery_pathkeys(root, rel, subpath->pathkeys,
     613             :                                                  make_tlist_from_pathtarget(subpath->pathtarget));
     614             : 
     615             :             /* Generate outer path using this subpath */
     616       20326 :             add_path(rel, (Path *) create_subqueryscan_path(root,
     617             :                                                             rel,
     618             :                                                             subpath,
     619             :                                                             trivial_tlist,
     620             :                                                             pathkeys,
     621             :                                                             NULL));
     622             :         }
     623             :     }
     624             : 
     625             :     /* if consider_parallel is false, there should be no partial paths */
     626             :     Assert(final_rel->consider_parallel ||
     627             :            final_rel->partial_pathlist == NIL);
     628             : 
     629             :     /*
     630             :      * If we have a partial path for the child relation, we can use that to
     631             :      * build a partial path for this relation.  But there's no point in
     632             :      * considering any path but the cheapest.
     633             :      */
     634       16040 :     if (rel->consider_parallel && bms_is_empty(rel->lateral_relids) &&
     635       11116 :         final_rel->partial_pathlist != NIL)
     636             :     {
     637             :         Path       *partial_subpath;
     638             :         Path       *partial_path;
     639             : 
     640          12 :         partial_subpath = linitial(final_rel->partial_pathlist);
     641             :         partial_path = (Path *)
     642          12 :             create_subqueryscan_path(root, rel, partial_subpath,
     643             :                                      trivial_tlist,
     644             :                                      NIL, NULL);
     645          12 :         add_partial_path(rel, partial_path);
     646             :     }
     647             : 
     648       16040 :     postprocess_setop_rel(root, rel);
     649             : 
     650             :     /*
     651             :      * Estimate number of groups if caller wants it.  If the subquery used
     652             :      * grouping or aggregation, its output is probably mostly unique anyway;
     653             :      * otherwise do statistical estimation.
     654             :      *
     655             :      * XXX you don't really want to know about this: we do the estimation
     656             :      * using the subroot->parse's original targetlist expressions, not the
     657             :      * subroot->processed_tlist which might seem more appropriate.  The reason
     658             :      * is that if the subquery is itself a setop, it may return a
     659             :      * processed_tlist containing "varno 0" Vars generated by
     660             :      * generate_append_tlist, and those would confuse estimate_num_groups
     661             :      * mightily.  We ought to get rid of the "varno 0" hack, but that requires
     662             :      * a redesign of the parsetree representation of setops, so that there can
     663             :      * be an RTE corresponding to each setop's output. Note, we use this not
     664             :      * subquery's targetlist but subroot->parse's targetlist, because it was
     665             :      * revised by self-join removal.  subquery's targetlist might contain the
     666             :      * references to the removed relids.
     667             :      */
     668       16040 :     if (pNumGroups)
     669             :     {
     670        1354 :         PlannerInfo *subroot = rel->subroot;
     671        1354 :         Query      *subquery = subroot->parse;
     672             : 
     673        1354 :         if (subquery->groupClause || subquery->groupingSets ||
     674        1354 :             subquery->distinctClause || subroot->hasHavingQual ||
     675        1342 :             subquery->hasAggs)
     676          12 :             *pNumGroups = rel->cheapest_total_path->rows;
     677             :         else
     678        1342 :             *pNumGroups = estimate_num_groups(subroot,
     679        1342 :                                               get_tlist_exprs(subroot->parse->targetList, false),
     680        1342 :                                               rel->cheapest_total_path->rows,
     681             :                                               NULL,
     682             :                                               NULL);
     683             :     }
     684       16040 : }
     685             : 
     686             : /*
     687             :  * Generate paths for a UNION or UNION ALL node
     688             :  */
     689             : static RelOptInfo *
     690        4634 : generate_union_paths(SetOperationStmt *op, PlannerInfo *root,
     691             :                      List *refnames_tlist,
     692             :                      List **pTargetList)
     693             : {
     694        4634 :     Relids      relids = NULL;
     695             :     RelOptInfo *result_rel;
     696             :     ListCell   *lc;
     697             :     ListCell   *lc2;
     698             :     ListCell   *lc3;
     699        4634 :     List       *cheapest_pathlist = NIL;
     700        4634 :     List       *ordered_pathlist = NIL;
     701        4634 :     List       *partial_pathlist = NIL;
     702        4634 :     bool        partial_paths_valid = true;
     703        4634 :     bool        consider_parallel = true;
     704             :     List       *rellist;
     705             :     List       *tlist_list;
     706             :     List       *trivial_tlist_list;
     707             :     List       *tlist;
     708        4634 :     List       *groupList = NIL;
     709             :     Path       *apath;
     710        4634 :     Path       *gpath = NULL;
     711        4634 :     bool        try_sorted = false;
     712        4634 :     List       *union_pathkeys = NIL;
     713             : 
     714             :     /*
     715             :      * If any of my children are identical UNION nodes (same op, all-flag, and
     716             :      * colTypes/colCollations) then they can be merged into this node so that
     717             :      * we generate only one Append/MergeAppend and unique-ification for the
     718             :      * lot.  Recurse to find such nodes.
     719             :      */
     720        4634 :     rellist = plan_union_children(root,
     721             :                                   op,
     722             :                                   refnames_tlist,
     723             :                                   &tlist_list,
     724             :                                   &trivial_tlist_list);
     725             : 
     726             :     /*
     727             :      * Generate tlist for Append/MergeAppend plan node.
     728             :      *
     729             :      * The tlist for an Append plan isn't important as far as the Append is
     730             :      * concerned, but we must make it look real anyway for the benefit of the
     731             :      * next plan level up.
     732             :      */
     733        4634 :     tlist = generate_append_tlist(op->colTypes, op->colCollations,
     734             :                                   tlist_list, refnames_tlist);
     735        4634 :     *pTargetList = tlist;
     736             : 
     737             :     /* For UNIONs (not UNION ALL), try sorting, if sorting is possible */
     738        4634 :     if (!op->all)
     739             :     {
     740             :         /* Identify the grouping semantics */
     741        3930 :         groupList = generate_setop_grouplist(op, tlist);
     742             : 
     743        3930 :         if (grouping_is_sortable(op->groupClauses))
     744             :         {
     745        3828 :             try_sorted = true;
     746             :             /* Determine the pathkeys for sorting by the whole target list */
     747        3828 :             union_pathkeys = make_pathkeys_for_sortclauses(root, groupList,
     748             :                                                            tlist);
     749             : 
     750        3828 :             root->query_pathkeys = union_pathkeys;
     751             :         }
     752             :     }
     753             : 
     754             :     /*
     755             :      * Now that we've got the append target list, we can build the union child
     756             :      * paths.
     757             :      */
     758       17590 :     forthree(lc, rellist, lc2, trivial_tlist_list, lc3, tlist_list)
     759             :     {
     760       12956 :         RelOptInfo *rel = lfirst(lc);
     761       12956 :         bool        trivial_tlist = lfirst_int(lc2);
     762       12956 :         List       *child_tlist = lfirst_node(List, lc3);
     763             : 
     764             :         /* only build paths for the union children */
     765       12956 :         if (rel->rtekind == RTE_SUBQUERY)
     766       12836 :             build_setop_child_paths(root, rel, trivial_tlist, child_tlist,
     767             :                                     union_pathkeys, NULL);
     768             :     }
     769             : 
     770             :     /* Build path lists and relid set. */
     771       17590 :     foreach(lc, rellist)
     772             :     {
     773       12956 :         RelOptInfo *rel = lfirst(lc);
     774             :         Path       *ordered_path;
     775             : 
     776             :         /* Skip any UNION children that are proven not to yield any rows */
     777       12956 :         if (is_dummy_rel(rel))
     778          46 :             continue;
     779             : 
     780       12910 :         cheapest_pathlist = lappend(cheapest_pathlist,
     781       12910 :                                     rel->cheapest_total_path);
     782             : 
     783       12910 :         if (try_sorted)
     784             :         {
     785        4150 :             ordered_path = get_cheapest_path_for_pathkeys(rel->pathlist,
     786             :                                                           union_pathkeys,
     787             :                                                           NULL,
     788             :                                                           TOTAL_COST,
     789             :                                                           false);
     790             : 
     791        4150 :             if (ordered_path != NULL)
     792         632 :                 ordered_pathlist = lappend(ordered_pathlist, ordered_path);
     793             :             else
     794             :             {
     795             :                 /*
     796             :                  * If we can't find a sorted path, just give up trying to
     797             :                  * generate a list of correctly sorted child paths.  This can
     798             :                  * happen when type coercion was added to the targetlist due
     799             :                  * to mismatching types from the union children.
     800             :                  */
     801        3518 :                 try_sorted = false;
     802             :             }
     803             :         }
     804             : 
     805       12910 :         if (consider_parallel)
     806             :         {
     807        9296 :             if (!rel->consider_parallel)
     808             :             {
     809        3374 :                 consider_parallel = false;
     810        3374 :                 partial_paths_valid = false;
     811             :             }
     812        5922 :             else if (rel->partial_pathlist == NIL)
     813        5910 :                 partial_paths_valid = false;
     814             :             else
     815          12 :                 partial_pathlist = lappend(partial_pathlist,
     816          12 :                                            linitial(rel->partial_pathlist));
     817             :         }
     818             : 
     819       12910 :         relids = bms_add_members(relids, rel->relids);
     820             :     }
     821             : 
     822             :     /* Build result relation. */
     823        4634 :     result_rel = fetch_upper_rel(root, UPPERREL_SETOP, relids);
     824        4634 :     result_rel->reltarget = create_setop_pathtarget(root, tlist,
     825             :                                                     cheapest_pathlist);
     826        4634 :     result_rel->consider_parallel = consider_parallel;
     827        4634 :     result_rel->consider_startup = (root->tuple_fraction > 0);
     828             : 
     829             :     /* If all UNION children were dummy rels, make the resulting rel dummy */
     830        4634 :     if (cheapest_pathlist == NIL)
     831             :     {
     832           6 :         mark_dummy_rel(result_rel);
     833             : 
     834           6 :         return result_rel;
     835             :     }
     836             : 
     837             :     /*
     838             :      * Append the child results together using the cheapest paths from each
     839             :      * union child.
     840             :      */
     841        4628 :     apath = (Path *) create_append_path(root, result_rel, cheapest_pathlist,
     842             :                                         NIL, NIL, NULL, 0, false, -1);
     843             : 
     844             :     /*
     845             :      * Estimate number of groups.  For now we just assume the output is unique
     846             :      * --- this is certainly true for the UNION case, and we want worst-case
     847             :      * estimates anyway.
     848             :      */
     849        4628 :     result_rel->rows = apath->rows;
     850             : 
     851             :     /*
     852             :      * Now consider doing the same thing using the partial paths plus Append
     853             :      * plus Gather.
     854             :      */
     855        4628 :     if (partial_paths_valid)
     856             :     {
     857             :         Path       *papath;
     858           6 :         int         parallel_workers = 0;
     859             : 
     860             :         /* Find the highest number of workers requested for any subpath. */
     861          18 :         foreach(lc, partial_pathlist)
     862             :         {
     863          12 :             Path       *subpath = lfirst(lc);
     864             : 
     865          12 :             parallel_workers = Max(parallel_workers,
     866             :                                    subpath->parallel_workers);
     867             :         }
     868             :         Assert(parallel_workers > 0);
     869             : 
     870             :         /*
     871             :          * If the use of parallel append is permitted, always request at least
     872             :          * log2(# of children) paths.  We assume it can be useful to have
     873             :          * extra workers in this case because they will be spread out across
     874             :          * the children.  The precise formula is just a guess; see
     875             :          * add_paths_to_append_rel.
     876             :          */
     877           6 :         if (enable_parallel_append)
     878             :         {
     879           6 :             parallel_workers = Max(parallel_workers,
     880             :                                    pg_leftmost_one_pos32(list_length(partial_pathlist)) + 1);
     881           6 :             parallel_workers = Min(parallel_workers,
     882             :                                    max_parallel_workers_per_gather);
     883             :         }
     884             :         Assert(parallel_workers > 0);
     885             : 
     886             :         papath = (Path *)
     887           6 :             create_append_path(root, result_rel, NIL, partial_pathlist,
     888             :                                NIL, NULL, parallel_workers,
     889             :                                enable_parallel_append, -1);
     890             :         gpath = (Path *)
     891           6 :             create_gather_path(root, result_rel, papath,
     892           6 :                                result_rel->reltarget, NULL, NULL);
     893             :     }
     894             : 
     895        4628 :     if (!op->all)
     896             :     {
     897             :         double      dNumGroups;
     898        3924 :         bool        can_sort = grouping_is_sortable(groupList);
     899        3924 :         bool        can_hash = grouping_is_hashable(groupList);
     900             : 
     901        3924 :         if (list_length(cheapest_pathlist) == 1)
     902             :         {
     903          12 :             Path       *path = linitial(cheapest_pathlist);
     904             : 
     905             :             /*
     906             :              * In the case where only one union child remains due to the
     907             :              * detection of one or more dummy union children, obtain an
     908             :              * estimate on the surviving child directly.
     909             :              */
     910          12 :             dNumGroups = estimate_num_groups(root,
     911          12 :                                              path->pathtarget->exprs,
     912             :                                              path->rows,
     913             :                                              NULL,
     914             :                                              NULL);
     915             :         }
     916             :         else
     917             :         {
     918             :             /*
     919             :              * Otherwise, for the moment, take the number of distinct groups
     920             :              * as equal to the total input size, i.e., the worst case.  This
     921             :              * is too conservative, but it's not clear how to get a decent
     922             :              * estimate of the true size.  One should note as well the
     923             :              * propensity of novices to write UNION rather than UNION ALL even
     924             :              * when they don't expect any duplicates...
     925             :              */
     926        3912 :             dNumGroups = apath->rows;
     927             :         }
     928             : 
     929        3924 :         if (can_hash)
     930             :         {
     931             :             Path       *path;
     932             : 
     933             :             /*
     934             :              * Try a hash aggregate plan on 'apath'.  This is the cheapest
     935             :              * available path containing each append child.
     936             :              */
     937        3852 :             path = (Path *) create_agg_path(root,
     938             :                                             result_rel,
     939             :                                             apath,
     940        3852 :                                             result_rel->reltarget,
     941             :                                             AGG_HASHED,
     942             :                                             AGGSPLIT_SIMPLE,
     943             :                                             groupList,
     944             :                                             NIL,
     945             :                                             NULL,
     946             :                                             dNumGroups);
     947        3852 :             add_path(result_rel, path);
     948             : 
     949             :             /* Try hash aggregate on the Gather path, if valid */
     950        3852 :             if (gpath != NULL)
     951             :             {
     952             :                 /* Hashed aggregate plan --- no sort needed */
     953           6 :                 path = (Path *) create_agg_path(root,
     954             :                                                 result_rel,
     955             :                                                 gpath,
     956           6 :                                                 result_rel->reltarget,
     957             :                                                 AGG_HASHED,
     958             :                                                 AGGSPLIT_SIMPLE,
     959             :                                                 groupList,
     960             :                                                 NIL,
     961             :                                                 NULL,
     962             :                                                 dNumGroups);
     963           6 :                 add_path(result_rel, path);
     964             :             }
     965             :         }
     966             : 
     967        3924 :         if (can_sort)
     968             :         {
     969        3822 :             Path       *path = apath;
     970             : 
     971             :             /* Try Sort -> Unique on the Append path */
     972        3822 :             if (groupList != NIL)
     973        3792 :                 path = (Path *) create_sort_path(root, result_rel, path,
     974             :                                                  make_pathkeys_for_sortclauses(root, groupList, tlist),
     975             :                                                  -1.0);
     976             : 
     977        3822 :             path = (Path *) create_unique_path(root,
     978             :                                                result_rel,
     979             :                                                path,
     980        3822 :                                                list_length(path->pathkeys),
     981             :                                                dNumGroups);
     982             : 
     983        3822 :             add_path(result_rel, path);
     984             : 
     985             :             /* Try Sort -> Unique on the Gather path, if set */
     986        3822 :             if (gpath != NULL)
     987             :             {
     988           6 :                 path = gpath;
     989             : 
     990           6 :                 path = (Path *) create_sort_path(root, result_rel, path,
     991             :                                                  make_pathkeys_for_sortclauses(root, groupList, tlist),
     992             :                                                  -1.0);
     993             : 
     994           6 :                 path = (Path *) create_unique_path(root,
     995             :                                                    result_rel,
     996             :                                                    path,
     997           6 :                                                    list_length(path->pathkeys),
     998             :                                                    dNumGroups);
     999           6 :                 add_path(result_rel, path);
    1000             :             }
    1001             :         }
    1002             : 
    1003             :         /*
    1004             :          * Try making a MergeAppend path if we managed to find a path with the
    1005             :          * correct pathkeys in each union child query.
    1006             :          */
    1007        3924 :         if (try_sorted && groupList != NIL)
    1008             :         {
    1009             :             Path       *path;
    1010             : 
    1011         274 :             path = (Path *) create_merge_append_path(root,
    1012             :                                                      result_rel,
    1013             :                                                      ordered_pathlist,
    1014             :                                                      union_pathkeys,
    1015             :                                                      NULL);
    1016             : 
    1017             :             /* and make the MergeAppend unique */
    1018         274 :             path = (Path *) create_unique_path(root,
    1019             :                                                result_rel,
    1020             :                                                path,
    1021             :                                                list_length(tlist),
    1022             :                                                dNumGroups);
    1023             : 
    1024         274 :             add_path(result_rel, path);
    1025             :         }
    1026             :     }
    1027             :     else
    1028             :     {
    1029             :         /* UNION ALL */
    1030         704 :         add_path(result_rel, apath);
    1031             : 
    1032         704 :         if (gpath != NULL)
    1033           0 :             add_path(result_rel, gpath);
    1034             :     }
    1035             : 
    1036        4628 :     return result_rel;
    1037             : }
    1038             : 
    1039             : /*
    1040             :  * Generate paths for an INTERSECT, INTERSECT ALL, EXCEPT, or EXCEPT ALL node
    1041             :  */
    1042             : static RelOptInfo *
    1043         692 : generate_nonunion_paths(SetOperationStmt *op, PlannerInfo *root,
    1044             :                         List *refnames_tlist,
    1045             :                         List **pTargetList)
    1046             : {
    1047             :     RelOptInfo *result_rel;
    1048             :     RelOptInfo *lrel,
    1049             :                *rrel;
    1050         692 :     double      save_fraction = root->tuple_fraction;
    1051             :     Path       *lpath,
    1052             :                *rpath,
    1053             :                *path;
    1054             :     List       *lpath_tlist,
    1055             :                *rpath_tlist,
    1056             :                *tlist,
    1057             :                *groupList;
    1058             :     bool        lpath_trivial_tlist,
    1059             :                 rpath_trivial_tlist,
    1060             :                 result_trivial_tlist;
    1061         692 :     List       *nonunion_pathkeys = NIL;
    1062             :     double      dLeftGroups,
    1063             :                 dRightGroups,
    1064             :                 dNumGroups,
    1065             :                 dNumOutputRows;
    1066             :     bool        can_sort;
    1067             :     bool        can_hash;
    1068             :     SetOpCmd    cmd;
    1069             : 
    1070             :     /*
    1071             :      * Tell children to fetch all tuples.
    1072             :      */
    1073         692 :     root->tuple_fraction = 0.0;
    1074             : 
    1075             :     /* Recurse on children */
    1076         692 :     lrel = recurse_set_operations(op->larg, root,
    1077             :                                   op,
    1078             :                                   op->colTypes, op->colCollations,
    1079             :                                   refnames_tlist,
    1080             :                                   &lpath_tlist,
    1081             :                                   &lpath_trivial_tlist);
    1082             : 
    1083         692 :     rrel = recurse_set_operations(op->rarg, root,
    1084             :                                   op,
    1085             :                                   op->colTypes, op->colCollations,
    1086             :                                   refnames_tlist,
    1087             :                                   &rpath_tlist,
    1088             :                                   &rpath_trivial_tlist);
    1089             : 
    1090             :     /*
    1091             :      * Generate tlist for SetOp plan node.
    1092             :      *
    1093             :      * The tlist for a SetOp plan isn't important so far as the SetOp is
    1094             :      * concerned, but we must make it look real anyway for the benefit of the
    1095             :      * next plan level up.
    1096             :      */
    1097         692 :     tlist = generate_setop_tlist(op->colTypes, op->colCollations,
    1098             :                                  0, false, lpath_tlist, refnames_tlist,
    1099             :                                  &result_trivial_tlist);
    1100             : 
    1101             :     /* We should not have needed any type coercions in the tlist */
    1102             :     Assert(result_trivial_tlist);
    1103             : 
    1104         692 :     *pTargetList = tlist;
    1105             : 
    1106             :     /* Identify the grouping semantics */
    1107         692 :     groupList = generate_setop_grouplist(op, tlist);
    1108             : 
    1109             :     /* Check whether the operators support sorting or hashing */
    1110         692 :     can_sort = grouping_is_sortable(groupList);
    1111         692 :     can_hash = grouping_is_hashable(groupList);
    1112         692 :     if (!can_sort && !can_hash)
    1113           0 :         ereport(ERROR,
    1114             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1115             :         /* translator: %s is INTERSECT or EXCEPT */
    1116             :                  errmsg("could not implement %s",
    1117             :                         (op->op == SETOP_INTERSECT) ? "INTERSECT" : "EXCEPT"),
    1118             :                  errdetail("Some of the datatypes only support hashing, while others only support sorting.")));
    1119             : 
    1120         692 :     if (can_sort)
    1121             :     {
    1122             :         /* Determine the pathkeys for sorting by the whole target list */
    1123         692 :         nonunion_pathkeys = make_pathkeys_for_sortclauses(root, groupList,
    1124             :                                                           tlist);
    1125             : 
    1126         692 :         root->query_pathkeys = nonunion_pathkeys;
    1127             :     }
    1128             : 
    1129             :     /*
    1130             :      * Now that we've got all that info, we can build the child paths.
    1131             :      */
    1132         692 :     if (lrel->rtekind == RTE_SUBQUERY)
    1133         668 :         build_setop_child_paths(root, lrel, lpath_trivial_tlist, lpath_tlist,
    1134             :                                 nonunion_pathkeys, &dLeftGroups);
    1135             :     else
    1136          24 :         dLeftGroups = lrel->rows;
    1137         692 :     if (rrel->rtekind == RTE_SUBQUERY)
    1138         686 :         build_setop_child_paths(root, rrel, rpath_trivial_tlist, rpath_tlist,
    1139             :                                 nonunion_pathkeys, &dRightGroups);
    1140             :     else
    1141           6 :         dRightGroups = rrel->rows;
    1142             : 
    1143             :     /* Undo effects of forcing tuple_fraction to 0 */
    1144         692 :     root->tuple_fraction = save_fraction;
    1145             : 
    1146             :     /*
    1147             :      * For EXCEPT, we must put the left input first.  For INTERSECT, either
    1148             :      * order should give the same results, and we prefer to put the smaller
    1149             :      * input first in order to (a) minimize the size of the hash table in the
    1150             :      * hashing case, and (b) improve our chances of exploiting the executor's
    1151             :      * fast path for empty left-hand input.  "Smaller" means the one with the
    1152             :      * fewer groups.
    1153             :      */
    1154         692 :     if (op->op != SETOP_EXCEPT && dLeftGroups > dRightGroups)
    1155             :     {
    1156             :         /* need to swap the two inputs */
    1157             :         RelOptInfo *tmprel;
    1158             :         List       *tmplist;
    1159             :         double      tmpd;
    1160             : 
    1161          36 :         tmprel = lrel;
    1162          36 :         lrel = rrel;
    1163          36 :         rrel = tmprel;
    1164          36 :         tmplist = lpath_tlist;
    1165          36 :         lpath_tlist = rpath_tlist;
    1166          36 :         rpath_tlist = tmplist;
    1167          36 :         tmpd = dLeftGroups;
    1168          36 :         dLeftGroups = dRightGroups;
    1169          36 :         dRightGroups = tmpd;
    1170             :     }
    1171             : 
    1172         692 :     lpath = lrel->cheapest_total_path;
    1173         692 :     rpath = rrel->cheapest_total_path;
    1174             : 
    1175             :     /* Build result relation. */
    1176         692 :     result_rel = fetch_upper_rel(root, UPPERREL_SETOP,
    1177         692 :                                  bms_union(lrel->relids, rrel->relids));
    1178             : 
    1179             :     /*
    1180             :      * Create the PathTarget and set the width accordingly.  For EXCEPT, since
    1181             :      * the set op result won't contain rows from the rpath, we only account
    1182             :      * for the width of the lpath.  For INTERSECT, use both input paths.
    1183             :      */
    1184         692 :     if (op->op == SETOP_EXCEPT)
    1185         446 :         result_rel->reltarget = create_setop_pathtarget(root, tlist,
    1186         446 :                                                         list_make1(lpath));
    1187             :     else
    1188         246 :         result_rel->reltarget = create_setop_pathtarget(root, tlist,
    1189         246 :                                                         list_make2(lpath, rpath));
    1190             : 
    1191             :     /* Check for provably empty setop inputs and add short-circuit paths. */
    1192         692 :     if (op->op == SETOP_EXCEPT)
    1193             :     {
    1194             :         /*
    1195             :          * For EXCEPTs, if the left side is dummy then there's no need to
    1196             :          * inspect the right-hand side as scanning the right to find tuples to
    1197             :          * remove won't make the left-hand input any more empty.
    1198             :          */
    1199         446 :         if (is_dummy_rel(lrel))
    1200             :         {
    1201           6 :             mark_dummy_rel(result_rel);
    1202             : 
    1203           6 :             return result_rel;
    1204             :         }
    1205             : 
    1206             :         /* Handle EXCEPTs with dummy right input */
    1207         440 :         if (is_dummy_rel(rrel))
    1208             :         {
    1209           6 :             if (op->all)
    1210             :             {
    1211             :                 Path       *apath;
    1212             : 
    1213             :                 /*
    1214             :                  * EXCEPT ALL: If the right-hand input is dummy then we can
    1215             :                  * simply scan the left-hand input.  To keep createplan.c
    1216             :                  * happy, use a single child Append to handle the translation
    1217             :                  * between the set op targetlist and the targetlist of the
    1218             :                  * left input.  The Append will be removed in setrefs.c.
    1219             :                  */
    1220           6 :                 apath = (Path *) create_append_path(root, result_rel, list_make1(lpath),
    1221             :                                                     NIL, NIL, NULL, 0, false, -1);
    1222             : 
    1223           6 :                 add_path(result_rel, apath);
    1224             : 
    1225           6 :                 return result_rel;
    1226             :             }
    1227             :             else
    1228             :             {
    1229             :                 /*
    1230             :                  * To make EXCEPT with a dummy RHS work means having to
    1231             :                  * deduplicate the left input.  That could be done with
    1232             :                  * AggPaths, but it doesn't seem worth the effort.  Let the
    1233             :                  * normal path generation code below handle this one.
    1234             :                  */
    1235             :             }
    1236             :         }
    1237             :     }
    1238             :     else
    1239             :     {
    1240             :         /*
    1241             :          * For INTERSECT, if either input is a dummy rel then we can mark the
    1242             :          * result_rel as dummy since intersecting with an empty relation can
    1243             :          * never yield any results.  This is true regardless of INTERSECT or
    1244             :          * INTERSECT ALL.
    1245             :          */
    1246         246 :         if (is_dummy_rel(lrel) || is_dummy_rel(rrel))
    1247             :         {
    1248          18 :             mark_dummy_rel(result_rel);
    1249             : 
    1250          18 :             return result_rel;
    1251             :         }
    1252             :     }
    1253             : 
    1254             :     /*
    1255             :      * Estimate number of distinct groups that we'll need hashtable entries
    1256             :      * for; this is the size of the left-hand input for EXCEPT, or the smaller
    1257             :      * input for INTERSECT.  Also estimate the number of eventual output rows.
    1258             :      * In non-ALL cases, we estimate each group produces one output row; in
    1259             :      * ALL cases use the relevant relation size.  These are worst-case
    1260             :      * estimates, of course, but we need to be conservative.
    1261             :      */
    1262         662 :     if (op->op == SETOP_EXCEPT)
    1263             :     {
    1264         434 :         dNumGroups = dLeftGroups;
    1265         434 :         dNumOutputRows = op->all ? lpath->rows : dNumGroups;
    1266             :     }
    1267             :     else
    1268             :     {
    1269         228 :         dNumGroups = dLeftGroups;
    1270         228 :         dNumOutputRows = op->all ? Min(lpath->rows, rpath->rows) : dNumGroups;
    1271             :     }
    1272         662 :     result_rel->rows = dNumOutputRows;
    1273             : 
    1274             :     /* Select the SetOpCmd type */
    1275         662 :     switch (op->op)
    1276             :     {
    1277         228 :         case SETOP_INTERSECT:
    1278         228 :             cmd = op->all ? SETOPCMD_INTERSECT_ALL : SETOPCMD_INTERSECT;
    1279         228 :             break;
    1280         434 :         case SETOP_EXCEPT:
    1281         434 :             cmd = op->all ? SETOPCMD_EXCEPT_ALL : SETOPCMD_EXCEPT;
    1282         434 :             break;
    1283           0 :         default:
    1284           0 :             elog(ERROR, "unrecognized set op: %d", (int) op->op);
    1285             :             cmd = SETOPCMD_INTERSECT;   /* keep compiler quiet */
    1286             :             break;
    1287             :     }
    1288             : 
    1289             :     /*
    1290             :      * If we can hash, that just requires a SetOp atop the cheapest inputs.
    1291             :      */
    1292         662 :     if (can_hash)
    1293             :     {
    1294         602 :         path = (Path *) create_setop_path(root,
    1295             :                                           result_rel,
    1296             :                                           lpath,
    1297             :                                           rpath,
    1298             :                                           cmd,
    1299             :                                           SETOP_HASHED,
    1300             :                                           groupList,
    1301             :                                           dNumGroups,
    1302             :                                           dNumOutputRows);
    1303         602 :         add_path(result_rel, path);
    1304             :     }
    1305             : 
    1306             :     /*
    1307             :      * If we can sort, generate the cheapest sorted input paths, and add a
    1308             :      * SetOp atop those.
    1309             :      */
    1310         662 :     if (can_sort)
    1311             :     {
    1312             :         List       *pathkeys;
    1313             :         Path       *slpath,
    1314             :                    *srpath;
    1315             : 
    1316             :         /* First the left input ... */
    1317         662 :         pathkeys = make_pathkeys_for_sortclauses(root,
    1318             :                                                  groupList,
    1319             :                                                  lpath_tlist);
    1320         662 :         if (pathkeys_contained_in(pathkeys, lpath->pathkeys))
    1321          96 :             slpath = lpath;     /* cheapest path is already sorted */
    1322             :         else
    1323             :         {
    1324         566 :             slpath = get_cheapest_path_for_pathkeys(lrel->pathlist,
    1325             :                                                     nonunion_pathkeys,
    1326             :                                                     NULL,
    1327             :                                                     TOTAL_COST,
    1328             :                                                     false);
    1329             :             /* Subquery failed to produce any presorted paths? */
    1330         566 :             if (slpath == NULL)
    1331         144 :                 slpath = (Path *) create_sort_path(root,
    1332             :                                                    lpath->parent,
    1333             :                                                    lpath,
    1334             :                                                    pathkeys,
    1335             :                                                    -1.0);
    1336             :         }
    1337             : 
    1338             :         /* and now the same for the right. */
    1339         662 :         pathkeys = make_pathkeys_for_sortclauses(root,
    1340             :                                                  groupList,
    1341             :                                                  rpath_tlist);
    1342         662 :         if (pathkeys_contained_in(pathkeys, rpath->pathkeys))
    1343         108 :             srpath = rpath;     /* cheapest path is already sorted */
    1344             :         else
    1345             :         {
    1346         554 :             srpath = get_cheapest_path_for_pathkeys(rrel->pathlist,
    1347             :                                                     nonunion_pathkeys,
    1348             :                                                     NULL,
    1349             :                                                     TOTAL_COST,
    1350             :                                                     false);
    1351             :             /* Subquery failed to produce any presorted paths? */
    1352         554 :             if (srpath == NULL)
    1353         150 :                 srpath = (Path *) create_sort_path(root,
    1354             :                                                    rpath->parent,
    1355             :                                                    rpath,
    1356             :                                                    pathkeys,
    1357             :                                                    -1.0);
    1358             :         }
    1359             : 
    1360         662 :         path = (Path *) create_setop_path(root,
    1361             :                                           result_rel,
    1362             :                                           slpath,
    1363             :                                           srpath,
    1364             :                                           cmd,
    1365             :                                           SETOP_SORTED,
    1366             :                                           groupList,
    1367             :                                           dNumGroups,
    1368             :                                           dNumOutputRows);
    1369         662 :         add_path(result_rel, path);
    1370             :     }
    1371             : 
    1372         662 :     return result_rel;
    1373             : }
    1374             : 
    1375             : /*
    1376             :  * Pull up children of a UNION node that are identically-propertied UNIONs,
    1377             :  * and perform planning of the queries underneath the N-way UNION.
    1378             :  *
    1379             :  * The result is a list of RelOptInfos containing Paths for sub-nodes, with
    1380             :  * one entry for each descendant that is a leaf query or non-identical setop.
    1381             :  * We also return parallel lists of the childrens' targetlists and
    1382             :  * is-trivial-tlist flags.
    1383             :  *
    1384             :  * NOTE: we can also pull a UNION ALL up into a UNION, since the distinct
    1385             :  * output rows will be lost anyway.
    1386             :  */
    1387             : static List *
    1388        4634 : plan_union_children(PlannerInfo *root,
    1389             :                     SetOperationStmt *top_union,
    1390             :                     List *refnames_tlist,
    1391             :                     List **tlist_list,
    1392             :                     List **istrivial_tlist)
    1393             : {
    1394        4634 :     List       *pending_rels = list_make1(top_union);
    1395        4634 :     List       *result = NIL;
    1396             :     List       *child_tlist;
    1397             :     bool        trivial_tlist;
    1398             : 
    1399        4634 :     *tlist_list = NIL;
    1400        4634 :     *istrivial_tlist = NIL;
    1401             : 
    1402       25912 :     while (pending_rels != NIL)
    1403             :     {
    1404       21278 :         Node       *setOp = linitial(pending_rels);
    1405             : 
    1406       21278 :         pending_rels = list_delete_first(pending_rels);
    1407             : 
    1408       21278 :         if (IsA(setOp, SetOperationStmt))
    1409             :         {
    1410        8442 :             SetOperationStmt *op = (SetOperationStmt *) setOp;
    1411             : 
    1412        8442 :             if (op->op == top_union->op &&
    1413       16686 :                 (op->all == top_union->all || op->all) &&
    1414       16656 :                 equal(op->colTypes, top_union->colTypes) &&
    1415        8322 :                 equal(op->colCollations, top_union->colCollations))
    1416             :             {
    1417             :                 /* Same UNION, so fold children into parent */
    1418        8322 :                 pending_rels = lcons(op->rarg, pending_rels);
    1419        8322 :                 pending_rels = lcons(op->larg, pending_rels);
    1420        8322 :                 continue;
    1421             :             }
    1422             :         }
    1423             : 
    1424             :         /*
    1425             :          * Not same, so plan this child separately.
    1426             :          *
    1427             :          * If top_union isn't a UNION ALL, then we are interested in sorted
    1428             :          * output from the child, so pass top_union as parentOp.  Note that
    1429             :          * this isn't necessarily the child node's immediate SetOperationStmt
    1430             :          * parent, but that's fine: it's the effective parent.
    1431             :          */
    1432       12956 :         result = lappend(result, recurse_set_operations(setOp, root,
    1433       12956 :                                                         top_union->all ? NULL : top_union,
    1434             :                                                         top_union->colTypes,
    1435             :                                                         top_union->colCollations,
    1436             :                                                         refnames_tlist,
    1437             :                                                         &child_tlist,
    1438             :                                                         &trivial_tlist));
    1439       12956 :         *tlist_list = lappend(*tlist_list, child_tlist);
    1440       12956 :         *istrivial_tlist = lappend_int(*istrivial_tlist, trivial_tlist);
    1441             :     }
    1442             : 
    1443        4634 :     return result;
    1444             : }
    1445             : 
    1446             : /*
    1447             :  * postprocess_setop_rel - perform steps required after adding paths
    1448             :  */
    1449             : static void
    1450       22288 : postprocess_setop_rel(PlannerInfo *root, RelOptInfo *rel)
    1451             : {
    1452             :     /*
    1453             :      * We don't currently worry about allowing FDWs to contribute paths to
    1454             :      * this relation, but give extensions a chance.
    1455             :      */
    1456       22288 :     if (create_upper_paths_hook)
    1457           0 :         (*create_upper_paths_hook) (root, UPPERREL_SETOP,
    1458             :                                     NULL, rel, NULL);
    1459             : 
    1460             :     /* Select cheapest path */
    1461       22288 :     set_cheapest(rel);
    1462       22288 : }
    1463             : 
    1464             : /*
    1465             :  * Generate targetlist for a set-operation plan node
    1466             :  *
    1467             :  * colTypes: OID list of set-op's result column datatypes
    1468             :  * colCollations: OID list of set-op's result column collations
    1469             :  * varno: varno to use in generated Vars
    1470             :  * hack_constants: true to copy up constants (see comments in code)
    1471             :  * input_tlist: targetlist of this node's input node
    1472             :  * refnames_tlist: targetlist to take column names from
    1473             :  * trivial_tlist: output parameter, set to true if targetlist is trivial
    1474             :  */
    1475             : static List *
    1476       16744 : generate_setop_tlist(List *colTypes, List *colCollations,
    1477             :                      Index varno,
    1478             :                      bool hack_constants,
    1479             :                      List *input_tlist,
    1480             :                      List *refnames_tlist,
    1481             :                      bool *trivial_tlist)
    1482             : {
    1483       16744 :     List       *tlist = NIL;
    1484       16744 :     int         resno = 1;
    1485             :     ListCell   *ctlc,
    1486             :                *cclc,
    1487             :                *itlc,
    1488             :                *rtlc;
    1489             :     TargetEntry *tle;
    1490             :     Node       *expr;
    1491             : 
    1492       16744 :     *trivial_tlist = true;      /* until proven differently */
    1493             : 
    1494       66586 :     forfour(ctlc, colTypes, cclc, colCollations,
    1495             :             itlc, input_tlist, rtlc, refnames_tlist)
    1496             :     {
    1497       49842 :         Oid         colType = lfirst_oid(ctlc);
    1498       49842 :         Oid         colColl = lfirst_oid(cclc);
    1499       49842 :         TargetEntry *inputtle = (TargetEntry *) lfirst(itlc);
    1500       49842 :         TargetEntry *reftle = (TargetEntry *) lfirst(rtlc);
    1501             : 
    1502             :         Assert(inputtle->resno == resno);
    1503             :         Assert(reftle->resno == resno);
    1504             :         Assert(!inputtle->resjunk);
    1505             :         Assert(!reftle->resjunk);
    1506             : 
    1507             :         /*
    1508             :          * Generate columns referencing input columns and having appropriate
    1509             :          * data types and column names.  Insert datatype coercions where
    1510             :          * necessary.
    1511             :          *
    1512             :          * HACK: constants in the input's targetlist are copied up as-is
    1513             :          * rather than being referenced as subquery outputs.  This is mainly
    1514             :          * to ensure that when we try to coerce them to the output column's
    1515             :          * datatype, the right things happen for UNKNOWN constants.  But do
    1516             :          * this only at the first level of subquery-scan plans; we don't want
    1517             :          * phony constants appearing in the output tlists of upper-level
    1518             :          * nodes!
    1519             :          *
    1520             :          * Note that copying a constant doesn't in itself require us to mark
    1521             :          * the tlist nontrivial; see trivial_subqueryscan() in setrefs.c.
    1522             :          */
    1523       49842 :         if (hack_constants && inputtle->expr && IsA(inputtle->expr, Const))
    1524       15936 :             expr = (Node *) inputtle->expr;
    1525             :         else
    1526      135624 :             expr = (Node *) makeVar(varno,
    1527       33906 :                                     inputtle->resno,
    1528       33906 :                                     exprType((Node *) inputtle->expr),
    1529       33906 :                                     exprTypmod((Node *) inputtle->expr),
    1530       33906 :                                     exprCollation((Node *) inputtle->expr),
    1531             :                                     0);
    1532             : 
    1533       49842 :         if (exprType(expr) != colType)
    1534             :         {
    1535             :             /*
    1536             :              * Note: it's not really cool to be applying coerce_to_common_type
    1537             :              * here; one notable point is that assign_expr_collations never
    1538             :              * gets run on any generated nodes.  For the moment that's not a
    1539             :              * problem because we force the correct exposed collation below.
    1540             :              * It would likely be best to make the parser generate the correct
    1541             :              * output tlist for every set-op to begin with, though.
    1542             :              */
    1543        1642 :             expr = coerce_to_common_type(NULL,  /* no UNKNOWNs here */
    1544             :                                          expr,
    1545             :                                          colType,
    1546             :                                          "UNION/INTERSECT/EXCEPT");
    1547        1642 :             *trivial_tlist = false; /* the coercion makes it not trivial */
    1548             :         }
    1549             : 
    1550             :         /*
    1551             :          * Ensure the tlist entry's exposed collation matches the set-op. This
    1552             :          * is necessary because plan_set_operations() reports the result
    1553             :          * ordering as a list of SortGroupClauses, which don't carry collation
    1554             :          * themselves but just refer to tlist entries.  If we don't show the
    1555             :          * right collation then planner.c might do the wrong thing in
    1556             :          * higher-level queries.
    1557             :          *
    1558             :          * Note we use RelabelType, not CollateExpr, since this expression
    1559             :          * will reach the executor without any further processing.
    1560             :          */
    1561       49842 :         if (exprCollation(expr) != colColl)
    1562             :         {
    1563       13142 :             expr = applyRelabelType(expr,
    1564             :                                     exprType(expr), exprTypmod(expr), colColl,
    1565             :                                     COERCE_IMPLICIT_CAST, -1, false);
    1566       13142 :             *trivial_tlist = false; /* the relabel makes it not trivial */
    1567             :         }
    1568             : 
    1569       99684 :         tle = makeTargetEntry((Expr *) expr,
    1570       49842 :                               (AttrNumber) resno++,
    1571       49842 :                               pstrdup(reftle->resname),
    1572             :                               false);
    1573             : 
    1574             :         /*
    1575             :          * By convention, all output columns in a setop tree have
    1576             :          * ressortgroupref equal to their resno.  In some cases the ref isn't
    1577             :          * needed, but this is a cleaner way than modifying the tlist later.
    1578             :          */
    1579       49842 :         tle->ressortgroupref = tle->resno;
    1580             : 
    1581       49842 :         tlist = lappend(tlist, tle);
    1582             :     }
    1583             : 
    1584       16744 :     return tlist;
    1585             : }
    1586             : 
    1587             : /*
    1588             :  * Generate targetlist for a set-operation Append node
    1589             :  *
    1590             :  * colTypes: OID list of set-op's result column datatypes
    1591             :  * colCollations: OID list of set-op's result column collations
    1592             :  * input_tlists: list of tlists for sub-plans of the Append
    1593             :  * refnames_tlist: targetlist to take column names from
    1594             :  *
    1595             :  * The entries in the Append's targetlist should always be simple Vars;
    1596             :  * we just have to make sure they have the right datatypes/typmods/collations.
    1597             :  * The Vars are always generated with varno 0.
    1598             :  *
    1599             :  * XXX a problem with the varno-zero approach is that set_pathtarget_cost_width
    1600             :  * cannot figure out a realistic width for the tlist we make here.  But we
    1601             :  * ought to refactor this code to produce a PathTarget directly, anyway.
    1602             :  */
    1603             : static List *
    1604        5562 : generate_append_tlist(List *colTypes, List *colCollations,
    1605             :                       List *input_tlists,
    1606             :                       List *refnames_tlist)
    1607             : {
    1608        5562 :     List       *tlist = NIL;
    1609        5562 :     int         resno = 1;
    1610             :     ListCell   *curColType;
    1611             :     ListCell   *curColCollation;
    1612             :     ListCell   *ref_tl_item;
    1613             :     int         colindex;
    1614             :     TargetEntry *tle;
    1615             :     Node       *expr;
    1616             :     ListCell   *tlistl;
    1617             :     int32      *colTypmods;
    1618             : 
    1619             :     /*
    1620             :      * First extract typmods to use.
    1621             :      *
    1622             :      * If the inputs all agree on type and typmod of a particular column, use
    1623             :      * that typmod; else use -1.
    1624             :      */
    1625        5562 :     colTypmods = (int32 *) palloc(list_length(colTypes) * sizeof(int32));
    1626             : 
    1627       20374 :     foreach(tlistl, input_tlists)
    1628             :     {
    1629       14812 :         List       *subtlist = (List *) lfirst(tlistl);
    1630             :         ListCell   *subtlistl;
    1631             : 
    1632       14812 :         curColType = list_head(colTypes);
    1633       14812 :         colindex = 0;
    1634       58156 :         foreach(subtlistl, subtlist)
    1635             :         {
    1636       43344 :             TargetEntry *subtle = (TargetEntry *) lfirst(subtlistl);
    1637             : 
    1638             :             Assert(!subtle->resjunk);
    1639             :             Assert(curColType != NULL);
    1640       43344 :             if (exprType((Node *) subtle->expr) == lfirst_oid(curColType))
    1641             :             {
    1642             :                 /* If first subplan, copy the typmod; else compare */
    1643       43344 :                 int32       subtypmod = exprTypmod((Node *) subtle->expr);
    1644             : 
    1645       43344 :                 if (tlistl == list_head(input_tlists))
    1646       15340 :                     colTypmods[colindex] = subtypmod;
    1647       28004 :                 else if (subtypmod != colTypmods[colindex])
    1648          12 :                     colTypmods[colindex] = -1;
    1649             :             }
    1650             :             else
    1651             :             {
    1652             :                 /* types disagree, so force typmod to -1 */
    1653           0 :                 colTypmods[colindex] = -1;
    1654             :             }
    1655       43344 :             curColType = lnext(colTypes, curColType);
    1656       43344 :             colindex++;
    1657             :         }
    1658             :         Assert(curColType == NULL);
    1659             :     }
    1660             : 
    1661             :     /*
    1662             :      * Now we can build the tlist for the Append.
    1663             :      */
    1664        5562 :     colindex = 0;
    1665       20902 :     forthree(curColType, colTypes, curColCollation, colCollations,
    1666             :              ref_tl_item, refnames_tlist)
    1667             :     {
    1668       15340 :         Oid         colType = lfirst_oid(curColType);
    1669       15340 :         int32       colTypmod = colTypmods[colindex++];
    1670       15340 :         Oid         colColl = lfirst_oid(curColCollation);
    1671       15340 :         TargetEntry *reftle = (TargetEntry *) lfirst(ref_tl_item);
    1672             : 
    1673             :         Assert(reftle->resno == resno);
    1674             :         Assert(!reftle->resjunk);
    1675       15340 :         expr = (Node *) makeVar(0,
    1676             :                                 resno,
    1677             :                                 colType,
    1678             :                                 colTypmod,
    1679             :                                 colColl,
    1680             :                                 0);
    1681       30680 :         tle = makeTargetEntry((Expr *) expr,
    1682       15340 :                               (AttrNumber) resno++,
    1683       15340 :                               pstrdup(reftle->resname),
    1684             :                               false);
    1685             : 
    1686             :         /*
    1687             :          * By convention, all output columns in a setop tree have
    1688             :          * ressortgroupref equal to their resno.  In some cases the ref isn't
    1689             :          * needed, but this is a cleaner way than modifying the tlist later.
    1690             :          */
    1691       15340 :         tle->ressortgroupref = tle->resno;
    1692             : 
    1693       15340 :         tlist = lappend(tlist, tle);
    1694             :     }
    1695             : 
    1696        5562 :     pfree(colTypmods);
    1697             : 
    1698        5562 :     return tlist;
    1699             : }
    1700             : 
    1701             : /*
    1702             :  * generate_setop_grouplist
    1703             :  *      Build a SortGroupClause list defining the sort/grouping properties
    1704             :  *      of the setop's output columns.
    1705             :  *
    1706             :  * Parse analysis already determined the properties and built a suitable
    1707             :  * list, except that the entries do not have sortgrouprefs set because
    1708             :  * the parser output representation doesn't include a tlist for each
    1709             :  * setop.  So what we need to do here is copy that list and install
    1710             :  * proper sortgrouprefs into it (copying those from the targetlist).
    1711             :  */
    1712             : static List *
    1713        5002 : generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
    1714             : {
    1715        5002 :     List       *grouplist = copyObject(op->groupClauses);
    1716             :     ListCell   *lg;
    1717             :     ListCell   *lt;
    1718             : 
    1719        5002 :     lg = list_head(grouplist);
    1720       19002 :     foreach(lt, targetlist)
    1721             :     {
    1722       14000 :         TargetEntry *tle = (TargetEntry *) lfirst(lt);
    1723             :         SortGroupClause *sgc;
    1724             : 
    1725             :         Assert(!tle->resjunk);
    1726             : 
    1727             :         /* non-resjunk columns should have sortgroupref = resno */
    1728             :         Assert(tle->ressortgroupref == tle->resno);
    1729             : 
    1730             :         /* non-resjunk columns should have grouping clauses */
    1731             :         Assert(lg != NULL);
    1732       14000 :         sgc = (SortGroupClause *) lfirst(lg);
    1733       14000 :         lg = lnext(grouplist, lg);
    1734             :         Assert(sgc->tleSortGroupRef == 0);
    1735             : 
    1736       14000 :         sgc->tleSortGroupRef = tle->ressortgroupref;
    1737             :     }
    1738             :     Assert(lg == NULL);
    1739        5002 :     return grouplist;
    1740             : }
    1741             : 
    1742             : /*
    1743             :  * create_setop_pathtarget
    1744             :  *      Do the normal create_pathtarget() work, plus set the resulting
    1745             :  *      PathTarget's width to the average width of the Paths in child_pathlist
    1746             :  *      weighted using the estimated row count of each path.
    1747             :  *
    1748             :  * Note: This is required because set op target lists use varno==0, which
    1749             :  * results in a type default width estimate rather than one that's based on
    1750             :  * statistics of the columns from the set op children.
    1751             :  */
    1752             : static PathTarget *
    1753        5326 : create_setop_pathtarget(PlannerInfo *root, List *tlist, List *child_pathlist)
    1754             : {
    1755             :     PathTarget *reltarget;
    1756             :     ListCell   *lc;
    1757        5326 :     double      parent_rows = 0;
    1758        5326 :     double      parent_size = 0;
    1759             : 
    1760        5326 :     reltarget = create_pathtarget(root, tlist);
    1761             : 
    1762             :     /* Calculate the total rows and total size. */
    1763       19174 :     foreach(lc, child_pathlist)
    1764             :     {
    1765       13848 :         Path       *path = (Path *) lfirst(lc);
    1766             : 
    1767       13848 :         parent_rows += path->rows;
    1768       13848 :         parent_size += path->parent->reltarget->width * path->rows;
    1769             :     }
    1770             : 
    1771        5326 :     if (parent_rows > 0)
    1772        5308 :         reltarget->width = rint(parent_size / parent_rows);
    1773             : 
    1774        5326 :     return reltarget;
    1775             : }

Generated by: LCOV version 1.16