LCOV - code coverage report
Current view: top level - src/backend/optimizer/prep - prepunion.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 97.2 % 746 725
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 12 12
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 74.8 % 428 320

             Branch data     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-2026, 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                 :         799 : plan_set_operations(PlannerInfo *root)
      98                 :             : {
      99                 :         799 :         Query      *parse = root->parse;
     100                 :         799 :         SetOperationStmt *topop = castNode(SetOperationStmt, parse->setOperations);
     101                 :         799 :         Node       *node;
     102                 :         799 :         RangeTblEntry *leftmostRTE;
     103                 :         799 :         Query      *leftmostQuery;
     104                 :         799 :         RelOptInfo *setop_rel;
     105                 :         799 :         List       *top_tlist;
     106                 :             : 
     107         [ +  - ]:         799 :         Assert(topop);
     108                 :             : 
     109                 :             :         /* check for unsupported stuff */
     110         [ +  - ]:         799 :         Assert(parse->jointree->fromlist == NIL);
     111         [ +  - ]:         799 :         Assert(parse->jointree->quals == NULL);
     112         [ +  - ]:         799 :         Assert(parse->groupClause == NIL);
     113         [ +  - ]:         799 :         Assert(parse->havingQual == NULL);
     114         [ +  - ]:         799 :         Assert(parse->windowClause == NIL);
     115         [ +  - ]:         799 :         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         [ +  - ]:         799 :         Assert(root->eq_classes == NIL);
     124                 :         799 :         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                 :         799 :         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                 :         799 :         node = topop->larg;
     138   [ -  +  +  + ]:        1331 :         while (node && IsA(node, SetOperationStmt))
     139                 :         532 :                 node = ((SetOperationStmt *) node)->larg;
     140         [ +  - ]:         799 :         Assert(node && IsA(node, RangeTblRef));
     141                 :         799 :         leftmostRTE = root->simple_rte_array[((RangeTblRef *) node)->rtindex];
     142                 :         799 :         leftmostQuery = leftmostRTE->subquery;
     143         [ +  - ]:         799 :         Assert(leftmostQuery != NULL);
     144                 :             : 
     145                 :             :         /*
     146                 :             :          * If the topmost node is a recursive union, it needs special processing.
     147                 :             :          */
     148         [ +  + ]:         799 :         if (root->hasRecursion)
     149                 :             :         {
     150                 :         148 :                 setop_rel = generate_recursion_path(topop, root,
     151                 :          74 :                                                                                         leftmostQuery->targetList,
     152                 :             :                                                                                         &top_tlist);
     153                 :          74 :         }
     154                 :             :         else
     155                 :             :         {
     156                 :         725 :                 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                 :        1450 :                 setop_rel = recurse_set_operations((Node *) topop, root,
     164                 :             :                                                                                    NULL,        /* no parent */
     165                 :         725 :                                                                                    topop->colTypes, topop->colCollations,
     166                 :         725 :                                                                                    leftmostQuery->targetList,
     167                 :             :                                                                                    &top_tlist,
     168                 :             :                                                                                    &trivial_tlist);
     169                 :         725 :         }
     170                 :             : 
     171                 :             :         /* Must return the built tlist into root->processed_tlist. */
     172                 :         799 :         root->processed_tlist = top_tlist;
     173                 :             : 
     174                 :        1598 :         return setop_rel;
     175                 :         799 : }
     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                 :        2893 : 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                 :        2893 :         RelOptInfo *rel;
     221                 :             : 
     222                 :        2893 :         *istrivial_tlist = true;        /* for now */
     223                 :             : 
     224                 :             :         /* Guard against stack overflow due to overly complex setop nests */
     225                 :        2893 :         check_stack_depth();
     226                 :             : 
     227         [ +  + ]:        2893 :         if (IsA(setOp, RangeTblRef))
     228                 :             :         {
     229                 :        2141 :                 RangeTblRef *rtr = (RangeTblRef *) setOp;
     230                 :        2141 :                 RangeTblEntry *rte = root->simple_rte_array[rtr->rtindex];
     231                 :        2141 :                 Query      *subquery = rte->subquery;
     232                 :        2141 :                 PlannerInfo *subroot;
     233                 :        2141 :                 List       *tlist;
     234                 :        2141 :                 bool            trivial_tlist;
     235                 :        2141 :                 char       *plan_name;
     236                 :             : 
     237         [ +  - ]:        2141 :                 Assert(subquery != NULL);
     238                 :             : 
     239                 :             :                 /* Build a RelOptInfo for this leaf subquery. */
     240                 :        2141 :                 rel = build_simple_rel(root, rtr->rtindex, NULL);
     241                 :             : 
     242                 :             :                 /* plan_params should not be in use in current query level */
     243         [ +  - ]:        2141 :                 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                 :        2141 :                 plan_name = choose_plan_name(root->glob, "setop", true);
     251                 :        4282 :                 subroot = rel->subroot = subquery_planner(root->glob, subquery,
     252                 :        2141 :                                                                                                   plan_name, root,
     253                 :        2141 :                                                                                                   false, root->tuple_fraction,
     254                 :        2141 :                                                                                                   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         [ +  - ]:        2141 :                 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                 :        4282 :                 tlist = generate_setop_tlist(colTypes, colCollations,
     265                 :        2141 :                                                                          rtr->rtindex,
     266                 :             :                                                                          true,
     267                 :        2141 :                                                                          subroot->processed_tlist,
     268                 :        2141 :                                                                          refnames_tlist,
     269                 :             :                                                                          &trivial_tlist);
     270                 :        2141 :                 rel->reltarget = create_pathtarget(root, tlist);
     271                 :             : 
     272                 :             :                 /* Return the fully-fledged tlist to caller, too */
     273                 :        2141 :                 *pTargetList = tlist;
     274                 :        2141 :                 *istrivial_tlist = trivial_tlist;
     275                 :        2141 :         }
     276         [ +  - ]:         752 :         else if (IsA(setOp, SetOperationStmt))
     277                 :             :         {
     278                 :         752 :                 SetOperationStmt *op = (SetOperationStmt *) setOp;
     279                 :             : 
     280                 :             :                 /* UNIONs are much different from INTERSECT/EXCEPT */
     281         [ +  + ]:         752 :                 if (op->op == SETOP_UNION)
     282                 :        1274 :                         rel = generate_union_paths(op, root,
     283                 :         637 :                                                                            refnames_tlist,
     284                 :         637 :                                                                            pTargetList);
     285                 :             :                 else
     286                 :         230 :                         rel = generate_nonunion_paths(op, root,
     287                 :         115 :                                                                                   refnames_tlist,
     288                 :         115 :                                                                                   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   [ +  +  +  - ]:         752 :                 if (!tlist_same_datatypes(*pTargetList, colTypes, false) ||
     304                 :         750 :                         !tlist_same_collations(*pTargetList, colCollations, false))
     305                 :             :                 {
     306                 :           2 :                         PathTarget *target;
     307                 :           2 :                         bool            trivial_tlist;
     308                 :           2 :                         ListCell   *lc;
     309                 :             : 
     310                 :           4 :                         *pTargetList = generate_setop_tlist(colTypes, colCollations,
     311                 :             :                                                                                                 0,
     312                 :             :                                                                                                 false,
     313                 :           2 :                                                                                                 *pTargetList,
     314                 :           2 :                                                                                                 refnames_tlist,
     315                 :             :                                                                                                 &trivial_tlist);
     316                 :           2 :                         *istrivial_tlist = trivial_tlist;
     317                 :           2 :                         target = create_pathtarget(root, *pTargetList);
     318                 :             : 
     319                 :             :                         /* Apply projection to each path */
     320   [ +  -  +  +  :           4 :                         foreach(lc, rel->pathlist)
                   +  + ]
     321                 :             :                         {
     322                 :           2 :                                 Path       *subpath = (Path *) lfirst(lc);
     323                 :           2 :                                 Path       *path;
     324                 :             : 
     325         [ +  - ]:           2 :                                 Assert(subpath->param_info == NULL);
     326                 :           4 :                                 path = apply_projection_to_path(root, subpath->parent,
     327                 :           2 :                                                                                                 subpath, target);
     328                 :             :                                 /* If we had to add a Result, path is different from subpath */
     329         [ -  + ]:           2 :                                 if (path != subpath)
     330                 :           2 :                                         lfirst(lc) = path;
     331                 :           2 :                         }
     332                 :             : 
     333                 :             :                         /* Apply projection to each partial path */
     334   [ -  +  #  #  :           2 :                         foreach(lc, rel->partial_pathlist)
                   +  - ]
     335                 :             :                         {
     336                 :           0 :                                 Path       *subpath = (Path *) lfirst(lc);
     337                 :           0 :                                 Path       *path;
     338                 :             : 
     339         [ #  # ]:           0 :                                 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                 :           0 :                                                                                                            subpath, target);
     344                 :           0 :                                 lfirst(lc) = path;
     345                 :           0 :                         }
     346                 :           2 :                 }
     347                 :         752 :                 postprocess_setop_rel(root, rel);
     348                 :         752 :         }
     349                 :             :         else
     350                 :             :         {
     351   [ #  #  #  # ]:           0 :                 elog(ERROR, "unrecognized node type: %d",
     352                 :             :                          (int) nodeTag(setOp));
     353                 :           0 :                 *pTargetList = NIL;
     354                 :           0 :                 rel = NULL;                             /* keep compiler quiet */
     355                 :             :         }
     356                 :             : 
     357                 :        5786 :         return rel;
     358                 :        2893 : }
     359                 :             : 
     360                 :             : /*
     361                 :             :  * Generate paths for a recursive UNION node
     362                 :             :  */
     363                 :             : static RelOptInfo *
     364                 :          74 : generate_recursion_path(SetOperationStmt *setOp, PlannerInfo *root,
     365                 :             :                                                 List *refnames_tlist,
     366                 :             :                                                 List **pTargetList)
     367                 :             : {
     368                 :          74 :         RelOptInfo *result_rel;
     369                 :          74 :         Path       *path;
     370                 :          74 :         RelOptInfo *lrel,
     371                 :             :                            *rrel;
     372                 :          74 :         Path       *lpath;
     373                 :          74 :         Path       *rpath;
     374                 :          74 :         List       *lpath_tlist;
     375                 :          74 :         bool            lpath_trivial_tlist;
     376                 :          74 :         List       *rpath_tlist;
     377                 :          74 :         bool            rpath_trivial_tlist;
     378                 :          74 :         List       *tlist;
     379                 :          74 :         List       *groupList;
     380                 :          74 :         double          dNumGroups;
     381                 :             : 
     382                 :             :         /* Parser should have rejected other cases */
     383         [ +  - ]:          74 :         if (setOp->op != SETOP_UNION)
     384   [ #  #  #  # ]:           0 :                 elog(ERROR, "only UNION queries can be recursive");
     385                 :             :         /* Worktable ID should be assigned */
     386         [ +  - ]:          74 :         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                 :         148 :         lrel = recurse_set_operations(setOp->larg, root,
     393                 :             :                                                                   NULL, /* no value in sorted results */
     394                 :          74 :                                                                   setOp->colTypes, setOp->colCollations,
     395                 :          74 :                                                                   refnames_tlist,
     396                 :             :                                                                   &lpath_tlist,
     397                 :             :                                                                   &lpath_trivial_tlist);
     398         [ -  + ]:          74 :         if (lrel->rtekind == RTE_SUBQUERY)
     399                 :          74 :                 build_setop_child_paths(root, lrel, lpath_trivial_tlist, lpath_tlist,
     400                 :             :                                                                 NIL, NULL);
     401                 :          74 :         lpath = lrel->cheapest_total_path;
     402                 :             :         /* The right path will want to look at the left one ... */
     403                 :          74 :         root->non_recursive_path = lpath;
     404                 :         148 :         rrel = recurse_set_operations(setOp->rarg, root,
     405                 :             :                                                                   NULL, /* no value in sorted results */
     406                 :          74 :                                                                   setOp->colTypes, setOp->colCollations,
     407                 :          74 :                                                                   refnames_tlist,
     408                 :             :                                                                   &rpath_tlist,
     409                 :             :                                                                   &rpath_trivial_tlist);
     410         [ +  + ]:          74 :         if (rrel->rtekind == RTE_SUBQUERY)
     411                 :          73 :                 build_setop_child_paths(root, rrel, rpath_trivial_tlist, rpath_tlist,
     412                 :             :                                                                 NIL, NULL);
     413                 :          74 :         rpath = rrel->cheapest_total_path;
     414                 :          74 :         root->non_recursive_path = NULL;
     415                 :             : 
     416                 :             :         /*
     417                 :             :          * Generate tlist for RecursiveUnion path node --- same as in Append cases
     418                 :             :          */
     419                 :         148 :         tlist = generate_append_tlist(setOp->colTypes, setOp->colCollations,
     420                 :          74 :                                                                   list_make2(lpath_tlist, rpath_tlist),
     421                 :          74 :                                                                   refnames_tlist);
     422                 :             : 
     423                 :          74 :         *pTargetList = tlist;
     424                 :             : 
     425                 :             :         /* Build result relation. */
     426                 :         148 :         result_rel = fetch_upper_rel(root, UPPERREL_SETOP,
     427                 :          74 :                                                                  bms_union(lrel->relids, rrel->relids));
     428                 :          74 :         result_rel->reltarget = create_pathtarget(root, tlist);
     429                 :             : 
     430                 :             :         /*
     431                 :             :          * If UNION, identify the grouping operators
     432                 :             :          */
     433         [ +  + ]:          74 :         if (setOp->all)
     434                 :             :         {
     435                 :          60 :                 groupList = NIL;
     436                 :          60 :                 dNumGroups = 0;
     437                 :          60 :         }
     438                 :             :         else
     439                 :             :         {
     440                 :             :                 /* Identify the grouping semantics */
     441                 :          14 :                 groupList = generate_setop_grouplist(setOp, tlist);
     442                 :             : 
     443                 :             :                 /* We only support hashing here */
     444         [ +  + ]:          14 :                 if (!grouping_is_hashable(groupList))
     445   [ +  -  +  - ]:           1 :                         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                 :          13 :                 dNumGroups = lpath->rows + rpath->rows * 10;
     455                 :             :         }
     456                 :             : 
     457                 :             :         /*
     458                 :             :          * And make the path node.
     459                 :             :          */
     460                 :         146 :         path = (Path *) create_recursiveunion_path(root,
     461                 :          73 :                                                                                            result_rel,
     462                 :          73 :                                                                                            lpath,
     463                 :          73 :                                                                                            rpath,
     464                 :          73 :                                                                                            result_rel->reltarget,
     465                 :          73 :                                                                                            groupList,
     466                 :          73 :                                                                                            root->wt_param_id,
     467                 :          73 :                                                                                            dNumGroups);
     468                 :             : 
     469                 :          73 :         add_path(result_rel, path);
     470                 :          73 :         postprocess_setop_rel(root, result_rel);
     471                 :         146 :         return result_rel;
     472                 :          73 : }
     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                 :        2141 : build_setop_child_paths(PlannerInfo *root, RelOptInfo *rel,
     489                 :             :                                                 bool trivial_tlist, List *child_tlist,
     490                 :             :                                                 List *interesting_pathkeys, double *pNumGroups)
     491                 :             : {
     492                 :        2141 :         RelOptInfo *final_rel;
     493                 :        2141 :         List       *setop_pathkeys = rel->subroot->setop_pathkeys;
     494                 :        2141 :         ListCell   *lc;
     495                 :             : 
     496                 :             :         /* it can't be a set op child rel if it's not a subquery */
     497         [ +  - ]:        2141 :         Assert(rel->rtekind == RTE_SUBQUERY);
     498                 :             : 
     499                 :             :         /* when sorting is needed, add child rel equivalences */
     500         [ +  + ]:        2141 :         if (interesting_pathkeys != NIL)
     501                 :        3810 :                 add_setop_child_rel_equivalences(root,
     502                 :        1905 :                                                                                  rel,
     503                 :        1905 :                                                                                  child_tlist,
     504                 :        1905 :                                                                                  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                 :        2141 :         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                 :        2141 :         final_rel = fetch_upper_rel(rel->subroot, UPPERREL_FINAL, NULL);
     518                 :        2141 :         rel->consider_parallel = final_rel->consider_parallel;
     519                 :             : 
     520                 :             :         /* Generate subquery scan paths for any interesting path in final_rel */
     521   [ +  -  +  +  :        5828 :         foreach(lc, final_rel->pathlist)
                   +  + ]
     522                 :             :         {
     523                 :        3687 :                 Path       *subpath = (Path *) lfirst(lc);
     524                 :        3687 :                 List       *pathkeys;
     525                 :        3687 :                 Path       *cheapest_input_path = final_rel->cheapest_total_path;
     526                 :        3687 :                 bool            is_sorted;
     527                 :        3687 :                 int                     presorted_keys;
     528                 :             : 
     529                 :             :                 /* If the input rel is dummy, propagate that to this query level */
     530         [ +  + ]:        3687 :                 if (is_dummy_rel(final_rel))
     531                 :             :                 {
     532                 :          12 :                         mark_dummy_rel(rel);
     533                 :          12 :                         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         [ +  + ]:        3675 :                 if (subpath == cheapest_input_path)
     542                 :             :                 {
     543                 :             :                         /* Convert subpath's pathkeys to outer representation */
     544                 :        4258 :                         pathkeys = convert_subquery_pathkeys(root, rel, subpath->pathkeys,
     545                 :        2129 :                                                                                                  make_tlist_from_pathtarget(subpath->pathtarget));
     546                 :             : 
     547                 :             :                         /* Generate outer path using this subpath */
     548                 :        4258 :                         add_path(rel, (Path *) create_subqueryscan_path(root,
     549                 :        2129 :                                                                                                                         rel,
     550                 :        2129 :                                                                                                                         subpath,
     551                 :        2129 :                                                                                                                         trivial_tlist,
     552                 :        2129 :                                                                                                                         pathkeys,
     553                 :             :                                                                                                                         NULL));
     554                 :        2129 :                 }
     555                 :             : 
     556                 :             :                 /* skip dealing with sorted paths if the setop doesn't need them */
     557         [ +  + ]:        3675 :                 if (interesting_pathkeys == NIL)
     558                 :         238 :                         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                 :        6874 :                 is_sorted = pathkeys_count_contained_in(setop_pathkeys,
     566                 :        3437 :                                                                                                 subpath->pathkeys,
     567                 :             :                                                                                                 &presorted_keys);
     568                 :             : 
     569         [ +  + ]:        3437 :                 if (!is_sorted)
     570                 :             :                 {
     571                 :        2301 :                         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   [ +  +  +  - ]:        2821 :                         if (subpath != cheapest_input_path &&
     581         [ +  + ]:         522 :                                 (presorted_keys == 0 || !enable_incremental_sort))
     582                 :           2 :                                 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   [ +  +  -  + ]:        2299 :                         if (presorted_keys == 0 || !enable_incremental_sort)
     590                 :        3542 :                                 subpath = (Path *) create_sort_path(rel->subroot,
     591                 :        1771 :                                                                                                         final_rel,
     592                 :        1771 :                                                                                                         subpath,
     593                 :        1771 :                                                                                                         setop_pathkeys,
     594                 :        1771 :                                                                                                         limittuples);
     595                 :             :                         else
     596                 :        1056 :                                 subpath = (Path *) create_incremental_sort_path(rel->subroot,
     597                 :         528 :                                                                                                                                 final_rel,
     598                 :         528 :                                                                                                                                 subpath,
     599                 :         528 :                                                                                                                                 setop_pathkeys,
     600                 :         528 :                                                                                                                                 presorted_keys,
     601                 :         528 :                                                                                                                                 limittuples);
     602         [ +  + ]:        2301 :                 }
     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         [ +  + ]:        3435 :                 if (subpath != cheapest_input_path)
     610                 :             :                 {
     611                 :             :                         /* Convert subpath's pathkeys to outer representation */
     612                 :        6642 :                         pathkeys = convert_subquery_pathkeys(root, rel, subpath->pathkeys,
     613                 :        3321 :                                                                                                  make_tlist_from_pathtarget(subpath->pathtarget));
     614                 :             : 
     615                 :             :                         /* Generate outer path using this subpath */
     616                 :        6642 :                         add_path(rel, (Path *) create_subqueryscan_path(root,
     617                 :        3321 :                                                                                                                         rel,
     618                 :        3321 :                                                                                                                         subpath,
     619                 :        3321 :                                                                                                                         trivial_tlist,
     620                 :        3321 :                                                                                                                         pathkeys,
     621                 :             :                                                                                                                         NULL));
     622                 :        3321 :                 }
     623      [ -  +  + ]:        3687 :         }
     624                 :             : 
     625                 :             :         /* if consider_parallel is false, there should be no partial paths */
     626   [ +  +  +  - ]:        2141 :         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   [ +  +  +  -  :        2141 :         if (rel->consider_parallel && bms_is_empty(rel->lateral_relids) &&
                   +  + ]
     635                 :        1487 :                 final_rel->partial_pathlist != NIL)
     636                 :             :         {
     637                 :           2 :                 Path       *partial_subpath;
     638                 :           2 :                 Path       *partial_path;
     639                 :             : 
     640                 :           2 :                 partial_subpath = linitial(final_rel->partial_pathlist);
     641                 :           2 :                 partial_path = (Path *)
     642                 :           4 :                         create_subqueryscan_path(root, rel, partial_subpath,
     643                 :           2 :                                                                          trivial_tlist,
     644                 :             :                                                                          NIL, NULL);
     645                 :           2 :                 add_partial_path(rel, partial_path);
     646                 :           2 :         }
     647                 :             : 
     648                 :        2141 :         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         [ +  + ]:        2141 :         if (pNumGroups)
     669                 :             :         {
     670                 :         225 :                 PlannerInfo *subroot = rel->subroot;
     671                 :         225 :                 Query      *subquery = subroot->parse;
     672                 :             : 
     673   [ +  -  +  - ]:         225 :                 if (subquery->groupClause || subquery->groupingSets ||
     674   [ +  +  +  -  :         225 :                         subquery->distinctClause || subroot->hasHavingQual ||
                   -  + ]
     675                 :         223 :                         subquery->hasAggs)
     676                 :           2 :                         *pNumGroups = rel->cheapest_total_path->rows;
     677                 :             :                 else
     678                 :         446 :                         *pNumGroups = estimate_num_groups(subroot,
     679                 :         223 :                                                                                           get_tlist_exprs(subroot->parse->targetList, false),
     680                 :         223 :                                                                                           rel->cheapest_total_path->rows,
     681                 :             :                                                                                           NULL,
     682                 :             :                                                                                           NULL);
     683                 :         225 :         }
     684                 :        2141 : }
     685                 :             : 
     686                 :             : /*
     687                 :             :  * Generate paths for a UNION or UNION ALL node
     688                 :             :  */
     689                 :             : static RelOptInfo *
     690                 :         637 : generate_union_paths(SetOperationStmt *op, PlannerInfo *root,
     691                 :             :                                          List *refnames_tlist,
     692                 :             :                                          List **pTargetList)
     693                 :             : {
     694                 :         637 :         Relids          relids = NULL;
     695                 :         637 :         RelOptInfo *result_rel;
     696                 :         637 :         ListCell   *lc;
     697                 :         637 :         ListCell   *lc2;
     698                 :         637 :         ListCell   *lc3;
     699                 :         637 :         AppendPathInput cheapest = {0};
     700                 :         637 :         AppendPathInput ordered = {0};
     701                 :         637 :         AppendPathInput partial = {0};
     702                 :         637 :         bool            partial_paths_valid = true;
     703                 :         637 :         bool            consider_parallel = true;
     704                 :         637 :         List       *rellist;
     705                 :         637 :         List       *tlist_list;
     706                 :         637 :         List       *trivial_tlist_list;
     707                 :         637 :         List       *tlist;
     708                 :         637 :         List       *groupList = NIL;
     709                 :         637 :         Path       *apath;
     710                 :         637 :         Path       *gpath = NULL;
     711                 :         637 :         bool            try_sorted = false;
     712                 :         637 :         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                 :        1274 :         rellist = plan_union_children(root,
     721                 :         637 :                                                                   op,
     722                 :         637 :                                                                   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                 :        1274 :         tlist = generate_append_tlist(op->colTypes, op->colCollations,
     734                 :         637 :                                                                   tlist_list, refnames_tlist);
     735                 :         637 :         *pTargetList = tlist;
     736                 :             : 
     737                 :             :         /* For UNIONs (not UNION ALL), try sorting, if sorting is possible */
     738         [ +  + ]:         637 :         if (!op->all)
     739                 :             :         {
     740                 :             :                 /* Identify the grouping semantics */
     741                 :         604 :                 groupList = generate_setop_grouplist(op, tlist);
     742                 :             : 
     743         [ +  + ]:         604 :                 if (grouping_is_sortable(op->groupClauses))
     744                 :             :                 {
     745                 :         602 :                         try_sorted = true;
     746                 :             :                         /* Determine the pathkeys for sorting by the whole target list */
     747                 :        1204 :                         union_pathkeys = make_pathkeys_for_sortclauses(root, groupList,
     748                 :         602 :                                                                                                                    tlist);
     749                 :             : 
     750                 :         602 :                         root->query_pathkeys = union_pathkeys;
     751                 :         602 :                 }
     752                 :         604 :         }
     753                 :             : 
     754                 :             :         /*
     755                 :             :          * Now that we've got the append target list, we can build the union child
     756                 :             :          * paths.
     757                 :             :          */
     758   [ +  -  +  +  :        2427 :         forthree(lc, rellist, lc2, trivial_tlist_list, lc3, tlist_list)
          +  -  +  +  +  
          -  +  +  +  +  
             -  +  +  + ]
     759                 :             :         {
     760                 :        1790 :                 RelOptInfo *rel = lfirst(lc);
     761                 :        1790 :                 bool            trivial_tlist = lfirst_int(lc2);
     762                 :        1790 :                 List       *child_tlist = lfirst_node(List, lc3);
     763                 :             : 
     764                 :             :                 /* only build paths for the union children */
     765         [ +  + ]:        1790 :                 if (rel->rtekind == RTE_SUBQUERY)
     766                 :        3538 :                         build_setop_child_paths(root, rel, trivial_tlist, child_tlist,
     767                 :        1769 :                                                                         union_pathkeys, NULL);
     768                 :        1790 :         }
     769                 :             : 
     770                 :             :         /* Build path lists and relid set. */
     771   [ +  -  +  +  :        2427 :         foreach(lc, rellist)
                   +  + ]
     772                 :             :         {
     773                 :        1790 :                 RelOptInfo *rel = lfirst(lc);
     774                 :        1790 :                 Path       *ordered_path;
     775                 :             : 
     776                 :             :                 /*
     777                 :             :                  * Record the relids so that we can identify the correct
     778                 :             :                  * UPPERREL_SETOP RelOptInfo below.
     779                 :             :                  */
     780                 :        1790 :                 relids = bms_add_members(relids, rel->relids);
     781                 :             : 
     782                 :             :                 /* Skip any UNION children that are proven not to yield any rows */
     783         [ +  + ]:        1790 :                 if (is_dummy_rel(rel))
     784                 :           6 :                         continue;
     785                 :             : 
     786                 :        3568 :                 cheapest.subpaths = lappend(cheapest.subpaths,
     787                 :        1784 :                                                                         rel->cheapest_total_path);
     788                 :             : 
     789         [ +  + ]:        1784 :                 if (try_sorted)
     790                 :             :                 {
     791                 :        1286 :                         ordered_path = get_cheapest_path_for_pathkeys(rel->pathlist,
     792                 :         643 :                                                                                                                   union_pathkeys,
     793                 :             :                                                                                                                   NULL,
     794                 :             :                                                                                                                   TOTAL_COST,
     795                 :             :                                                                                                                   false);
     796                 :             : 
     797         [ +  + ]:         643 :                         if (ordered_path != NULL)
     798                 :          81 :                                 ordered.subpaths = lappend(ordered.subpaths, ordered_path);
     799                 :             :                         else
     800                 :             :                         {
     801                 :             :                                 /*
     802                 :             :                                  * If we can't find a sorted path, just give up trying to
     803                 :             :                                  * generate a list of correctly sorted child paths.  This can
     804                 :             :                                  * happen when type coercion was added to the targetlist due
     805                 :             :                                  * to mismatching types from the union children.
     806                 :             :                                  */
     807                 :         562 :                                 try_sorted = false;
     808                 :             :                         }
     809                 :         643 :                 }
     810                 :             : 
     811         [ +  + ]:        1784 :                 if (consider_parallel)
     812                 :             :                 {
     813         [ +  + ]:        1257 :                         if (!rel->consider_parallel)
     814                 :             :                         {
     815                 :         528 :                                 consider_parallel = false;
     816                 :         528 :                                 partial_paths_valid = false;
     817                 :         528 :                         }
     818         [ +  + ]:         729 :                         else if (rel->partial_pathlist == NIL)
     819                 :         727 :                                 partial_paths_valid = false;
     820                 :             :                         else
     821                 :           4 :                                 partial.partial_subpaths = lappend(partial.partial_subpaths,
     822                 :           2 :                                                                                                    linitial(rel->partial_pathlist));
     823                 :        1257 :                 }
     824      [ -  +  + ]:        1790 :         }
     825                 :             : 
     826                 :             :         /* Build result relation. */
     827                 :         637 :         result_rel = fetch_upper_rel(root, UPPERREL_SETOP, relids);
     828                 :        1274 :         result_rel->reltarget = create_setop_pathtarget(root, tlist,
     829                 :         637 :                                                                                                         cheapest.subpaths);
     830                 :         637 :         result_rel->consider_parallel = consider_parallel;
     831                 :         637 :         result_rel->consider_startup = (root->tuple_fraction > 0);
     832                 :             : 
     833                 :             :         /* If all UNION children were dummy rels, make the resulting rel dummy */
     834         [ +  + ]:         637 :         if (cheapest.subpaths == NIL)
     835                 :             :         {
     836                 :           1 :                 mark_dummy_rel(result_rel);
     837                 :             : 
     838                 :           1 :                 return result_rel;
     839                 :             :         }
     840                 :             : 
     841                 :             :         /*
     842                 :             :          * Append the child results together using the cheapest paths from each
     843                 :             :          * union child.
     844                 :             :          */
     845                 :         636 :         apath = (Path *) create_append_path(root, result_rel, cheapest,
     846                 :             :                                                                                 NIL, NULL, 0, false, -1);
     847                 :             : 
     848                 :             :         /*
     849                 :             :          * Estimate number of groups.  For now we just assume the output is unique
     850                 :             :          * --- this is certainly true for the UNION case, and we want worst-case
     851                 :             :          * estimates anyway.
     852                 :             :          */
     853                 :         636 :         result_rel->rows = apath->rows;
     854                 :             : 
     855                 :             :         /*
     856                 :             :          * Now consider doing the same thing using the partial paths plus Append
     857                 :             :          * plus Gather.
     858                 :             :          */
     859         [ +  + ]:         636 :         if (partial_paths_valid)
     860                 :             :         {
     861                 :           1 :                 Path       *papath;
     862                 :           1 :                 int                     parallel_workers = 0;
     863                 :             : 
     864                 :             :                 /* Find the highest number of workers requested for any subpath. */
     865   [ +  -  +  +  :           3 :                 foreach(lc, partial.partial_subpaths)
                   +  + ]
     866                 :             :                 {
     867                 :           2 :                         Path       *subpath = lfirst(lc);
     868                 :             : 
     869         [ -  + ]:           2 :                         parallel_workers = Max(parallel_workers,
     870                 :             :                                                                    subpath->parallel_workers);
     871                 :           2 :                 }
     872         [ +  - ]:           1 :                 Assert(parallel_workers > 0);
     873                 :             : 
     874                 :             :                 /*
     875                 :             :                  * If the use of parallel append is permitted, always request at least
     876                 :             :                  * log2(# of children) paths.  We assume it can be useful to have
     877                 :             :                  * extra workers in this case because they will be spread out across
     878                 :             :                  * the children.  The precise formula is just a guess; see
     879                 :             :                  * add_paths_to_append_rel.
     880                 :             :                  */
     881         [ -  + ]:           1 :                 if (enable_parallel_append)
     882                 :             :                 {
     883         [ -  + ]:           1 :                         parallel_workers = Max(parallel_workers,
     884                 :             :                                                                    pg_leftmost_one_pos32(list_length(partial.partial_subpaths)) + 1);
     885         [ -  + ]:           1 :                         parallel_workers = Min(parallel_workers,
     886                 :             :                                                                    max_parallel_workers_per_gather);
     887                 :           1 :                 }
     888         [ +  - ]:           1 :                 Assert(parallel_workers > 0);
     889                 :             : 
     890                 :           1 :                 papath = (Path *)
     891                 :           2 :                         create_append_path(root, result_rel, partial,
     892                 :           1 :                                                            NIL, NULL, parallel_workers,
     893                 :           1 :                                                            enable_parallel_append, -1);
     894                 :           1 :                 gpath = (Path *)
     895                 :           2 :                         create_gather_path(root, result_rel, papath,
     896                 :           1 :                                                            result_rel->reltarget, NULL, NULL);
     897                 :           1 :         }
     898                 :             : 
     899         [ +  + ]:         636 :         if (!op->all)
     900                 :             :         {
     901                 :         603 :                 double          dNumGroups;
     902                 :         603 :                 bool            can_sort = grouping_is_sortable(groupList);
     903                 :         603 :                 bool            can_hash = grouping_is_hashable(groupList);
     904                 :         603 :                 Path       *first_path = linitial(cheapest.subpaths);
     905                 :             : 
     906                 :             :                 /*
     907                 :             :                  * Estimate the number of UNION output rows.  In the case when only a
     908                 :             :                  * single UNION child remains, we can use estimate_num_groups() on
     909                 :             :                  * that child.  We must be careful not to do this when that child is
     910                 :             :                  * the result of some other set operation as the targetlist will
     911                 :             :                  * contain Vars with varno==0, which estimate_num_groups() wouldn't
     912                 :             :                  * like.
     913                 :             :                  */
     914   [ +  +  +  + ]:         603 :                 if (list_length(cheapest.subpaths) == 1 &&
     915                 :           3 :                         first_path->parent->reloptkind != RELOPT_UPPER_REL)
     916                 :             :                 {
     917                 :           4 :                         dNumGroups = estimate_num_groups(root,
     918                 :           2 :                                                                                          first_path->pathtarget->exprs,
     919                 :           2 :                                                                                          first_path->rows,
     920                 :             :                                                                                          NULL,
     921                 :             :                                                                                          NULL);
     922                 :           2 :                 }
     923                 :             :                 else
     924                 :             :                 {
     925                 :             :                         /*
     926                 :             :                          * Otherwise, for the moment, take the number of distinct groups
     927                 :             :                          * as equal to the total input size, i.e., the worst case.  This
     928                 :             :                          * is too conservative, but it's not clear how to get a decent
     929                 :             :                          * estimate of the true size.  One should note as well the
     930                 :             :                          * propensity of novices to write UNION rather than UNION ALL even
     931                 :             :                          * when they don't expect any duplicates...
     932                 :             :                          */
     933                 :         601 :                         dNumGroups = apath->rows;
     934                 :             :                 }
     935                 :             : 
     936         [ +  + ]:         603 :                 if (can_hash)
     937                 :             :                 {
     938                 :         591 :                         Path       *path;
     939                 :             : 
     940                 :             :                         /*
     941                 :             :                          * Try a hash aggregate plan on 'apath'.  This is the cheapest
     942                 :             :                          * available path containing each append child.
     943                 :             :                          */
     944                 :        1182 :                         path = (Path *) create_agg_path(root,
     945                 :         591 :                                                                                         result_rel,
     946                 :         591 :                                                                                         apath,
     947                 :         591 :                                                                                         result_rel->reltarget,
     948                 :             :                                                                                         AGG_HASHED,
     949                 :             :                                                                                         AGGSPLIT_SIMPLE,
     950                 :         591 :                                                                                         groupList,
     951                 :             :                                                                                         NIL,
     952                 :             :                                                                                         NULL,
     953                 :         591 :                                                                                         dNumGroups);
     954                 :         591 :                         add_path(result_rel, path);
     955                 :             : 
     956                 :             :                         /* Try hash aggregate on the Gather path, if valid */
     957         [ +  + ]:         591 :                         if (gpath != NULL)
     958                 :             :                         {
     959                 :             :                                 /* Hashed aggregate plan --- no sort needed */
     960                 :           2 :                                 path = (Path *) create_agg_path(root,
     961                 :           1 :                                                                                                 result_rel,
     962                 :           1 :                                                                                                 gpath,
     963                 :           1 :                                                                                                 result_rel->reltarget,
     964                 :             :                                                                                                 AGG_HASHED,
     965                 :             :                                                                                                 AGGSPLIT_SIMPLE,
     966                 :           1 :                                                                                                 groupList,
     967                 :             :                                                                                                 NIL,
     968                 :             :                                                                                                 NULL,
     969                 :           1 :                                                                                                 dNumGroups);
     970                 :           1 :                                 add_path(result_rel, path);
     971                 :           1 :                         }
     972                 :         591 :                 }
     973                 :             : 
     974         [ +  + ]:         603 :                 if (can_sort)
     975                 :             :                 {
     976                 :         601 :                         Path       *path = apath;
     977                 :             : 
     978                 :             :                         /* Try Sort -> Unique on the Append path */
     979         [ +  + ]:         601 :                         if (groupList != NIL)
     980                 :        1192 :                                 path = (Path *) create_sort_path(root, result_rel, path,
     981                 :         596 :                                                                                                  make_pathkeys_for_sortclauses(root, groupList, tlist),
     982                 :             :                                                                                                  -1.0);
     983                 :             : 
     984                 :        1202 :                         path = (Path *) create_unique_path(root,
     985                 :         601 :                                                                                            result_rel,
     986                 :         601 :                                                                                            path,
     987                 :         601 :                                                                                            list_length(path->pathkeys),
     988                 :         601 :                                                                                            dNumGroups);
     989                 :             : 
     990                 :         601 :                         add_path(result_rel, path);
     991                 :             : 
     992                 :             :                         /* Try Sort -> Unique on the Gather path, if set */
     993         [ +  + ]:         601 :                         if (gpath != NULL)
     994                 :             :                         {
     995                 :           1 :                                 path = gpath;
     996                 :             : 
     997                 :           2 :                                 path = (Path *) create_sort_path(root, result_rel, path,
     998                 :           1 :                                                                                                  make_pathkeys_for_sortclauses(root, groupList, tlist),
     999                 :             :                                                                                                  -1.0);
    1000                 :             : 
    1001                 :           2 :                                 path = (Path *) create_unique_path(root,
    1002                 :           1 :                                                                                                    result_rel,
    1003                 :           1 :                                                                                                    path,
    1004                 :           1 :                                                                                                    list_length(path->pathkeys),
    1005                 :           1 :                                                                                                    dNumGroups);
    1006                 :           1 :                                 add_path(result_rel, path);
    1007                 :           1 :                         }
    1008                 :         601 :                 }
    1009                 :             : 
    1010                 :             :                 /*
    1011                 :             :                  * Try making a MergeAppend path if we managed to find a path with the
    1012                 :             :                  * correct pathkeys in each union child query.
    1013                 :             :                  */
    1014   [ +  +  +  + ]:         603 :                 if (try_sorted && groupList != NIL)
    1015                 :             :                 {
    1016                 :          34 :                         Path       *path;
    1017                 :             : 
    1018                 :          68 :                         path = (Path *) create_merge_append_path(root,
    1019                 :          34 :                                                                                                          result_rel,
    1020                 :          34 :                                                                                                          ordered.subpaths,
    1021                 :             :                                                                                                          NIL,
    1022                 :          34 :                                                                                                          union_pathkeys,
    1023                 :             :                                                                                                          NULL);
    1024                 :             : 
    1025                 :             :                         /* and make the MergeAppend unique */
    1026                 :          68 :                         path = (Path *) create_unique_path(root,
    1027                 :          34 :                                                                                            result_rel,
    1028                 :          34 :                                                                                            path,
    1029                 :          34 :                                                                                            list_length(tlist),
    1030                 :          34 :                                                                                            dNumGroups);
    1031                 :             : 
    1032                 :          34 :                         add_path(result_rel, path);
    1033                 :          34 :                 }
    1034                 :         603 :         }
    1035                 :             :         else
    1036                 :             :         {
    1037                 :             :                 /* UNION ALL */
    1038                 :          33 :                 add_path(result_rel, apath);
    1039                 :             : 
    1040         [ +  - ]:          33 :                 if (gpath != NULL)
    1041                 :           0 :                         add_path(result_rel, gpath);
    1042                 :             :         }
    1043                 :             : 
    1044                 :         636 :         return result_rel;
    1045                 :         637 : }
    1046                 :             : 
    1047                 :             : /*
    1048                 :             :  * Generate paths for an INTERSECT, INTERSECT ALL, EXCEPT, or EXCEPT ALL node
    1049                 :             :  */
    1050                 :             : static RelOptInfo *
    1051                 :         115 : generate_nonunion_paths(SetOperationStmt *op, PlannerInfo *root,
    1052                 :             :                                                 List *refnames_tlist,
    1053                 :             :                                                 List **pTargetList)
    1054                 :             : {
    1055                 :         115 :         RelOptInfo *result_rel;
    1056                 :         115 :         RelOptInfo *lrel,
    1057                 :             :                            *rrel;
    1058                 :         115 :         double          save_fraction = root->tuple_fraction;
    1059                 :         115 :         Path       *lpath,
    1060                 :             :                            *rpath,
    1061                 :             :                            *path;
    1062                 :         115 :         List       *lpath_tlist,
    1063                 :             :                            *rpath_tlist,
    1064                 :             :                            *tlist,
    1065                 :             :                            *groupList;
    1066                 :         115 :         bool            lpath_trivial_tlist,
    1067                 :             :                                 rpath_trivial_tlist,
    1068                 :             :                                 result_trivial_tlist;
    1069                 :         115 :         List       *nonunion_pathkeys = NIL;
    1070                 :         115 :         double          dLeftGroups,
    1071                 :             :                                 dRightGroups,
    1072                 :             :                                 dNumGroups,
    1073                 :             :                                 dNumOutputRows;
    1074                 :         115 :         bool            can_sort;
    1075                 :         115 :         bool            can_hash;
    1076                 :         115 :         SetOpCmd        cmd;
    1077                 :             : 
    1078                 :             :         /*
    1079                 :             :          * Tell children to fetch all tuples.
    1080                 :             :          */
    1081                 :         115 :         root->tuple_fraction = 0.0;
    1082                 :             : 
    1083                 :             :         /* Recurse on children */
    1084                 :         230 :         lrel = recurse_set_operations(op->larg, root,
    1085                 :         115 :                                                                   op,
    1086                 :         115 :                                                                   op->colTypes, op->colCollations,
    1087                 :         115 :                                                                   refnames_tlist,
    1088                 :             :                                                                   &lpath_tlist,
    1089                 :             :                                                                   &lpath_trivial_tlist);
    1090                 :             : 
    1091                 :         230 :         rrel = recurse_set_operations(op->rarg, root,
    1092                 :         115 :                                                                   op,
    1093                 :         115 :                                                                   op->colTypes, op->colCollations,
    1094                 :         115 :                                                                   refnames_tlist,
    1095                 :             :                                                                   &rpath_tlist,
    1096                 :             :                                                                   &rpath_trivial_tlist);
    1097                 :             : 
    1098                 :             :         /*
    1099                 :             :          * Generate tlist for SetOp plan node.
    1100                 :             :          *
    1101                 :             :          * The tlist for a SetOp plan isn't important so far as the SetOp is
    1102                 :             :          * concerned, but we must make it look real anyway for the benefit of the
    1103                 :             :          * next plan level up.
    1104                 :             :          */
    1105                 :         230 :         tlist = generate_setop_tlist(op->colTypes, op->colCollations,
    1106                 :         115 :                                                                  0, false, lpath_tlist, refnames_tlist,
    1107                 :             :                                                                  &result_trivial_tlist);
    1108                 :             : 
    1109                 :             :         /* We should not have needed any type coercions in the tlist */
    1110         [ +  - ]:         115 :         Assert(result_trivial_tlist);
    1111                 :             : 
    1112                 :         115 :         *pTargetList = tlist;
    1113                 :             : 
    1114                 :             :         /* Identify the grouping semantics */
    1115                 :         115 :         groupList = generate_setop_grouplist(op, tlist);
    1116                 :             : 
    1117                 :             :         /* Check whether the operators support sorting or hashing */
    1118                 :         115 :         can_sort = grouping_is_sortable(groupList);
    1119                 :         115 :         can_hash = grouping_is_hashable(groupList);
    1120   [ -  +  #  # ]:         115 :         if (!can_sort && !can_hash)
    1121   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    1122                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1123                 :             :                 /* translator: %s is INTERSECT or EXCEPT */
    1124                 :             :                                  errmsg("could not implement %s",
    1125                 :             :                                                 (op->op == SETOP_INTERSECT) ? "INTERSECT" : "EXCEPT"),
    1126                 :             :                                  errdetail("Some of the datatypes only support hashing, while others only support sorting.")));
    1127                 :             : 
    1128         [ -  + ]:         115 :         if (can_sort)
    1129                 :             :         {
    1130                 :             :                 /* Determine the pathkeys for sorting by the whole target list */
    1131                 :         230 :                 nonunion_pathkeys = make_pathkeys_for_sortclauses(root, groupList,
    1132                 :         115 :                                                                                                                   tlist);
    1133                 :             : 
    1134                 :         115 :                 root->query_pathkeys = nonunion_pathkeys;
    1135                 :         115 :         }
    1136                 :             : 
    1137                 :             :         /*
    1138                 :             :          * Now that we've got all that info, we can build the child paths.
    1139                 :             :          */
    1140         [ +  + ]:         115 :         if (lrel->rtekind == RTE_SUBQUERY)
    1141                 :         222 :                 build_setop_child_paths(root, lrel, lpath_trivial_tlist, lpath_tlist,
    1142                 :         111 :                                                                 nonunion_pathkeys, &dLeftGroups);
    1143                 :             :         else
    1144                 :           4 :                 dLeftGroups = lrel->rows;
    1145         [ +  + ]:         115 :         if (rrel->rtekind == RTE_SUBQUERY)
    1146                 :         228 :                 build_setop_child_paths(root, rrel, rpath_trivial_tlist, rpath_tlist,
    1147                 :         114 :                                                                 nonunion_pathkeys, &dRightGroups);
    1148                 :             :         else
    1149                 :           1 :                 dRightGroups = rrel->rows;
    1150                 :             : 
    1151                 :             :         /* Undo effects of forcing tuple_fraction to 0 */
    1152                 :         115 :         root->tuple_fraction = save_fraction;
    1153                 :             : 
    1154                 :             :         /*
    1155                 :             :          * For EXCEPT, we must put the left input first.  For INTERSECT, either
    1156                 :             :          * order should give the same results, and we prefer to put the smaller
    1157                 :             :          * input first in order to (a) minimize the size of the hash table in the
    1158                 :             :          * hashing case, and (b) improve our chances of exploiting the executor's
    1159                 :             :          * fast path for empty left-hand input.  "Smaller" means the one with the
    1160                 :             :          * fewer groups.
    1161                 :             :          */
    1162   [ +  +  +  + ]:         115 :         if (op->op != SETOP_EXCEPT && dLeftGroups > dRightGroups)
    1163                 :             :         {
    1164                 :             :                 /* need to swap the two inputs */
    1165                 :           6 :                 RelOptInfo *tmprel;
    1166                 :           6 :                 List       *tmplist;
    1167                 :           6 :                 double          tmpd;
    1168                 :             : 
    1169                 :           6 :                 tmprel = lrel;
    1170                 :           6 :                 lrel = rrel;
    1171                 :           6 :                 rrel = tmprel;
    1172                 :           6 :                 tmplist = lpath_tlist;
    1173                 :           6 :                 lpath_tlist = rpath_tlist;
    1174                 :           6 :                 rpath_tlist = tmplist;
    1175                 :           6 :                 tmpd = dLeftGroups;
    1176                 :           6 :                 dLeftGroups = dRightGroups;
    1177                 :           6 :                 dRightGroups = tmpd;
    1178                 :           6 :         }
    1179                 :             : 
    1180                 :         115 :         lpath = lrel->cheapest_total_path;
    1181                 :         115 :         rpath = rrel->cheapest_total_path;
    1182                 :             : 
    1183                 :             :         /* Build result relation. */
    1184                 :         230 :         result_rel = fetch_upper_rel(root, UPPERREL_SETOP,
    1185                 :         115 :                                                                  bms_union(lrel->relids, rrel->relids));
    1186                 :             : 
    1187                 :             :         /*
    1188                 :             :          * Create the PathTarget and set the width accordingly.  For EXCEPT, since
    1189                 :             :          * the set op result won't contain rows from the rpath, we only account
    1190                 :             :          * for the width of the lpath.  For INTERSECT, use both input paths.
    1191                 :             :          */
    1192         [ +  + ]:         115 :         if (op->op == SETOP_EXCEPT)
    1193                 :         148 :                 result_rel->reltarget = create_setop_pathtarget(root, tlist,
    1194                 :          74 :                                                                                                                 list_make1(lpath));
    1195                 :             :         else
    1196                 :          82 :                 result_rel->reltarget = create_setop_pathtarget(root, tlist,
    1197                 :          41 :                                                                                                                 list_make2(lpath, rpath));
    1198                 :             : 
    1199                 :             :         /* Check for provably empty setop inputs and add short-circuit paths. */
    1200         [ +  + ]:         115 :         if (op->op == SETOP_EXCEPT)
    1201                 :             :         {
    1202                 :             :                 /*
    1203                 :             :                  * For EXCEPTs, if the left side is dummy then there's no need to
    1204                 :             :                  * inspect the right-hand side as scanning the right to find tuples to
    1205                 :             :                  * remove won't make the left-hand input any more empty.
    1206                 :             :                  */
    1207         [ +  + ]:          74 :                 if (is_dummy_rel(lrel))
    1208                 :             :                 {
    1209                 :           1 :                         mark_dummy_rel(result_rel);
    1210                 :             : 
    1211                 :           1 :                         return result_rel;
    1212                 :             :                 }
    1213                 :             : 
    1214                 :             :                 /* Handle EXCEPTs with dummy right input */
    1215         [ +  + ]:          73 :                 if (is_dummy_rel(rrel))
    1216                 :             :                 {
    1217         [ +  - ]:           1 :                         if (op->all)
    1218                 :             :                         {
    1219                 :           1 :                                 Path       *apath;
    1220                 :           1 :                                 AppendPathInput append = {0};
    1221                 :             : 
    1222                 :           1 :                                 append.subpaths = list_make1(lpath);
    1223                 :             : 
    1224                 :             :                                 /*
    1225                 :             :                                  * EXCEPT ALL: If the right-hand input is dummy then we can
    1226                 :             :                                  * simply scan the left-hand input.  To keep createplan.c
    1227                 :             :                                  * happy, use a single child Append to handle the translation
    1228                 :             :                                  * between the set op targetlist and the targetlist of the
    1229                 :             :                                  * left input.  The Append will be removed in setrefs.c.
    1230                 :             :                                  */
    1231                 :           1 :                                 apath = (Path *) create_append_path(root, result_rel,
    1232                 :             :                                                                                                         append, NIL, NULL, 0,
    1233                 :             :                                                                                                         false, -1);
    1234                 :             : 
    1235                 :           1 :                                 add_path(result_rel, apath);
    1236                 :             : 
    1237                 :           1 :                                 return result_rel;
    1238                 :           1 :                         }
    1239                 :             :                         else
    1240                 :             :                         {
    1241                 :             :                                 /*
    1242                 :             :                                  * To make EXCEPT with a dummy RHS work means having to
    1243                 :             :                                  * deduplicate the left input.  That could be done with
    1244                 :             :                                  * AggPaths, but it doesn't seem worth the effort.  Let the
    1245                 :             :                                  * normal path generation code below handle this one.
    1246                 :             :                                  */
    1247                 :             :                         }
    1248                 :           0 :                 }
    1249                 :          72 :         }
    1250                 :             :         else
    1251                 :             :         {
    1252                 :             :                 /*
    1253                 :             :                  * For INTERSECT, if either input is a dummy rel then we can mark the
    1254                 :             :                  * result_rel as dummy since intersecting with an empty relation can
    1255                 :             :                  * never yield any results.  This is true regardless of INTERSECT or
    1256                 :             :                  * INTERSECT ALL.
    1257                 :             :                  */
    1258   [ +  +  -  + ]:          41 :                 if (is_dummy_rel(lrel) || is_dummy_rel(rrel))
    1259                 :             :                 {
    1260                 :           3 :                         mark_dummy_rel(result_rel);
    1261                 :             : 
    1262                 :           3 :                         return result_rel;
    1263                 :             :                 }
    1264                 :             :         }
    1265                 :             : 
    1266                 :             :         /*
    1267                 :             :          * Estimate number of distinct groups that we'll need hashtable entries
    1268                 :             :          * for; this is the size of the left-hand input for EXCEPT, or the smaller
    1269                 :             :          * input for INTERSECT.  Also estimate the number of eventual output rows.
    1270                 :             :          * In non-ALL cases, we estimate each group produces one output row; in
    1271                 :             :          * ALL cases use the relevant relation size.  These are worst-case
    1272                 :             :          * estimates, of course, but we need to be conservative.
    1273                 :             :          */
    1274         [ +  + ]:         110 :         if (op->op == SETOP_EXCEPT)
    1275                 :             :         {
    1276                 :          72 :                 dNumGroups = dLeftGroups;
    1277         [ +  + ]:          72 :                 dNumOutputRows = op->all ? lpath->rows : dNumGroups;
    1278                 :          72 :         }
    1279                 :             :         else
    1280                 :             :         {
    1281                 :          38 :                 dNumGroups = dLeftGroups;
    1282   [ +  +  -  + ]:          38 :                 dNumOutputRows = op->all ? Min(lpath->rows, rpath->rows) : dNumGroups;
    1283                 :             :         }
    1284                 :         110 :         result_rel->rows = dNumOutputRows;
    1285                 :             : 
    1286                 :             :         /* Select the SetOpCmd type */
    1287      [ +  +  - ]:         110 :         switch (op->op)
    1288                 :             :         {
    1289                 :             :                 case SETOP_INTERSECT:
    1290                 :          38 :                         cmd = op->all ? SETOPCMD_INTERSECT_ALL : SETOPCMD_INTERSECT;
    1291                 :          38 :                         break;
    1292                 :             :                 case SETOP_EXCEPT:
    1293                 :          72 :                         cmd = op->all ? SETOPCMD_EXCEPT_ALL : SETOPCMD_EXCEPT;
    1294                 :          72 :                         break;
    1295                 :             :                 default:
    1296   [ #  #  #  # ]:           0 :                         elog(ERROR, "unrecognized set op: %d", (int) op->op);
    1297                 :           0 :                         cmd = SETOPCMD_INTERSECT;       /* keep compiler quiet */
    1298                 :           0 :                         break;
    1299                 :             :         }
    1300                 :             : 
    1301                 :             :         /*
    1302                 :             :          * If we can hash, that just requires a SetOp atop the cheapest inputs.
    1303                 :             :          */
    1304         [ +  + ]:         110 :         if (can_hash)
    1305                 :             :         {
    1306                 :         200 :                 path = (Path *) create_setop_path(root,
    1307                 :         100 :                                                                                   result_rel,
    1308                 :         100 :                                                                                   lpath,
    1309                 :         100 :                                                                                   rpath,
    1310                 :         100 :                                                                                   cmd,
    1311                 :             :                                                                                   SETOP_HASHED,
    1312                 :         100 :                                                                                   groupList,
    1313                 :         100 :                                                                                   dNumGroups,
    1314                 :         100 :                                                                                   dNumOutputRows);
    1315                 :         100 :                 add_path(result_rel, path);
    1316                 :         100 :         }
    1317                 :             : 
    1318                 :             :         /*
    1319                 :             :          * If we can sort, generate the cheapest sorted input paths, and add a
    1320                 :             :          * SetOp atop those.
    1321                 :             :          */
    1322         [ -  + ]:         110 :         if (can_sort)
    1323                 :             :         {
    1324                 :         110 :                 List       *pathkeys;
    1325                 :         110 :                 Path       *slpath,
    1326                 :             :                                    *srpath;
    1327                 :             : 
    1328                 :             :                 /* First the left input ... */
    1329                 :         220 :                 pathkeys = make_pathkeys_for_sortclauses(root,
    1330                 :         110 :                                                                                                  groupList,
    1331                 :         110 :                                                                                                  lpath_tlist);
    1332         [ +  + ]:         110 :                 if (pathkeys_contained_in(pathkeys, lpath->pathkeys))
    1333                 :          16 :                         slpath = lpath;         /* cheapest path is already sorted */
    1334                 :             :                 else
    1335                 :             :                 {
    1336                 :         188 :                         slpath = get_cheapest_path_for_pathkeys(lrel->pathlist,
    1337                 :          94 :                                                                                                         nonunion_pathkeys,
    1338                 :             :                                                                                                         NULL,
    1339                 :             :                                                                                                         TOTAL_COST,
    1340                 :             :                                                                                                         false);
    1341                 :             :                         /* Subquery failed to produce any presorted paths? */
    1342         [ +  + ]:          94 :                         if (slpath == NULL)
    1343                 :          48 :                                 slpath = (Path *) create_sort_path(root,
    1344                 :          24 :                                                                                                    lpath->parent,
    1345                 :          24 :                                                                                                    lpath,
    1346                 :          24 :                                                                                                    pathkeys,
    1347                 :             :                                                                                                    -1.0);
    1348                 :             :                 }
    1349                 :             : 
    1350                 :             :                 /* and now the same for the right. */
    1351                 :         220 :                 pathkeys = make_pathkeys_for_sortclauses(root,
    1352                 :         110 :                                                                                                  groupList,
    1353                 :         110 :                                                                                                  rpath_tlist);
    1354         [ +  + ]:         110 :                 if (pathkeys_contained_in(pathkeys, rpath->pathkeys))
    1355                 :          18 :                         srpath = rpath;         /* cheapest path is already sorted */
    1356                 :             :                 else
    1357                 :             :                 {
    1358                 :         184 :                         srpath = get_cheapest_path_for_pathkeys(rrel->pathlist,
    1359                 :          92 :                                                                                                         nonunion_pathkeys,
    1360                 :             :                                                                                                         NULL,
    1361                 :             :                                                                                                         TOTAL_COST,
    1362                 :             :                                                                                                         false);
    1363                 :             :                         /* Subquery failed to produce any presorted paths? */
    1364         [ +  + ]:          92 :                         if (srpath == NULL)
    1365                 :          50 :                                 srpath = (Path *) create_sort_path(root,
    1366                 :          25 :                                                                                                    rpath->parent,
    1367                 :          25 :                                                                                                    rpath,
    1368                 :          25 :                                                                                                    pathkeys,
    1369                 :             :                                                                                                    -1.0);
    1370                 :             :                 }
    1371                 :             : 
    1372                 :         220 :                 path = (Path *) create_setop_path(root,
    1373                 :         110 :                                                                                   result_rel,
    1374                 :         110 :                                                                                   slpath,
    1375                 :         110 :                                                                                   srpath,
    1376                 :         110 :                                                                                   cmd,
    1377                 :             :                                                                                   SETOP_SORTED,
    1378                 :         110 :                                                                                   groupList,
    1379                 :         110 :                                                                                   dNumGroups,
    1380                 :         110 :                                                                                   dNumOutputRows);
    1381                 :         110 :                 add_path(result_rel, path);
    1382                 :         110 :         }
    1383                 :             : 
    1384                 :         110 :         return result_rel;
    1385                 :         115 : }
    1386                 :             : 
    1387                 :             : /*
    1388                 :             :  * Pull up children of a UNION node that are identically-propertied UNIONs,
    1389                 :             :  * and perform planning of the queries underneath the N-way UNION.
    1390                 :             :  *
    1391                 :             :  * The result is a list of RelOptInfos containing Paths for sub-nodes, with
    1392                 :             :  * one entry for each descendant that is a leaf query or non-identical setop.
    1393                 :             :  * We also return parallel lists of the childrens' targetlists and
    1394                 :             :  * is-trivial-tlist flags.
    1395                 :             :  *
    1396                 :             :  * NOTE: we can also pull a UNION ALL up into a UNION, since the distinct
    1397                 :             :  * output rows will be lost anyway.
    1398                 :             :  */
    1399                 :             : static List *
    1400                 :         637 : plan_union_children(PlannerInfo *root,
    1401                 :             :                                         SetOperationStmt *top_union,
    1402                 :             :                                         List *refnames_tlist,
    1403                 :             :                                         List **tlist_list,
    1404                 :             :                                         List **istrivial_tlist)
    1405                 :             : {
    1406                 :         637 :         List       *pending_rels = list_make1(top_union);
    1407                 :         637 :         List       *result = NIL;
    1408                 :         637 :         List       *child_tlist;
    1409                 :         637 :         bool            trivial_tlist;
    1410                 :             : 
    1411                 :         637 :         *tlist_list = NIL;
    1412                 :         637 :         *istrivial_tlist = NIL;
    1413                 :             : 
    1414         [ +  + ]:        3580 :         while (pending_rels != NIL)
    1415                 :             :         {
    1416                 :        2943 :                 Node       *setOp = linitial(pending_rels);
    1417                 :             : 
    1418                 :        2943 :                 pending_rels = list_delete_first(pending_rels);
    1419                 :             : 
    1420         [ +  + ]:        2943 :                 if (IsA(setOp, SetOperationStmt))
    1421                 :             :                 {
    1422                 :        1174 :                         SetOperationStmt *op = (SetOperationStmt *) setOp;
    1423                 :             : 
    1424         [ +  + ]:        1174 :                         if (op->op == top_union->op &&
    1425         [ +  + ]:        1158 :                                 (op->all == top_union->all || op->all) &&
    1426   [ +  +  -  + ]:        1158 :                                 equal(op->colTypes, top_union->colTypes) &&
    1427                 :        1153 :                                 equal(op->colCollations, top_union->colCollations))
    1428                 :             :                         {
    1429                 :             :                                 /* Same UNION, so fold children into parent */
    1430                 :        1153 :                                 pending_rels = lcons(op->rarg, pending_rels);
    1431                 :        1153 :                                 pending_rels = lcons(op->larg, pending_rels);
    1432                 :        1153 :                                 continue;
    1433                 :             :                         }
    1434         [ +  + ]:        1174 :                 }
    1435                 :             : 
    1436                 :             :                 /*
    1437                 :             :                  * Not same, so plan this child separately.
    1438                 :             :                  *
    1439                 :             :                  * If top_union isn't a UNION ALL, then we are interested in sorted
    1440                 :             :                  * output from the child, so pass top_union as parentOp.  Note that
    1441                 :             :                  * this isn't necessarily the child node's immediate SetOperationStmt
    1442                 :             :                  * parent, but that's fine: it's the effective parent.
    1443                 :             :                  */
    1444                 :        3580 :                 result = lappend(result, recurse_set_operations(setOp, root,
    1445         [ +  + ]:        1790 :                                                                                                                 top_union->all ? NULL : top_union,
    1446                 :        1790 :                                                                                                                 top_union->colTypes,
    1447                 :        1790 :                                                                                                                 top_union->colCollations,
    1448                 :        1790 :                                                                                                                 refnames_tlist,
    1449                 :             :                                                                                                                 &child_tlist,
    1450                 :             :                                                                                                                 &trivial_tlist));
    1451                 :        1790 :                 *tlist_list = lappend(*tlist_list, child_tlist);
    1452                 :        1790 :                 *istrivial_tlist = lappend_int(*istrivial_tlist, trivial_tlist);
    1453      [ -  +  + ]:        2943 :         }
    1454                 :             : 
    1455                 :        1274 :         return result;
    1456                 :         637 : }
    1457                 :             : 
    1458                 :             : /*
    1459                 :             :  * postprocess_setop_rel - perform steps required after adding paths
    1460                 :             :  */
    1461                 :             : static void
    1462                 :        2966 : postprocess_setop_rel(PlannerInfo *root, RelOptInfo *rel)
    1463                 :             : {
    1464                 :             :         /*
    1465                 :             :          * We don't currently worry about allowing FDWs to contribute paths to
    1466                 :             :          * this relation, but give extensions a chance.
    1467                 :             :          */
    1468         [ +  - ]:        2966 :         if (create_upper_paths_hook)
    1469                 :           0 :                 (*create_upper_paths_hook) (root, UPPERREL_SETOP,
    1470                 :           0 :                                                                         NULL, rel, NULL);
    1471                 :             : 
    1472                 :             :         /* Select cheapest path */
    1473                 :        2966 :         set_cheapest(rel);
    1474                 :        2966 : }
    1475                 :             : 
    1476                 :             : /*
    1477                 :             :  * Generate targetlist for a set-operation plan node
    1478                 :             :  *
    1479                 :             :  * colTypes: OID list of set-op's result column datatypes
    1480                 :             :  * colCollations: OID list of set-op's result column collations
    1481                 :             :  * varno: varno to use in generated Vars
    1482                 :             :  * hack_constants: true to copy up constants (see comments in code)
    1483                 :             :  * input_tlist: targetlist of this node's input node
    1484                 :             :  * refnames_tlist: targetlist to take column names from
    1485                 :             :  * trivial_tlist: output parameter, set to true if targetlist is trivial
    1486                 :             :  */
    1487                 :             : static List *
    1488                 :        2258 : generate_setop_tlist(List *colTypes, List *colCollations,
    1489                 :             :                                          Index varno,
    1490                 :             :                                          bool hack_constants,
    1491                 :             :                                          List *input_tlist,
    1492                 :             :                                          List *refnames_tlist,
    1493                 :             :                                          bool *trivial_tlist)
    1494                 :             : {
    1495                 :        2258 :         List       *tlist = NIL;
    1496                 :        2258 :         int                     resno = 1;
    1497                 :        2258 :         ListCell   *ctlc,
    1498                 :             :                            *cclc,
    1499                 :             :                            *itlc,
    1500                 :             :                            *rtlc;
    1501                 :        2258 :         TargetEntry *tle;
    1502                 :        2258 :         Node       *expr;
    1503                 :             : 
    1504                 :        2258 :         *trivial_tlist = true;          /* until proven differently */
    1505                 :             : 
    1506   [ +  +  +  +  :        8607 :         forfour(ctlc, colTypes, cclc, colCollations,
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
             -  -  +  +  
                      + ]
    1507                 :             :                         itlc, input_tlist, rtlc, refnames_tlist)
    1508                 :             :         {
    1509                 :        6349 :                 Oid                     colType = lfirst_oid(ctlc);
    1510                 :        6349 :                 Oid                     colColl = lfirst_oid(cclc);
    1511                 :        6349 :                 TargetEntry *inputtle = (TargetEntry *) lfirst(itlc);
    1512                 :        6349 :                 TargetEntry *reftle = (TargetEntry *) lfirst(rtlc);
    1513                 :             : 
    1514         [ -  + ]:        6349 :                 Assert(inputtle->resno == resno);
    1515         [ -  + ]:        6349 :                 Assert(reftle->resno == resno);
    1516         [ -  + ]:        6349 :                 Assert(!inputtle->resjunk);
    1517         [ -  + ]:        6349 :                 Assert(!reftle->resjunk);
    1518                 :             : 
    1519                 :             :                 /*
    1520                 :             :                  * Generate columns referencing input columns and having appropriate
    1521                 :             :                  * data types and column names.  Insert datatype coercions where
    1522                 :             :                  * necessary.
    1523                 :             :                  *
    1524                 :             :                  * HACK: constants in the input's targetlist are copied up as-is
    1525                 :             :                  * rather than being referenced as subquery outputs.  This is mainly
    1526                 :             :                  * to ensure that when we try to coerce them to the output column's
    1527                 :             :                  * datatype, the right things happen for UNKNOWN constants.  But do
    1528                 :             :                  * this only at the first level of subquery-scan plans; we don't want
    1529                 :             :                  * phony constants appearing in the output tlists of upper-level
    1530                 :             :                  * nodes!
    1531                 :             :                  *
    1532                 :             :                  * Note that copying a constant doesn't in itself require us to mark
    1533                 :             :                  * the tlist nontrivial; see trivial_subqueryscan() in setrefs.c.
    1534                 :             :                  */
    1535   [ +  +  +  -  :        6349 :                 if (hack_constants && inputtle->expr && IsA(inputtle->expr, Const))
                   +  + ]
    1536                 :        2257 :                         expr = (Node *) inputtle->expr;
    1537                 :             :                 else
    1538                 :        8184 :                         expr = (Node *) makeVar(varno,
    1539                 :        4092 :                                                                         inputtle->resno,
    1540                 :        4092 :                                                                         exprType((Node *) inputtle->expr),
    1541                 :        4092 :                                                                         exprTypmod((Node *) inputtle->expr),
    1542                 :        4092 :                                                                         exprCollation((Node *) inputtle->expr),
    1543                 :             :                                                                         0);
    1544                 :             : 
    1545         [ +  + ]:        6349 :                 if (exprType(expr) != colType)
    1546                 :             :                 {
    1547                 :             :                         /*
    1548                 :             :                          * Note: it's not really cool to be applying coerce_to_common_type
    1549                 :             :                          * here; one notable point is that assign_expr_collations never
    1550                 :             :                          * gets run on any generated nodes.  For the moment that's not a
    1551                 :             :                          * problem because we force the correct exposed collation below.
    1552                 :             :                          * It would likely be best to make the parser generate the correct
    1553                 :             :                          * output tlist for every set-op to begin with, though.
    1554                 :             :                          */
    1555                 :          42 :                         expr = coerce_to_common_type(NULL,      /* no UNKNOWNs here */
    1556                 :          42 :                                                                                  expr,
    1557                 :          42 :                                                                                  colType,
    1558                 :             :                                                                                  "UNION/INTERSECT/EXCEPT");
    1559                 :          42 :                         *trivial_tlist = false; /* the coercion makes it not trivial */
    1560                 :          42 :                 }
    1561                 :             : 
    1562                 :             :                 /*
    1563                 :             :                  * Ensure the tlist entry's exposed collation matches the set-op. This
    1564                 :             :                  * is necessary because plan_set_operations() reports the result
    1565                 :             :                  * ordering as a list of SortGroupClauses, which don't carry collation
    1566                 :             :                  * themselves but just refer to tlist entries.  If we don't show the
    1567                 :             :                  * right collation then planner.c might do the wrong thing in
    1568                 :             :                  * higher-level queries.
    1569                 :             :                  *
    1570                 :             :                  * Note we use RelabelType, not CollateExpr, since this expression
    1571                 :             :                  * will reach the executor without any further processing.
    1572                 :             :                  */
    1573         [ +  + ]:        6349 :                 if (exprCollation(expr) != colColl)
    1574                 :             :                 {
    1575                 :        4068 :                         expr = applyRelabelType(expr,
    1576                 :        2034 :                                                                         exprType(expr), exprTypmod(expr), colColl,
    1577                 :             :                                                                         COERCE_IMPLICIT_CAST, -1, false);
    1578                 :        2034 :                         *trivial_tlist = false; /* the relabel makes it not trivial */
    1579                 :        2034 :                 }
    1580                 :             : 
    1581                 :       12698 :                 tle = makeTargetEntry((Expr *) expr,
    1582                 :        6349 :                                                           (AttrNumber) resno++,
    1583                 :        6349 :                                                           pstrdup(reftle->resname),
    1584                 :             :                                                           false);
    1585                 :             : 
    1586                 :             :                 /*
    1587                 :             :                  * By convention, all output columns in a setop tree have
    1588                 :             :                  * ressortgroupref equal to their resno.  In some cases the ref isn't
    1589                 :             :                  * needed, but this is a cleaner way than modifying the tlist later.
    1590                 :             :                  */
    1591                 :        6349 :                 tle->ressortgroupref = tle->resno;
    1592                 :             : 
    1593                 :        6349 :                 tlist = lappend(tlist, tle);
    1594                 :        6349 :         }
    1595                 :             : 
    1596                 :        4516 :         return tlist;
    1597                 :        2258 : }
    1598                 :             : 
    1599                 :             : /*
    1600                 :             :  * Generate targetlist for a set-operation Append node
    1601                 :             :  *
    1602                 :             :  * colTypes: OID list of set-op's result column datatypes
    1603                 :             :  * colCollations: OID list of set-op's result column collations
    1604                 :             :  * input_tlists: list of tlists for sub-plans of the Append
    1605                 :             :  * refnames_tlist: targetlist to take column names from
    1606                 :             :  *
    1607                 :             :  * The entries in the Append's targetlist should always be simple Vars;
    1608                 :             :  * we just have to make sure they have the right datatypes/typmods/collations.
    1609                 :             :  * The Vars are always generated with varno 0.
    1610                 :             :  *
    1611                 :             :  * XXX a problem with the varno-zero approach is that set_pathtarget_cost_width
    1612                 :             :  * cannot figure out a realistic width for the tlist we make here.  But we
    1613                 :             :  * ought to refactor this code to produce a PathTarget directly, anyway.
    1614                 :             :  */
    1615                 :             : static List *
    1616                 :         711 : generate_append_tlist(List *colTypes, List *colCollations,
    1617                 :             :                                           List *input_tlists,
    1618                 :             :                                           List *refnames_tlist)
    1619                 :             : {
    1620                 :         711 :         List       *tlist = NIL;
    1621                 :         711 :         int                     resno = 1;
    1622                 :         711 :         ListCell   *curColType;
    1623                 :         711 :         ListCell   *curColCollation;
    1624                 :         711 :         ListCell   *ref_tl_item;
    1625                 :         711 :         int                     colindex;
    1626                 :         711 :         TargetEntry *tle;
    1627                 :         711 :         Node       *expr;
    1628                 :         711 :         ListCell   *tlistl;
    1629                 :         711 :         int32      *colTypmods;
    1630                 :             : 
    1631                 :             :         /*
    1632                 :             :          * First extract typmods to use.
    1633                 :             :          *
    1634                 :             :          * If the inputs all agree on type and typmod of a particular column, use
    1635                 :             :          * that typmod; else use -1.
    1636                 :             :          */
    1637                 :         711 :         colTypmods = palloc_array(int32, list_length(colTypes));
    1638                 :             : 
    1639   [ +  -  +  +  :        2649 :         foreach(tlistl, input_tlists)
                   +  + ]
    1640                 :             :         {
    1641                 :        1938 :                 List       *subtlist = (List *) lfirst(tlistl);
    1642                 :        1938 :                 ListCell   *subtlistl;
    1643                 :             : 
    1644                 :        1938 :                 curColType = list_head(colTypes);
    1645                 :        1938 :                 colindex = 0;
    1646   [ +  +  +  +  :        7234 :                 foreach(subtlistl, subtlist)
                   +  + ]
    1647                 :             :                 {
    1648                 :        5296 :                         TargetEntry *subtle = (TargetEntry *) lfirst(subtlistl);
    1649                 :             : 
    1650         [ +  - ]:        5296 :                         Assert(!subtle->resjunk);
    1651         [ +  - ]:        5296 :                         Assert(curColType != NULL);
    1652         [ +  - ]:        5296 :                         if (exprType((Node *) subtle->expr) == lfirst_oid(curColType))
    1653                 :             :                         {
    1654                 :             :                                 /* If first subplan, copy the typmod; else compare */
    1655                 :        5296 :                                 int32           subtypmod = exprTypmod((Node *) subtle->expr);
    1656                 :             : 
    1657         [ +  + ]:        5296 :                                 if (tlistl == list_head(input_tlists))
    1658                 :        1881 :                                         colTypmods[colindex] = subtypmod;
    1659         [ +  + ]:        3415 :                                 else if (subtypmod != colTypmods[colindex])
    1660                 :           2 :                                         colTypmods[colindex] = -1;
    1661                 :        5296 :                         }
    1662                 :             :                         else
    1663                 :             :                         {
    1664                 :             :                                 /* types disagree, so force typmod to -1 */
    1665                 :           0 :                                 colTypmods[colindex] = -1;
    1666                 :             :                         }
    1667                 :        5296 :                         curColType = lnext(colTypes, curColType);
    1668                 :        5296 :                         colindex++;
    1669                 :        5296 :                 }
    1670         [ +  - ]:        1938 :                 Assert(curColType == NULL);
    1671                 :        1938 :         }
    1672                 :             : 
    1673                 :             :         /*
    1674                 :             :          * Now we can build the tlist for the Append.
    1675                 :             :          */
    1676                 :         711 :         colindex = 0;
    1677   [ +  +  +  +  :        2592 :         forthree(curColType, colTypes, curColCollation, colCollations,
          +  +  +  +  +  
          +  +  +  +  +  
             -  +  +  + ]
    1678                 :             :                          ref_tl_item, refnames_tlist)
    1679                 :             :         {
    1680                 :        1881 :                 Oid                     colType = lfirst_oid(curColType);
    1681                 :        1881 :                 int32           colTypmod = colTypmods[colindex++];
    1682                 :        1881 :                 Oid                     colColl = lfirst_oid(curColCollation);
    1683                 :        1881 :                 TargetEntry *reftle = (TargetEntry *) lfirst(ref_tl_item);
    1684                 :             : 
    1685         [ +  - ]:        1881 :                 Assert(reftle->resno == resno);
    1686         [ +  - ]:        1881 :                 Assert(!reftle->resjunk);
    1687                 :        1881 :                 expr = (Node *) makeVar(0,
    1688                 :        1881 :                                                                 resno,
    1689                 :        1881 :                                                                 colType,
    1690                 :        1881 :                                                                 colTypmod,
    1691                 :        1881 :                                                                 colColl,
    1692                 :             :                                                                 0);
    1693                 :        3762 :                 tle = makeTargetEntry((Expr *) expr,
    1694                 :        1881 :                                                           (AttrNumber) resno++,
    1695                 :        1881 :                                                           pstrdup(reftle->resname),
    1696                 :             :                                                           false);
    1697                 :             : 
    1698                 :             :                 /*
    1699                 :             :                  * By convention, all output columns in a setop tree have
    1700                 :             :                  * ressortgroupref equal to their resno.  In some cases the ref isn't
    1701                 :             :                  * needed, but this is a cleaner way than modifying the tlist later.
    1702                 :             :                  */
    1703                 :        1881 :                 tle->ressortgroupref = tle->resno;
    1704                 :             : 
    1705                 :        1881 :                 tlist = lappend(tlist, tle);
    1706                 :        1881 :         }
    1707                 :             : 
    1708                 :         711 :         pfree(colTypmods);
    1709                 :             : 
    1710                 :        1422 :         return tlist;
    1711                 :         711 : }
    1712                 :             : 
    1713                 :             : /*
    1714                 :             :  * generate_setop_grouplist
    1715                 :             :  *              Build a SortGroupClause list defining the sort/grouping properties
    1716                 :             :  *              of the setop's output columns.
    1717                 :             :  *
    1718                 :             :  * Parse analysis already determined the properties and built a suitable
    1719                 :             :  * list, except that the entries do not have sortgrouprefs set because
    1720                 :             :  * the parser output representation doesn't include a tlist for each
    1721                 :             :  * setop.  So what we need to do here is copy that list and install
    1722                 :             :  * proper sortgrouprefs into it (copying those from the targetlist).
    1723                 :             :  */
    1724                 :             : static List *
    1725                 :         733 : generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
    1726                 :             : {
    1727                 :         733 :         List       *grouplist = copyObject(op->groupClauses);
    1728                 :         733 :         ListCell   *lg;
    1729                 :         733 :         ListCell   *lt;
    1730                 :             : 
    1731                 :         733 :         lg = list_head(grouplist);
    1732   [ +  +  +  +  :        2770 :         foreach(lt, targetlist)
                   +  + ]
    1733                 :             :         {
    1734                 :        2037 :                 TargetEntry *tle = (TargetEntry *) lfirst(lt);
    1735                 :        2037 :                 SortGroupClause *sgc;
    1736                 :             : 
    1737         [ -  + ]:        2037 :                 Assert(!tle->resjunk);
    1738                 :             : 
    1739                 :             :                 /* non-resjunk columns should have sortgroupref = resno */
    1740         [ -  + ]:        2037 :                 Assert(tle->ressortgroupref == tle->resno);
    1741                 :             : 
    1742                 :             :                 /* non-resjunk columns should have grouping clauses */
    1743         [ -  + ]:        2037 :                 Assert(lg != NULL);
    1744                 :        2037 :                 sgc = (SortGroupClause *) lfirst(lg);
    1745                 :        2037 :                 lg = lnext(grouplist, lg);
    1746         [ -  + ]:        2037 :                 Assert(sgc->tleSortGroupRef == 0);
    1747                 :             : 
    1748                 :        2037 :                 sgc->tleSortGroupRef = tle->ressortgroupref;
    1749                 :        2037 :         }
    1750         [ +  - ]:         733 :         Assert(lg == NULL);
    1751                 :        1466 :         return grouplist;
    1752                 :         733 : }
    1753                 :             : 
    1754                 :             : /*
    1755                 :             :  * create_setop_pathtarget
    1756                 :             :  *              Do the normal create_pathtarget() work, plus set the resulting
    1757                 :             :  *              PathTarget's width to the average width of the Paths in child_pathlist
    1758                 :             :  *              weighted using the estimated row count of each path.
    1759                 :             :  *
    1760                 :             :  * Note: This is required because set op target lists use varno==0, which
    1761                 :             :  * results in a type default width estimate rather than one that's based on
    1762                 :             :  * statistics of the columns from the set op children.
    1763                 :             :  */
    1764                 :             : static PathTarget *
    1765                 :         752 : create_setop_pathtarget(PlannerInfo *root, List *tlist, List *child_pathlist)
    1766                 :             : {
    1767                 :         752 :         PathTarget *reltarget;
    1768                 :         752 :         ListCell   *lc;
    1769                 :         752 :         double          parent_rows = 0;
    1770                 :         752 :         double          parent_size = 0;
    1771                 :             : 
    1772                 :         752 :         reltarget = create_pathtarget(root, tlist);
    1773                 :             : 
    1774                 :             :         /* Calculate the total rows and total size. */
    1775   [ +  +  +  +  :        2692 :         foreach(lc, child_pathlist)
                   +  + ]
    1776                 :             :         {
    1777                 :        1940 :                 Path       *path = (Path *) lfirst(lc);
    1778                 :             : 
    1779                 :        1940 :                 parent_rows += path->rows;
    1780                 :        1940 :                 parent_size += path->parent->reltarget->width * path->rows;
    1781                 :        1940 :         }
    1782                 :             : 
    1783         [ +  + ]:         752 :         if (parent_rows > 0)
    1784                 :         749 :                 reltarget->width = rint(parent_size / parent_rows);
    1785                 :             : 
    1786                 :        1504 :         return reltarget;
    1787                 :         752 : }
        

Generated by: LCOV version 2.3.2-1