Relids required_outer);
  static void accumulate_append_subpath(Path *path,
                           List **subpaths, List **special_subpaths);
 +static void set_dummy_rel_pathlist(RelOptInfo *rel);
  static void set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
                       Index rti, RangeTblEntry *rte);
  static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel,
       * Consider an append of unordered, unparameterized partial paths.  Make
      * it parallel-aware if possible.
      */
 -   if (partial_subpaths_valid)
 +   if (partial_subpaths_valid && partial_subpaths != NIL)
     {
         AppendPath *appendpath;
         ListCell   *lc;
    *   Build a dummy path for a relation that's been excluded by constraints
   *
   * Rather than inventing a special "dummy" path type, we represent this as an
 - * AppendPath with no members (see also IS_DUMMY_PATH/IS_DUMMY_REL macros).
 + * AppendPath with no members (see also IS_DUMMY_APPEND/IS_DUMMY_REL macros).
   *
 - * This is exported because inheritance_planner() has need for it.
 + * (See also mark_dummy_rel, which does basically the same thing, but is
 + * typically used to change a rel into dummy state after we already made
 + * paths for it.)
   */
 -void
 +static void
  set_dummy_rel_pathlist(RelOptInfo *rel)
  {
     /* Set dummy size estimates --- we leave attr_widths[] as zeroes */
      rel->pathlist = NIL;
     rel->partial_pathlist = NIL;
  
 +   /* Set up the dummy path */
     add_path(rel, (Path *) create_append_path(NULL, rel, NIL, NIL, NULL,
                                               0, false, NIL, -1));
  
     /*
 -    * We set the cheapest path immediately, to ensure that IS_DUMMY_REL()
 -    * will recognize the relation as dummy if anyone asks.  This is redundant
 -    * when we're called from set_rel_size(), but not when called from
 -    * elsewhere, and doing it twice is harmless anyway.
 +    * We set the cheapest-path fields immediately, just in case they were
 +    * pointing at some discarded path.  This is redundant when we're called
 +    * from set_rel_size(), but not when called from elsewhere, and doing it
 +    * twice is harmless anyway.
      */
     set_cheapest(rel);
  }
          /* Add partitionwise join paths for partitioned child-joins. */
         generate_partitionwise_join_paths(root, child_rel);
  
 +       set_cheapest(child_rel);
 +
         /* Dummy children will not be scanned, so ignore those. */
         if (IS_DUMMY_REL(child_rel))
             continue;
  
 -       set_cheapest(child_rel);
 -
  #ifdef OPTIMIZER_DEBUG
         debug_print_rel(root, child_rel);
  #endif
                  * If this child rel was excluded by constraint exclusion, exclude it
          * from the result plan.
          */
 -       if (IS_DUMMY_PATH(subpath))
 +       if (IS_DUMMY_REL(sub_final_rel))
             continue;
  
         /*
   }
  
  
 -
 -/*
 - * Detect whether a plan node is a "dummy" plan created when a relation
 - * is deemed not to need scanning due to constraint exclusion.
 - *
 - * Currently, such dummy plans are Result nodes with constant FALSE
 - * filter quals (see set_dummy_rel_pathlist and create_append_plan).
 - *
 - * XXX this probably ought to be somewhere else, but not clear where.
 - */
 -bool
 -is_dummy_plan(Plan *plan)
 -{
 -   if (IsA(plan, Result))
 -   {
 -       List       *rcqual = (List *) ((Result *) plan)->resconstantqual;
 -
 -       if (list_length(rcqual) == 1)
 -       {
 -           Const      *constqual = (Const *) linitial(rcqual);
 -
 -           if (constqual && IsA(constqual, Const))
 -           {
 -               if (!constqual->constisnull &&
 -                   !DatumGetBool(constqual->constvalue))
 -                   return true;
 -           }
 -       }
 -   }
 -   return false;
 -}
 -
  /*
   * preprocess_rowmarks - set up PlanRowMarks if needed
   */
       * If this is the topmost grouping relation or if the parent relation is
      * doing some form of partitionwise aggregation, then we may be able to do
      * it at this level also.  However, if the input relation is not
 -    * partitioned, partitionwise aggregate is impossible, and if it is dummy,
 -    * partitionwise aggregate is pointless.
 +    * partitioned, partitionwise aggregate is impossible.
      */
     if (extra->patype != PARTITIONWISE_AGGREGATE_NONE &&
 -       input_rel->part_scheme && input_rel->part_rels &&
 -       !IS_DUMMY_REL(input_rel))
 +       IS_PARTITIONED_REL(input_rel))
     {
         /*
          * If this is the topmost relation or if the parent relation is doing
                                 bool scanjoin_target_parallel_safe,
                                bool tlist_same_exprs)
  {
 -   ListCell   *lc;
 +   bool        rel_is_partitioned = IS_PARTITIONED_REL(rel);
     PathTarget *scanjoin_target;
 -   bool        is_dummy_rel = IS_DUMMY_REL(rel);
 +   ListCell   *lc;
  
 +   /* This recurses, so be paranoid. */
     check_stack_depth();
  
 +   /*
 +    * If the rel is partitioned, we want to drop its existing paths and
 +    * generate new ones.  This function would still be correct if we kept the
 +    * existing paths: we'd modify them to generate the correct target above
 +    * the partitioning Append, and then they'd compete on cost with paths
 +    * generating the target below the Append.  However, in our current cost
 +    * model the latter way is always the same or cheaper cost, so modifying
 +    * the existing paths would just be useless work.  Moreover, when the cost
 +    * is the same, varying roundoff errors might sometimes allow an existing
 +    * path to be picked, resulting in undesirable cross-platform plan
 +    * variations.  So we drop old paths and thereby force the work to be done
 +    * below the Append, except in the case of a non-parallel-safe target.
 +    *
 +    * Some care is needed, because we have to allow generate_gather_paths to
 +    * see the old partial paths in the next stanza.  Hence, zap the main
 +    * pathlist here, then allow generate_gather_paths to add path(s) to the
 +    * main list, and finally zap the partial pathlist.
 +    */
 +   if (rel_is_partitioned)
 +       rel->pathlist = NIL;
 +
     /*
      * If the scan/join target is not parallel-safe, partial paths cannot
      * generate it.
      if (!scanjoin_target_parallel_safe)
     {
         /*
 -        * Since we can't generate the final scan/join target, this is our
 -        * last opportunity to use any partial paths that exist.  We don't do
 -        * this if the case where the target is parallel-safe, since we will
 -        * be able to generate superior paths by doing it after the final
 -        * scan/join target has been applied.
 -        *
 -        * Note that this may invalidate rel->cheapest_total_path, so we must
 -        * not rely on it after this point without first calling set_cheapest.
 +        * Since we can't generate the final scan/join target in parallel
 +        * workers, this is our last opportunity to use any partial paths that
 +        * exist; so build Gather path(s) that use them and emit whatever the
 +        * current reltarget is.  We don't do this in the case where the
 +        * target is parallel-safe, since we will be able to generate superior
 +        * paths by doing it after the final scan/join target has been
 +        * applied.
          */
         generate_gather_paths(root, rel, false);
  
          rel->consider_parallel = false;
     }
  
 -   /*
 -    * Update the reltarget.  This may not be strictly necessary in all cases,
 -    * but it is at least necessary when create_append_path() gets called
 -    * below directly or indirectly, since that function uses the reltarget as
 -    * the pathtarget for the resulting path.  It seems like a good idea to do
 -    * it unconditionally.
 -    */
 -   rel->reltarget = llast_node(PathTarget, scanjoin_targets);
 -
 -   /* Special case: handle dummy relations separately. */
 -   if (is_dummy_rel)
 -   {
 -       /*
 -        * Since this is a dummy rel, it's got a single Append path with no
 -        * child paths.  Replace it with a new path having the final scan/join
 -        * target.  (Note that since Append is not projection-capable, it
 -        * would be bad to handle this using the general purpose code below;
 -        * we'd end up putting a ProjectionPath on top of the existing Append
 -        * node, which would cause this relation to stop appearing to be a
 -        * dummy rel.)
 -        */
 -       rel->pathlist = list_make1(create_append_path(root, rel, NIL, NIL,
 -                                                     NULL, 0, false, NIL,
 -                                                     -1));
 +   /* Finish dropping old paths for a partitioned rel, per comment above */
 +   if (rel_is_partitioned)
         rel->partial_pathlist = NIL;
 -       set_cheapest(rel);
 -       Assert(IS_DUMMY_REL(rel));
 -
 -       /*
 -        * Forget about any child relations.  There's no point in adjusting
 -        * them and no point in using them for later planning stages (in
 -        * particular, partitionwise aggregate).
 -        */
 -       rel->nparts = 0;
 -       rel->part_rels = NULL;
 -       rel->boundinfo = NULL;
 -
 -       return;
 -   }
  
     /* Extract SRF-free scan/join target. */
     scanjoin_target = linitial_node(PathTarget, scanjoin_targets);
  
     /*
 -    * Adjust each input path.  If the tlist exprs are the same, we can just
 -    * inject the sortgroupref information into the existing pathtarget.
 -    * Otherwise, replace each path with a projection path that generates the
 -    * SRF-free scan/join target.  This can't change the ordering of paths
 -    * within rel->pathlist, so we just modify the list in place.
 +    * Apply the SRF-free scan/join target to each existing path.
 +    *
 +    * If the tlist exprs are the same, we can just inject the sortgroupref
 +    * information into the existing pathtargets.  Otherwise, replace each
 +    * path with a projection path that generates the SRF-free scan/join
 +    * target.  This can't change the ordering of paths within rel->pathlist,
 +    * so we just modify the list in place.
      */
     foreach(lc, rel->pathlist)
     {
         Path       *subpath = (Path *) lfirst(lc);
 -       Path       *newpath;
  
 +       /* Shouldn't have any parameterized paths anymore */
         Assert(subpath->param_info == NULL);
  
         if (tlist_same_exprs)
                  scanjoin_target->sortgrouprefs;
         else
         {
 +           Path       *newpath;
 +
             newpath = (Path *) create_projection_path(root, rel, subpath,
                                                       scanjoin_target);
             lfirst(lc) = newpath;
         }
     }
  
 -   /* Same for partial paths. */
 +   /* Likewise adjust the targets for any partial paths. */
     foreach(lc, rel->partial_pathlist)
     {
         Path       *subpath = (Path *) lfirst(lc);
 -       Path       *newpath;
  
         /* Shouldn't have any parameterized paths anymore */
         Assert(subpath->param_info == NULL);
                  scanjoin_target->sortgrouprefs;
         else
         {
 -           newpath = (Path *) create_projection_path(root,
 -                                                     rel,
 -                                                     subpath,
 +           Path       *newpath;
 +
 +           newpath = (Path *) create_projection_path(root, rel, subpath,
                                                       scanjoin_target);
             lfirst(lc) = newpath;
         }
     }
  
 -   /* Now fix things up if scan/join target contains SRFs */
 +   /*
 +    * Now, if final scan/join target contains SRFs, insert ProjectSetPath(s)
 +    * atop each existing path.  (Note that this function doesn't look at the
 +    * cheapest-path fields, which is a good thing because they're bogus right
 +    * now.)
 +    */
     if (root->parse->hasTargetSRFs)
         adjust_paths_for_srfs(root, rel,
                               scanjoin_targets,
                               scanjoin_targets_contain_srfs);
  
     /*
 -    * If the relation is partitioned, recursively apply the same changes to
 -    * all partitions and generate new Append paths.  Since Append is not
 -    * projection-capable, that might save a separate Result node, and it also
 -    * is important for partitionwise aggregate.
 +    * Update the rel's target to be the final (with SRFs) scan/join target.
 +    * This now matches the actual output of all the paths, and we might get
 +    * confused in createplan.c if they don't agree.  We must do this now so
 +    * that any append paths made in the next part will use the correct
 +    * pathtarget (cf. create_append_path).
      */
 -   if (rel->part_scheme && rel->part_rels)
 +   rel->reltarget = llast_node(PathTarget, scanjoin_targets);
 +
 +   /*
 +    * If the relation is partitioned, recursively apply the scan/join target
 +    * to all partitions, and generate brand-new Append paths in which the
 +    * scan/join target is computed below the Append rather than above it.
 +    * Since Append is not projection-capable, that might save a separate
 +    * Result node, and it also is important for partitionwise aggregate.
 +    */
 +   if (rel_is_partitioned)
     {
 -       int         partition_idx;
         List       *live_children = NIL;
 +       int         partition_idx;
  
         /* Adjust each partition. */
         for (partition_idx = 0; partition_idx < rel->nparts; partition_idx++)
         {
             RelOptInfo *child_rel = rel->part_rels[partition_idx];
 -           ListCell   *lc;
             AppendRelInfo **appinfos;
             int         nappinfos;
             List       *child_scanjoin_targets = NIL;
 +           ListCell   *lc;
  
             /* Translate scan/join targets for this child. */
             appinfos = find_appinfos_by_relids(root, child_rel->relids,
          }
  
         /* Build new paths for this relation by appending child paths. */
 -       if (live_children != NIL)
 -           add_paths_to_append_rel(root, rel, live_children);
 +       add_paths_to_append_rel(root, rel, live_children);
     }
  
     /*
           *
   * It's not enough to test whether rel->part_scheme is set, because it might
   * be that the basic partitioning properties of the input relations matched
 - * but the partition bounds did not.
 - *
 - * We treat dummy relations as unpartitioned.  We could alternatively
 - * treat them as partitioned, but it's not clear whether that's a useful thing
 - * to do.
 + * but the partition bounds did not.  Also, if we are able to prove a rel
 + * dummy (empty), we should henceforth treat it as unpartitioned.
   */
  #define IS_PARTITIONED_REL(rel) \
     ((rel)->part_scheme && (rel)->boundinfo && (rel)->nparts > 0 && \
 -    (rel)->part_rels && !(IS_DUMMY_REL(rel)))
 +    (rel)->part_rels && !IS_DUMMY_REL(rel))
  
  /*
   * Convenience macro to make sure that a partitioned relation has all the
    * elements.  These cases are optimized during create_append_plan.
   * In particular, an AppendPath with no subpaths is a "dummy" path that
   * is created to represent the case that a relation is provably empty.
 + * (This is a convenient representation because it means that when we build
 + * an appendrel and find that all its children have been excluded, no extra
 + * action is needed to recognize the relation as dummy.)
   */
  typedef struct AppendPath
  {
      int         first_partial_path;
  } AppendPath;
  
 -#define IS_DUMMY_PATH(p) \
 +#define IS_DUMMY_APPEND(p) \
     (IsA((p), AppendPath) && ((AppendPath *) (p))->subpaths == NIL)
  
 -/* A relation that's been proven empty will have one path that is dummy */
 -#define IS_DUMMY_REL(r) \
 -   ((r)->cheapest_total_path != NULL && \
 -    IS_DUMMY_PATH((r)->cheapest_total_path))
 +/*
 + * A relation that's been proven empty will have one path that is dummy
 + * (but might have projection paths on top).  For historical reasons,
 + * this is provided as a macro that wraps is_dummy_rel().
 + */
 +#define IS_DUMMY_REL(r) is_dummy_rel(r)
 +extern bool is_dummy_rel(RelOptInfo *rel);
  
  /*
   * MergeAppendPath represents a MergeAppend plan, ie, the merging of sorted