]> granicus.if.org Git - postgresql/blobdiff - src/backend/optimizer/plan/createplan.c
Spelling fixes in code comments
[postgresql] / src / backend / optimizer / plan / createplan.c
index e6bd4c326ca883c8ddb5dbdae964d19d33871af3..89e1946fc2622f26aba95c3ea800160ce5e4d002 100644 (file)
@@ -5,7 +5,7 @@
  *       Planning is complete, we just need to convert the selected
  *       Path into a Plan.
  *
- * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
 #include <limits.h>
 #include <math.h>
 
-#include "access/skey.h"
+#include "access/stratnum.h"
+#include "access/sysattr.h"
 #include "catalog/pg_class.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
+#include "nodes/extensible.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
 #include "utils/lsyscache.h"
 
 
-static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path);
-static Plan *create_scan_plan(PlannerInfo *root, Path *best_path);
+/*
+ * Flag bits that can appear in the flags argument of create_plan_recurse().
+ * These can be OR-ed together.
+ *
+ * CP_EXACT_TLIST specifies that the generated plan node must return exactly
+ * the tlist specified by the path's pathtarget (this overrides both
+ * CP_SMALL_TLIST and CP_LABEL_TLIST, if those are set).  Otherwise, the
+ * plan node is allowed to return just the Vars and PlaceHolderVars needed
+ * to evaluate the pathtarget.
+ *
+ * CP_SMALL_TLIST specifies that a narrower tlist is preferred.  This is
+ * passed down by parent nodes such as Sort and Hash, which will have to
+ * store the returned tuples.
+ *
+ * CP_LABEL_TLIST specifies that the plan node must return columns matching
+ * any sortgrouprefs specified in its pathtarget, with appropriate
+ * ressortgroupref labels.  This is passed down by parent nodes such as Sort
+ * and Group, which need these values to be available in their inputs.
+ */
+#define CP_EXACT_TLIST         0x0001          /* Plan must return specified tlist */
+#define CP_SMALL_TLIST         0x0002          /* Prefer narrower tlists */
+#define CP_LABEL_TLIST         0x0004          /* tlist must contain sortgrouprefs */
+
+
+static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path,
+                                       int flags);
+static Plan *create_scan_plan(PlannerInfo *root, Path *best_path,
+                                int flags);
 static List *build_path_tlist(PlannerInfo *root, Path *path);
-static bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel);
-static void disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path);
-static Plan *create_gating_plan(PlannerInfo *root, Plan *plan, List *quals);
+static bool use_physical_tlist(PlannerInfo *root, Path *path, int flags);
+static List *get_gating_quals(PlannerInfo *root, List *quals);
+static Plan *create_gating_plan(PlannerInfo *root, Path *path, Plan *plan,
+                                  List *gating_quals);
 static Plan *create_join_plan(PlannerInfo *root, JoinPath *best_path);
 static Plan *create_append_plan(PlannerInfo *root, AppendPath *best_path);
 static Plan *create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path);
 static Result *create_result_plan(PlannerInfo *root, ResultPath *best_path);
-static Material *create_material_plan(PlannerInfo *root, MaterialPath *best_path);
-static Plan *create_unique_plan(PlannerInfo *root, UniquePath *best_path);
+static ProjectSet *create_project_set_plan(PlannerInfo *root, ProjectSetPath *best_path);
+static Material *create_material_plan(PlannerInfo *root, MaterialPath *best_path,
+                                        int flags);
+static Plan *create_unique_plan(PlannerInfo *root, UniquePath *best_path,
+                                  int flags);
+static Gather *create_gather_plan(PlannerInfo *root, GatherPath *best_path);
+static Plan *create_projection_plan(PlannerInfo *root, ProjectionPath *best_path);
+static Plan *inject_projection_plan(Plan *subplan, List *tlist);
+static Sort *create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags);
+static Group *create_group_plan(PlannerInfo *root, GroupPath *best_path);
+static Unique *create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path,
+                                                int flags);
+static Agg *create_agg_plan(PlannerInfo *root, AggPath *best_path);
+static Plan *create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path);
+static Result *create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path);
+static WindowAgg *create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path);
+static SetOp *create_setop_plan(PlannerInfo *root, SetOpPath *best_path,
+                                 int flags);
+static RecursiveUnion *create_recursiveunion_plan(PlannerInfo *root, RecursiveUnionPath *best_path);
+static void get_column_info_for_window(PlannerInfo *root, WindowClause *wc,
+                                                  List *tlist,
+                                                  int numSortCols, AttrNumber *sortColIdx,
+                                                  int *partNumCols,
+                                                  AttrNumber **partColIdx,
+                                                  Oid **partOperators,
+                                                  int *ordNumCols,
+                                                  AttrNumber **ordColIdx,
+                                                  Oid **ordOperators);
+static LockRows *create_lockrows_plan(PlannerInfo *root, LockRowsPath *best_path,
+                                        int flags);
+static ModifyTable *create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path);
+static Limit *create_limit_plan(PlannerInfo *root, LimitPath *best_path,
+                                 int flags);
 static SeqScan *create_seqscan_plan(PlannerInfo *root, Path *best_path,
                                        List *tlist, List *scan_clauses);
+static SampleScan *create_samplescan_plan(PlannerInfo *root, Path *best_path,
+                                          List *tlist, List *scan_clauses);
 static Scan *create_indexscan_plan(PlannerInfo *root, IndexPath *best_path,
                                          List *tlist, List *scan_clauses, bool indexonly);
 static BitmapHeapScan *create_bitmap_scan_plan(PlannerInfo *root,
@@ -63,29 +125,31 @@ static BitmapHeapScan *create_bitmap_scan_plan(PlannerInfo *root,
                                                List *tlist, List *scan_clauses);
 static Plan *create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
                                          List **qual, List **indexqual, List **indexECs);
+static void bitmap_subplan_mark_shared(Plan *plan);
 static TidScan *create_tidscan_plan(PlannerInfo *root, TidPath *best_path,
                                        List *tlist, List *scan_clauses);
-static SubqueryScan *create_subqueryscan_plan(PlannerInfo *root, Path *best_path,
+static SubqueryScan *create_subqueryscan_plan(PlannerInfo *root,
+                                                SubqueryScanPath *best_path,
                                                 List *tlist, List *scan_clauses);
 static FunctionScan *create_functionscan_plan(PlannerInfo *root, Path *best_path,
                                                 List *tlist, List *scan_clauses);
 static ValuesScan *create_valuesscan_plan(PlannerInfo *root, Path *best_path,
                                           List *tlist, List *scan_clauses);
+static TableFuncScan *create_tablefuncscan_plan(PlannerInfo *root, Path *best_path,
+                                                 List *tlist, List *scan_clauses);
 static CteScan *create_ctescan_plan(PlannerInfo *root, Path *best_path,
                                        List *tlist, List *scan_clauses);
 static WorkTableScan *create_worktablescan_plan(PlannerInfo *root, Path *best_path,
                                                  List *tlist, List *scan_clauses);
 static ForeignScan *create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
                                                List *tlist, List *scan_clauses);
-static Plan *create_customscan_plan(PlannerInfo *root,
+static CustomScan *create_customscan_plan(PlannerInfo *root,
                                           CustomPath *best_path,
                                           List *tlist, List *scan_clauses);
-static NestLoop *create_nestloop_plan(PlannerInfo *root, NestPath *best_path,
-                                        Plan *outer_plan, Plan *inner_plan);
-static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path,
-                                         Plan *outer_plan, Plan *inner_plan);
-static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path,
-                                        Plan *outer_plan, Plan *inner_plan);
+static NestLoop *create_nestloop_plan(PlannerInfo *root, NestPath *best_path);
+static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path);
+static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path);
+static Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
 static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root);
 static void process_subquery_nestloop_params(PlannerInfo *root,
                                                                 List *subplan_params);
@@ -94,12 +158,17 @@ static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_pat
 static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol);
 static List *get_switched_clauses(List *clauses, Relids outerrelids);
 static List *order_qual_clauses(PlannerInfo *root, List *clauses);
-static void copy_path_costsize(Plan *dest, Path *src);
+static void copy_generic_path_info(Plan *dest, Path *src);
 static void copy_plan_costsize(Plan *dest, Plan *src);
+static void label_sort_with_costsize(PlannerInfo *root, Sort *plan,
+                                                double limit_tuples);
 static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
+static SampleScan *make_samplescan(List *qptlist, List *qpqual, Index scanrelid,
+                               TableSampleClause *tsc);
 static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
                           Oid indexid, List *indexqual, List *indexqualorig,
                           List *indexorderby, List *indexorderbyorig,
+                          List *indexorderbyops,
                           ScanDirection indexscandir);
 static IndexOnlyScan *make_indexonlyscan(List *qptlist, List *qpqual,
                                   Index scanrelid, Oid indexid,
@@ -116,14 +185,27 @@ static BitmapHeapScan *make_bitmap_heapscan(List *qptlist,
                                         Index scanrelid);
 static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
                         List *tidquals);
+static SubqueryScan *make_subqueryscan(List *qptlist,
+                                 List *qpqual,
+                                 Index scanrelid,
+                                 Plan *subplan);
 static FunctionScan *make_functionscan(List *qptlist, List *qpqual,
                                  Index scanrelid, List *functions, bool funcordinality);
 static ValuesScan *make_valuesscan(List *qptlist, List *qpqual,
                                Index scanrelid, List *values_lists);
+static TableFuncScan *make_tablefuncscan(List *qptlist, List *qpqual,
+                                  Index scanrelid, TableFunc *tablefunc);
 static CteScan *make_ctescan(List *qptlist, List *qpqual,
                         Index scanrelid, int ctePlanId, int cteParam);
 static WorkTableScan *make_worktablescan(List *qptlist, List *qpqual,
                                   Index scanrelid, int wtParam);
+static Append *make_append(List *appendplans, List *tlist);
+static RecursiveUnion *make_recursive_union(List *tlist,
+                                        Plan *lefttree,
+                                        Plan *righttree,
+                                        int wtParam,
+                                        List *distinctList,
+                                        long numGroups);
 static BitmapAnd *make_bitmap_and(List *bitmapplans);
 static BitmapOr *make_bitmap_or(List *bitmapplans);
 static NestLoop *make_nestloop(List *tlist,
@@ -150,12 +232,10 @@ static MergeJoin *make_mergejoin(List *tlist,
                           bool *mergenullsfirst,
                           Plan *lefttree, Plan *righttree,
                           JoinType jointype);
-static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
+static Sort *make_sort(Plan *lefttree, int numCols,
                  AttrNumber *sortColIdx, Oid *sortOperators,
-                 Oid *collations, bool *nullsFirst,
-                 double limit_tuples);
-static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
-                                                  Plan *lefttree, List *pathkeys,
+                 Oid *collations, bool *nullsFirst);
+static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
                                                   Relids relids,
                                                   const AttrNumber *reqColIdx,
                                                   bool adjust_tlist_in_place,
@@ -167,7 +247,38 @@ static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
 static EquivalenceMember *find_ec_member_for_tle(EquivalenceClass *ec,
                                           TargetEntry *tle,
                                           Relids relids);
+static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys);
+static Sort *make_sort_from_groupcols(List *groupcls,
+                                                AttrNumber *grpColIdx,
+                                                Plan *lefttree);
 static Material *make_material(Plan *lefttree);
+static WindowAgg *make_windowagg(List *tlist, Index winref,
+                          int partNumCols, AttrNumber *partColIdx, Oid *partOperators,
+                          int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators,
+                          int frameOptions, Node *startOffset, Node *endOffset,
+                          Plan *lefttree);
+static Group *make_group(List *tlist, List *qual, int numGroupCols,
+                  AttrNumber *grpColIdx, Oid *grpOperators,
+                  Plan *lefttree);
+static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
+static Unique *make_unique_from_pathkeys(Plan *lefttree,
+                                                 List *pathkeys, int numCols);
+static Gather *make_gather(List *qptlist, List *qpqual,
+                       int nworkers, bool single_copy, Plan *subplan);
+static SetOp *make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
+                  List *distinctList, AttrNumber flagColIdx, int firstFlag,
+                  long numGroups);
+static LockRows *make_lockrows(Plan *lefttree, List *rowMarks, int epqParam);
+static Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
+static ProjectSet *make_project_set(List *tlist, Plan *subplan);
+static ModifyTable *make_modifytable(PlannerInfo *root,
+                                CmdType operation, bool canSetTag,
+                                Index nominalRelation,
+                                List *resultRelations, List *subplans,
+                                List *withCheckOptionLists, List *returningLists,
+                                List *rowMarks, OnConflictExpr *onconflict, int epqParam);
+static GatherMerge *create_gather_merge_plan(PlannerInfo *root,
+                                                GatherMergePath *best_path);
 
 
 /*
@@ -197,8 +308,27 @@ create_plan(PlannerInfo *root, Path *best_path)
        root->curOuterRels = NULL;
        root->curOuterParams = NIL;
 
-       /* Recursively process the path tree */
-       plan = create_plan_recurse(root, best_path);
+       /* Recursively process the path tree, demanding the correct tlist result */
+       plan = create_plan_recurse(root, best_path, CP_EXACT_TLIST);
+
+       /*
+        * Make sure the topmost plan node's targetlist exposes the original
+        * column names and other decorative info.  Targetlists generated within
+        * the planner don't bother with that stuff, but we must have it on the
+        * top-level tlist seen at execution time.  However, ModifyTable plan
+        * nodes don't have a tlist matching the querytree targetlist.
+        */
+       if (!IsA(plan, ModifyTable))
+               apply_tlist_labeling(plan->targetlist, root->processed_tlist);
+
+       /*
+        * Attach any initPlans created in this query level to the topmost plan
+        * node.  (In principle the initplans could go in any plan node at or
+        * above where they're referenced, but there seems no reason to put them
+        * any lower than the topmost node for the query level.  Also, see
+        * comments for SS_finalize_plan before you try to change this.)
+        */
+       SS_attach_initplans(root, plan);
 
        /* Check we successfully assigned all NestLoopParams to plan nodes */
        if (root->curOuterParams != NIL)
@@ -218,25 +348,27 @@ create_plan(PlannerInfo *root, Path *best_path)
  *       Recursive guts of create_plan().
  */
 static Plan *
-create_plan_recurse(PlannerInfo *root, Path *best_path)
+create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
 {
        Plan       *plan;
 
        switch (best_path->pathtype)
        {
                case T_SeqScan:
+               case T_SampleScan:
                case T_IndexScan:
                case T_IndexOnlyScan:
                case T_BitmapHeapScan:
                case T_TidScan:
                case T_SubqueryScan:
                case T_FunctionScan:
+               case T_TableFuncScan:
                case T_ValuesScan:
                case T_CteScan:
                case T_WorkTableScan:
                case T_ForeignScan:
                case T_CustomScan:
-                       plan = create_scan_plan(root, best_path);
+                       plan = create_scan_plan(root, best_path, flags);
                        break;
                case T_HashJoin:
                case T_MergeJoin:
@@ -253,16 +385,101 @@ create_plan_recurse(PlannerInfo *root, Path *best_path)
                                                                                        (MergeAppendPath *) best_path);
                        break;
                case T_Result:
-                       plan = (Plan *) create_result_plan(root,
-                                                                                          (ResultPath *) best_path);
+                       if (IsA(best_path, ProjectionPath))
+                       {
+                               plan = create_projection_plan(root,
+                                                                                         (ProjectionPath *) best_path);
+                       }
+                       else if (IsA(best_path, MinMaxAggPath))
+                       {
+                               plan = (Plan *) create_minmaxagg_plan(root,
+                                                                                               (MinMaxAggPath *) best_path);
+                       }
+                       else
+                       {
+                               Assert(IsA(best_path, ResultPath));
+                               plan = (Plan *) create_result_plan(root,
+                                                                                                  (ResultPath *) best_path);
+                       }
+                       break;
+               case T_ProjectSet:
+                       plan = (Plan *) create_project_set_plan(root,
+                                                                                          (ProjectSetPath *) best_path);
                        break;
                case T_Material:
                        plan = (Plan *) create_material_plan(root,
-                                                                                                (MaterialPath *) best_path);
+                                                                                                (MaterialPath *) best_path,
+                                                                                                flags);
                        break;
                case T_Unique:
-                       plan = create_unique_plan(root,
-                                                                         (UniquePath *) best_path);
+                       if (IsA(best_path, UpperUniquePath))
+                       {
+                               plan = (Plan *) create_upper_unique_plan(root,
+                                                                                          (UpperUniquePath *) best_path,
+                                                                                                                flags);
+                       }
+                       else
+                       {
+                               Assert(IsA(best_path, UniquePath));
+                               plan = create_unique_plan(root,
+                                                                                 (UniquePath *) best_path,
+                                                                                 flags);
+                       }
+                       break;
+               case T_Gather:
+                       plan = (Plan *) create_gather_plan(root,
+                                                                                          (GatherPath *) best_path);
+                       break;
+               case T_Sort:
+                       plan = (Plan *) create_sort_plan(root,
+                                                                                        (SortPath *) best_path,
+                                                                                        flags);
+                       break;
+               case T_Group:
+                       plan = (Plan *) create_group_plan(root,
+                                                                                         (GroupPath *) best_path);
+                       break;
+               case T_Agg:
+                       if (IsA(best_path, GroupingSetsPath))
+                               plan = create_groupingsets_plan(root,
+                                                                                        (GroupingSetsPath *) best_path);
+                       else
+                       {
+                               Assert(IsA(best_path, AggPath));
+                               plan = (Plan *) create_agg_plan(root,
+                                                                                               (AggPath *) best_path);
+                       }
+                       break;
+               case T_WindowAgg:
+                       plan = (Plan *) create_windowagg_plan(root,
+                                                                                               (WindowAggPath *) best_path);
+                       break;
+               case T_SetOp:
+                       plan = (Plan *) create_setop_plan(root,
+                                                                                         (SetOpPath *) best_path,
+                                                                                         flags);
+                       break;
+               case T_RecursiveUnion:
+                       plan = (Plan *) create_recursiveunion_plan(root,
+                                                                                  (RecursiveUnionPath *) best_path);
+                       break;
+               case T_LockRows:
+                       plan = (Plan *) create_lockrows_plan(root,
+                                                                                                (LockRowsPath *) best_path,
+                                                                                                flags);
+                       break;
+               case T_ModifyTable:
+                       plan = (Plan *) create_modifytable_plan(root,
+                                                                                         (ModifyTablePath *) best_path);
+                       break;
+               case T_Limit:
+                       plan = (Plan *) create_limit_plan(root,
+                                                                                         (LimitPath *) best_path,
+                                                                                         flags);
+                       break;
+               case T_GatherMerge:
+                       plan = (Plan *) create_gather_merge_plan(root,
+                                                                                         (GatherMergePath *) best_path);
                        break;
                default:
                        elog(ERROR, "unrecognized node type: %d",
@@ -279,34 +496,90 @@ create_plan_recurse(PlannerInfo *root, Path *best_path)
  *      Create a scan plan for the parent relation of 'best_path'.
  */
 static Plan *
-create_scan_plan(PlannerInfo *root, Path *best_path)
+create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
 {
        RelOptInfo *rel = best_path->parent;
-       List       *tlist;
        List       *scan_clauses;
+       List       *gating_clauses;
+       List       *tlist;
        Plan       *plan;
 
+       /*
+        * Extract the relevant restriction clauses from the parent relation. The
+        * executor must apply all these restrictions during the scan, except for
+        * pseudoconstants which we'll take care of below.
+        *
+        * If this is a plain indexscan or index-only scan, we need not consider
+        * restriction clauses that are implied by the index's predicate, so use
+        * indrestrictinfo not baserestrictinfo.  Note that we can't do that for
+        * bitmap indexscans, since there's not necessarily a single index
+        * involved; but it doesn't matter since create_bitmap_scan_plan() will be
+        * able to get rid of such clauses anyway via predicate proof.
+        */
+       switch (best_path->pathtype)
+       {
+               case T_IndexScan:
+               case T_IndexOnlyScan:
+                       scan_clauses = castNode(IndexPath, best_path)->indexinfo->indrestrictinfo;
+                       break;
+               default:
+                       scan_clauses = rel->baserestrictinfo;
+                       break;
+       }
+
+       /*
+        * If this is a parameterized scan, we also need to enforce all the join
+        * clauses available from the outer relation(s).
+        *
+        * For paranoia's sake, don't modify the stored baserestrictinfo list.
+        */
+       if (best_path->param_info)
+               scan_clauses = list_concat(list_copy(scan_clauses),
+                                                                  best_path->param_info->ppi_clauses);
+
+       /*
+        * Detect whether we have any pseudoconstant quals to deal with.  Then, if
+        * we'll need a gating Result node, it will be able to project, so there
+        * are no requirements on the child's tlist.
+        */
+       gating_clauses = get_gating_quals(root, scan_clauses);
+       if (gating_clauses)
+               flags = 0;
+
        /*
         * For table scans, rather than using the relation targetlist (which is
         * only those Vars actually needed by the query), we prefer to generate a
         * tlist containing all Vars in order.  This will allow the executor to
-        * optimize away projection of the table tuples, if possible.  (Note that
-        * planner.c may replace the tlist we generate here, forcing projection to
-        * occur.)
+        * optimize away projection of the table tuples, if possible.
         */
-       if (use_physical_tlist(root, rel))
+       if (use_physical_tlist(root, best_path, flags))
        {
                if (best_path->pathtype == T_IndexOnlyScan)
                {
                        /* For index-only scan, the preferred tlist is the index's */
                        tlist = copyObject(((IndexPath *) best_path)->indexinfo->indextlist);
+
+                       /*
+                        * Transfer any sortgroupref data to the replacement tlist, unless
+                        * we don't care because the gating Result will handle it.
+                        */
+                       if (!gating_clauses)
+                               apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
                }
                else
                {
                        tlist = build_physical_tlist(root, rel);
-                       /* if fail because of dropped cols, use regular method */
                        if (tlist == NIL)
+                       {
+                               /* Failed because of dropped cols, so use regular method */
                                tlist = build_path_tlist(root, best_path);
+                       }
+                       else
+                       {
+                               /* As above, transfer sortgroupref data to replacement tlist */
+                               if (!gating_clauses)
+                                       apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
+                       }
                }
        }
        else
@@ -314,23 +587,6 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
                tlist = build_path_tlist(root, best_path);
        }
 
-       /*
-        * Extract the relevant restriction clauses from the parent relation. The
-        * executor must apply all these restrictions during the scan, except for
-        * pseudoconstants which we'll take care of below.
-        */
-       scan_clauses = rel->baserestrictinfo;
-
-       /*
-        * If this is a parameterized scan, we also need to enforce all the join
-        * clauses available from the outer relation(s).
-        *
-        * For paranoia's sake, don't modify the stored baserestrictinfo list.
-        */
-       if (best_path->param_info)
-               scan_clauses = list_concat(list_copy(scan_clauses),
-                                                                  best_path->param_info->ppi_clauses);
-
        switch (best_path->pathtype)
        {
                case T_SeqScan:
@@ -340,6 +596,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
                                                                                                scan_clauses);
                        break;
 
+               case T_SampleScan:
+                       plan = (Plan *) create_samplescan_plan(root,
+                                                                                                  best_path,
+                                                                                                  tlist,
+                                                                                                  scan_clauses);
+                       break;
+
                case T_IndexScan:
                        plan = (Plan *) create_indexscan_plan(root,
                                                                                                  (IndexPath *) best_path,
@@ -372,7 +635,7 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
 
                case T_SubqueryScan:
                        plan = (Plan *) create_subqueryscan_plan(root,
-                                                                                                        best_path,
+                                                                                         (SubqueryScanPath *) best_path,
                                                                                                         tlist,
                                                                                                         scan_clauses);
                        break;
@@ -384,6 +647,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
                                                                                                         scan_clauses);
                        break;
 
+               case T_TableFuncScan:
+                       plan = (Plan *) create_tablefuncscan_plan(root,
+                                                                                                         best_path,
+                                                                                                         tlist,
+                                                                                                         scan_clauses);
+                       break;
+
                case T_ValuesScan:
                        plan = (Plan *) create_valuesscan_plan(root,
                                                                                                   best_path,
@@ -413,10 +683,10 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
                        break;
 
                case T_CustomScan:
-                       plan = create_customscan_plan(root,
-                                                                                 (CustomPath *) best_path,
-                                                                                 tlist,
-                                                                                 scan_clauses);
+                       plan = (Plan *) create_customscan_plan(root,
+                                                                                                  (CustomPath *) best_path,
+                                                                                                  tlist,
+                                                                                                  scan_clauses);
                        break;
 
                default:
@@ -431,27 +701,30 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
         * gating Result node that evaluates the pseudoconstants as one-time
         * quals.
         */
-       if (root->hasPseudoConstantQuals)
-               plan = create_gating_plan(root, plan, scan_clauses);
+       if (gating_clauses)
+               plan = create_gating_plan(root, best_path, plan, gating_clauses);
 
        return plan;
 }
 
 /*
  * Build a target list (ie, a list of TargetEntry) for the Path's output.
+ *
+ * This is almost just make_tlist_from_pathtarget(), but we also have to
+ * deal with replacing nestloop params.
  */
 static List *
 build_path_tlist(PlannerInfo *root, Path *path)
 {
-       RelOptInfo *rel = path->parent;
        List       *tlist = NIL;
+       Index      *sortgrouprefs = path->pathtarget->sortgrouprefs;
        int                     resno = 1;
        ListCell   *v;
 
-       foreach(v, rel->reltargetlist)
+       foreach(v, path->pathtarget->exprs)
        {
-               /* Do we really need to copy here?      Not sure */
-               Node       *node = (Node *) copyObject(lfirst(v));
+               Node       *node = (Node *) lfirst(v);
+               TargetEntry *tle;
 
                /*
                 * If it's a parameterized path, there might be lateral references in
@@ -462,10 +735,14 @@ build_path_tlist(PlannerInfo *root, Path *path)
                if (path->param_info)
                        node = replace_nestloop_params(root, node);
 
-               tlist = lappend(tlist, makeTargetEntry((Expr *) node,
-                                                                                          resno,
-                                                                                          NULL,
-                                                                                          false));
+               tle = makeTargetEntry((Expr *) node,
+                                                         resno,
+                                                         NULL,
+                                                         false);
+               if (sortgrouprefs)
+                       tle->ressortgroupref = sortgrouprefs[resno - 1];
+
+               tlist = lappend(tlist, tle);
                resno++;
        }
        return tlist;
@@ -477,25 +754,34 @@ build_path_tlist(PlannerInfo *root, Path *path)
  *             rather than only those Vars actually referenced.
  */
 static bool
-use_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
+use_physical_tlist(PlannerInfo *root, Path *path, int flags)
 {
+       RelOptInfo *rel = path->parent;
        int                     i;
        ListCell   *lc;
 
+       /*
+        * Forget it if either exact tlist or small tlist is demanded.
+        */
+       if (flags & (CP_EXACT_TLIST | CP_SMALL_TLIST))
+               return false;
+
        /*
         * We can do this for real relation scans, subquery scans, function scans,
-        * values scans, and CTE scans (but not for, eg, joins).
+        * tablefunc scans, values scans, and CTE scans (but not for, eg, joins).
         */
        if (rel->rtekind != RTE_RELATION &&
                rel->rtekind != RTE_SUBQUERY &&
                rel->rtekind != RTE_FUNCTION &&
+               rel->rtekind != RTE_TABLEFUNC &&
                rel->rtekind != RTE_VALUES &&
                rel->rtekind != RTE_CTE)
                return false;
 
        /*
         * Can't do it with inheritance cases either (mainly because Append
-        * doesn't project).
+        * doesn't project; this test may be unnecessary now that
+        * create_append_plan instructs its children to return an exact tlist).
         */
        if (rel->reloptkind != RELOPT_BASEREL)
                return false;
@@ -524,80 +810,104 @@ use_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
                        return false;
        }
 
+       /*
+        * Also, can't do it if CP_LABEL_TLIST is specified and path is requested
+        * to emit any sort/group columns that are not simple Vars.  (If they are
+        * simple Vars, they should appear in the physical tlist, and
+        * apply_pathtarget_labeling_to_tlist will take care of getting them
+        * labeled again.)      We also have to check that no two sort/group columns
+        * are the same Var, else that element of the physical tlist would need
+        * conflicting ressortgroupref labels.
+        */
+       if ((flags & CP_LABEL_TLIST) && path->pathtarget->sortgrouprefs)
+       {
+               Bitmapset  *sortgroupatts = NULL;
+
+               i = 0;
+               foreach(lc, path->pathtarget->exprs)
+               {
+                       Expr       *expr = (Expr *) lfirst(lc);
+
+                       if (path->pathtarget->sortgrouprefs[i])
+                       {
+                               if (expr && IsA(expr, Var))
+                               {
+                                       int                     attno = ((Var *) expr)->varattno;
+
+                                       attno -= FirstLowInvalidHeapAttributeNumber;
+                                       if (bms_is_member(attno, sortgroupatts))
+                                               return false;
+                                       sortgroupatts = bms_add_member(sortgroupatts, attno);
+                               }
+                               else
+                                       return false;
+                       }
+                       i++;
+               }
+       }
+
        return true;
 }
 
 /*
- * disuse_physical_tlist
- *             Switch a plan node back to emitting only Vars actually referenced.
+ * get_gating_quals
+ *       See if there are pseudoconstant quals in a node's quals list
  *
- * If the plan node immediately above a scan would prefer to get only
- * needed Vars and not a physical tlist, it must call this routine to
- * undo the decision made by use_physical_tlist().  Currently, Hash, Sort,
- * and Material nodes want this, so they don't have to store useless columns.
+ * If the node's quals list includes any pseudoconstant quals,
+ * return just those quals.
  */
-static void
-disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path)
+static List *
+get_gating_quals(PlannerInfo *root, List *quals)
 {
-       /* Only need to undo it for path types handled by create_scan_plan() */
-       switch (path->pathtype)
-       {
-               case T_SeqScan:
-               case T_IndexScan:
-               case T_IndexOnlyScan:
-               case T_BitmapHeapScan:
-               case T_TidScan:
-               case T_SubqueryScan:
-               case T_FunctionScan:
-               case T_ValuesScan:
-               case T_CteScan:
-               case T_WorkTableScan:
-               case T_ForeignScan:
-               case T_CustomScan:
-                       plan->targetlist = build_path_tlist(root, path);
-                       break;
-               default:
-                       break;
-       }
+       /* No need to look if we know there are no pseudoconstants */
+       if (!root->hasPseudoConstantQuals)
+               return NIL;
+
+       /* Sort into desirable execution order while still in RestrictInfo form */
+       quals = order_qual_clauses(root, quals);
+
+       /* Pull out any pseudoconstant quals from the RestrictInfo list */
+       return extract_actual_clauses(quals, true);
 }
 
 /*
  * create_gating_plan
  *       Deal with pseudoconstant qual clauses
  *
- * If the node's quals list includes any pseudoconstant quals, put them
- * into a gating Result node atop the already-built plan.  Otherwise,
- * return the plan as-is.
- *
- * Note that we don't change cost or size estimates when doing gating.
- * The costs of qual eval were already folded into the plan's startup cost.
- * Leaving the size alone amounts to assuming that the gating qual will
- * succeed, which is the conservative estimate for planning upper queries.
- * We certainly don't want to assume the output size is zero (unless the
- * gating qual is actually constant FALSE, and that case is dealt with in
- * clausesel.c).  Interpolating between the two cases is silly, because
- * it doesn't reflect what will really happen at runtime, and besides which
- * in most cases we have only a very bad idea of the probability of the gating
- * qual being true.
+ * Add a gating Result node atop the already-built plan.
  */
 static Plan *
-create_gating_plan(PlannerInfo *root, Plan *plan, List *quals)
+create_gating_plan(PlannerInfo *root, Path *path, Plan *plan,
+                                  List *gating_quals)
 {
-       List       *pseudoconstants;
+       Plan       *gplan;
 
-       /* Sort into desirable execution order while still in RestrictInfo form */
-       quals = order_qual_clauses(root, quals);
+       Assert(gating_quals);
 
-       /* Pull out any pseudoconstant quals from the RestrictInfo list */
-       pseudoconstants = extract_actual_clauses(quals, true);
+       /*
+        * Since we need a Result node anyway, always return the path's requested
+        * tlist; that's never a wrong choice, even if the parent node didn't ask
+        * for CP_EXACT_TLIST.
+        */
+       gplan = (Plan *) make_result(build_path_tlist(root, path),
+                                                                (Node *) gating_quals,
+                                                                plan);
 
-       if (!pseudoconstants)
-               return plan;
+       /*
+        * Notice that we don't change cost or size estimates when doing gating.
+        * The costs of qual eval were already included in the subplan's cost.
+        * Leaving the size alone amounts to assuming that the gating qual will
+        * succeed, which is the conservative estimate for planning upper queries.
+        * We certainly don't want to assume the output size is zero (unless the
+        * gating qual is actually constant FALSE, and that case is dealt with in
+        * clausesel.c).  Interpolating between the two cases is silly, because it
+        * doesn't reflect what will really happen at runtime, and besides which
+        * in most cases we have only a very bad idea of the probability of the
+        * gating qual being true.
+        */
+       copy_plan_costsize(gplan, plan);
 
-       return (Plan *) make_result(root,
-                                                               plan->targetlist,
-                                                               (Node *) pseudoconstants,
-                                                               plan);
+       return gplan;
 }
 
 /*
@@ -608,43 +918,22 @@ create_gating_plan(PlannerInfo *root, Plan *plan, List *quals)
 static Plan *
 create_join_plan(PlannerInfo *root, JoinPath *best_path)
 {
-       Plan       *outer_plan;
-       Plan       *inner_plan;
        Plan       *plan;
-       Relids          saveOuterRels = root->curOuterRels;
-
-       outer_plan = create_plan_recurse(root, best_path->outerjoinpath);
-
-       /* For a nestloop, include outer relids in curOuterRels for inner side */
-       if (best_path->path.pathtype == T_NestLoop)
-               root->curOuterRels = bms_union(root->curOuterRels,
-                                                                  best_path->outerjoinpath->parent->relids);
-
-       inner_plan = create_plan_recurse(root, best_path->innerjoinpath);
+       List       *gating_clauses;
 
        switch (best_path->path.pathtype)
        {
                case T_MergeJoin:
                        plan = (Plan *) create_mergejoin_plan(root,
-                                                                                                 (MergePath *) best_path,
-                                                                                                 outer_plan,
-                                                                                                 inner_plan);
+                                                                                                 (MergePath *) best_path);
                        break;
                case T_HashJoin:
                        plan = (Plan *) create_hashjoin_plan(root,
-                                                                                                (HashPath *) best_path,
-                                                                                                outer_plan,
-                                                                                                inner_plan);
+                                                                                                (HashPath *) best_path);
                        break;
                case T_NestLoop:
-                       /* Restore curOuterRels */
-                       bms_free(root->curOuterRels);
-                       root->curOuterRels = saveOuterRels;
-
                        plan = (Plan *) create_nestloop_plan(root,
-                                                                                                (NestPath *) best_path,
-                                                                                                outer_plan,
-                                                                                                inner_plan);
+                                                                                                (NestPath *) best_path);
                        break;
                default:
                        elog(ERROR, "unrecognized node type: %d",
@@ -658,8 +947,10 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path)
         * gating Result node that evaluates the pseudoconstants as one-time
         * quals.
         */
-       if (root->hasPseudoConstantQuals)
-               plan = create_gating_plan(root, plan, best_path->joinrestrictinfo);
+       gating_clauses = get_gating_quals(root, best_path->joinrestrictinfo);
+       if (gating_clauses)
+               plan = create_gating_plan(root, (Path *) best_path, plan,
+                                                                 gating_clauses);
 
 #ifdef NOT_USED
 
@@ -704,19 +995,28 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
        if (best_path->subpaths == NIL)
        {
                /* Generate a Result plan with constant-FALSE gating qual */
-               return (Plan *) make_result(root,
-                                                                       tlist,
+               Plan       *plan;
+
+               plan = (Plan *) make_result(tlist,
                                                                        (Node *) list_make1(makeBoolConst(false,
                                                                                                                                          false)),
                                                                        NULL);
+
+               copy_generic_path_info(plan, (Path *) best_path);
+
+               return plan;
        }
 
        /* Build the plan for each child */
        foreach(subpaths, best_path->subpaths)
        {
                Path       *subpath = (Path *) lfirst(subpaths);
+               Plan       *subplan;
+
+               /* Must insist that all children return the same tlist */
+               subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
-               subplans = lappend(subplans, create_plan_recurse(root, subpath));
+               subplans = lappend(subplans, subplan);
        }
 
        /*
@@ -728,6 +1028,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
 
        plan = make_append(subplans, tlist);
 
+       copy_generic_path_info(&plan->plan, (Path *) best_path);
+
        return (Plan *) plan;
 }
 
@@ -754,14 +1056,14 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
         * prepare_sort_from_pathkeys on it before we do so on the individual
         * child plans, to make cross-checking the sort info easier.
         */
-       copy_path_costsize(plan, (Path *) best_path);
+       copy_generic_path_info(plan, (Path *) best_path);
        plan->targetlist = tlist;
        plan->qual = NIL;
        plan->lefttree = NULL;
        plan->righttree = NULL;
 
        /* Compute sort column info, and adjust MergeAppend's tlist as needed */
-       (void) prepare_sort_from_pathkeys(root, plan, pathkeys,
+       (void) prepare_sort_from_pathkeys(plan, pathkeys,
                                                                          best_path->path.parent->relids,
                                                                          NULL,
                                                                          true,
@@ -787,10 +1089,11 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
                bool       *nullsFirst;
 
                /* Build the child plan */
-               subplan = create_plan_recurse(root, subpath);
+               /* Must insist that all children return the same tlist */
+               subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
                /* Compute sort column info, and adjust subplan's tlist as needed */
-               subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
+               subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
                                                                                         subpath->parent->relids,
                                                                                         node->sortColIdx,
                                                                                         false,
@@ -819,10 +1122,14 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
 
                /* Now, insert a Sort node if subplan isn't sufficiently ordered */
                if (!pathkeys_contained_in(pathkeys, subpath->pathkeys))
-                       subplan = (Plan *) make_sort(root, subplan, numsortkeys,
+               {
+                       Sort       *sort = make_sort(subplan, numsortkeys,
                                                                                 sortColIdx, sortOperators,
-                                                                                collations, nullsFirst,
-                                                                                best_path->limit_tuples);
+                                                                                collations, nullsFirst);
+
+                       label_sort_with_costsize(root, sort, best_path->limit_tuples);
+                       subplan = (Plan *) sort;
+               }
 
                subplans = lappend(subplans, subplan);
        }
@@ -835,25 +1142,53 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
 /*
  * create_result_plan
  *       Create a Result plan for 'best_path'.
- *       This is only used for the case of a query with an empty jointree.
+ *       This is only used for degenerate cases, such as a query with an empty
+ *       jointree.
  *
  *       Returns a Plan node.
  */
 static Result *
 create_result_plan(PlannerInfo *root, ResultPath *best_path)
 {
+       Result     *plan;
        List       *tlist;
        List       *quals;
 
-       /* The tlist will be installed later, since we have no RelOptInfo */
-       Assert(best_path->path.parent == NULL);
-       tlist = NIL;
+       tlist = build_path_tlist(root, &best_path->path);
 
        /* best_path->quals is just bare clauses */
-
        quals = order_qual_clauses(root, best_path->quals);
 
-       return make_result(root, tlist, (Node *) quals, NULL);
+       plan = make_result(tlist, (Node *) quals, NULL);
+
+       copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+       return plan;
+}
+
+/*
+ * create_project_set_plan
+ *       Create a ProjectSet plan for 'best_path'.
+ *
+ *       Returns a Plan node.
+ */
+static ProjectSet *
+create_project_set_plan(PlannerInfo *root, ProjectSetPath *best_path)
+{
+       ProjectSet *plan;
+       Plan       *subplan;
+       List       *tlist;
+
+       /* Since we intend to project, we don't need to constrain child tlist */
+       subplan = create_plan_recurse(root, best_path->subpath, 0);
+
+       tlist = build_path_tlist(root, &best_path->path);
+
+       plan = make_project_set(tlist, subplan);
+
+       copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+       return plan;
 }
 
 /*
@@ -864,19 +1199,22 @@ create_result_plan(PlannerInfo *root, ResultPath *best_path)
  *       Returns a Plan node.
  */
 static Material *
-create_material_plan(PlannerInfo *root, MaterialPath *best_path)
+create_material_plan(PlannerInfo *root, MaterialPath *best_path, int flags)
 {
        Material   *plan;
        Plan       *subplan;
 
-       subplan = create_plan_recurse(root, best_path->subpath);
-
-       /* We don't want any excess columns in the materialized tuples */
-       disuse_physical_tlist(root, subplan, best_path->subpath);
+       /*
+        * We don't want any excess columns in the materialized tuples, so request
+        * a smaller tlist.  Otherwise, since Material doesn't project, tlist
+        * requirements pass through.
+        */
+       subplan = create_plan_recurse(root, best_path->subpath,
+                                                                 flags | CP_SMALL_TLIST);
 
        plan = make_material(subplan);
 
-       copy_path_costsize(&plan->plan, (Path *) best_path);
+       copy_generic_path_info(&plan->plan, (Path *) best_path);
 
        return plan;
 }
@@ -889,7 +1227,7 @@ create_material_plan(PlannerInfo *root, MaterialPath *best_path)
  *       Returns a Plan node.
  */
 static Plan *
-create_unique_plan(PlannerInfo *root, UniquePath *best_path)
+create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
 {
        Plan       *plan;
        Plan       *subplan;
@@ -903,7 +1241,8 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
        int                     groupColPos;
        ListCell   *l;
 
-       subplan = create_plan_recurse(root, best_path->subpath);
+       /* Unique doesn't project, so tlist requirements pass through */
+       subplan = create_plan_recurse(root, best_path->subpath, flags);
 
        /* Done if we don't need to do any actual unique-ifying */
        if (best_path->umethod == UNIQUE_PATH_NOOP)
@@ -960,7 +1299,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
                 */
                if (!is_projection_capable_plan(subplan) &&
                        !tlist_same_exprs(newtlist, subplan->targetlist))
-                       subplan = (Plan *) make_result(root, newtlist, NULL, subplan);
+                       subplan = inject_projection_plan(subplan, newtlist);
                else
                        subplan->targetlist = newtlist;
        }
@@ -989,11 +1328,8 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
 
        if (best_path->umethod == UNIQUE_PATH_HASH)
        {
-               long            numGroups;
                Oid                *groupOperators;
 
-               numGroups = (long) Min(best_path->path.rows, (double) LONG_MAX);
-
                /*
                 * Get the hashable equality operators for the Agg node to use.
                 * Normally these are the same as the IN clause operators, but if
@@ -1018,20 +1354,22 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
                 * minimum output tlist, without any stuff we might have added to the
                 * subplan tlist.
                 */
-               plan = (Plan *) make_agg(root,
-                                                                build_path_tlist(root, &best_path->path),
+               plan = (Plan *) make_agg(build_path_tlist(root, &best_path->path),
                                                                 NIL,
                                                                 AGG_HASHED,
-                                                                NULL,
+                                                                AGGSPLIT_SIMPLE,
                                                                 numGroupCols,
                                                                 groupColIdx,
                                                                 groupOperators,
-                                                                numGroups,
+                                                                NIL,
+                                                                NIL,
+                                                                best_path->path.rows,
                                                                 subplan);
        }
        else
        {
                List       *sortList = NIL;
+               Sort       *sort;
 
                /* Create an ORDER BY list to sort the input compatibly */
                groupColPos = 0;
@@ -1073,39 +1411,1012 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
                        sortList = lappend(sortList, sortcl);
                        groupColPos++;
                }
-               plan = (Plan *) make_sort_from_sortclauses(root, sortList, subplan);
-               plan = (Plan *) make_unique(plan, sortList);
+               sort = make_sort_from_sortclauses(sortList, subplan);
+               label_sort_with_costsize(root, sort, -1.0);
+               plan = (Plan *) make_unique_from_sortclauses((Plan *) sort, sortList);
        }
 
-       /* Adjust output size estimate (other fields should be OK already) */
-       plan->plan_rows = best_path->path.rows;
+       /* Copy cost data from Path to Plan */
+       copy_generic_path_info(plan, &best_path->path);
 
        return plan;
 }
 
+/*
+ * create_gather_plan
+ *
+ *       Create a Gather plan for 'best_path' and (recursively) plans
+ *       for its subpaths.
+ */
+static Gather *
+create_gather_plan(PlannerInfo *root, GatherPath *best_path)
+{
+       Gather     *gather_plan;
+       Plan       *subplan;
+       List       *tlist;
+
+       /*
+        * Although the Gather node can project, we prefer to push down such work
+        * to its child node, so demand an exact tlist from the child.
+        */
+       subplan = create_plan_recurse(root, best_path->subpath, CP_EXACT_TLIST);
+
+       tlist = build_path_tlist(root, &best_path->path);
+
+       gather_plan = make_gather(tlist,
+                                                         NIL,
+                                                         best_path->path.parallel_workers,
+                                                         best_path->single_copy,
+                                                         subplan);
+
+       copy_generic_path_info(&gather_plan->plan, &best_path->path);
+
+       /* use parallel mode for parallel plans. */
+       root->glob->parallelModeNeeded = true;
+
+       return gather_plan;
+}
+
+/*
+ * create_gather_merge_plan
+ *
+ *       Create a Gather Merge plan for 'best_path' and (recursively)
+ *       plans for its subpaths.
+ */
+static GatherMerge *
+create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
+{
+       GatherMerge *gm_plan;
+       Plan       *subplan;
+       List       *pathkeys = best_path->path.pathkeys;
+       List       *tlist = build_path_tlist(root, &best_path->path);
+
+       /* As with Gather, it's best to project away columns in the workers. */
+       subplan = create_plan_recurse(root, best_path->subpath, CP_EXACT_TLIST);
+
+       /* Create a shell for a GatherMerge plan. */
+       gm_plan = makeNode(GatherMerge);
+       gm_plan->plan.targetlist = tlist;
+       gm_plan->num_workers = best_path->num_workers;
+       copy_generic_path_info(&gm_plan->plan, &best_path->path);
+
+       /* Gather Merge is pointless with no pathkeys; use Gather instead. */
+       Assert(pathkeys != NIL);
+
+       /* Compute sort column info, and adjust subplan's tlist as needed */
+       subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+                                                                                best_path->subpath->parent->relids,
+                                                                                gm_plan->sortColIdx,
+                                                                                false,
+                                                                                &gm_plan->numCols,
+                                                                                &gm_plan->sortColIdx,
+                                                                                &gm_plan->sortOperators,
+                                                                                &gm_plan->collations,
+                                                                                &gm_plan->nullsFirst);
+
+
+       /* Now, insert a Sort node if subplan isn't sufficiently ordered */
+       if (!pathkeys_contained_in(pathkeys, best_path->subpath->pathkeys))
+               subplan = (Plan *) make_sort(subplan, gm_plan->numCols,
+                                                                        gm_plan->sortColIdx,
+                                                                        gm_plan->sortOperators,
+                                                                        gm_plan->collations,
+                                                                        gm_plan->nullsFirst);
+
+       /* Now insert the subplan under GatherMerge. */
+       gm_plan->plan.lefttree = subplan;
+
+       /* use parallel mode for parallel plans. */
+       root->glob->parallelModeNeeded = true;
+
+       return gm_plan;
+}
+
+/*
+ * create_projection_plan
+ *
+ *       Create a plan tree to do a projection step and (recursively) plans
+ *       for its subpaths.  We may need a Result node for the projection,
+ *       but sometimes we can just let the subplan do the work.
+ */
+static Plan *
+create_projection_plan(PlannerInfo *root, ProjectionPath *best_path)
+{
+       Plan       *plan;
+       Plan       *subplan;
+       List       *tlist;
+
+       /* Since we intend to project, we don't need to constrain child tlist */
+       subplan = create_plan_recurse(root, best_path->subpath, 0);
+
+       tlist = build_path_tlist(root, &best_path->path);
+
+       /*
+        * We might not really need a Result node here, either because the subplan
+        * can project or because it's returning the right list of expressions
+        * anyway.  Usually create_projection_path will have detected that and set
+        * dummypp if we don't need a Result; but its decision can't be final,
+        * because some createplan.c routines change the tlists of their nodes.
+        * (An example is that create_merge_append_plan might add resjunk sort
+        * columns to a MergeAppend.)  So we have to recheck here.  If we do
+        * arrive at a different answer than create_projection_path did, we'll
+        * have made slightly wrong cost estimates; but label the plan with the
+        * cost estimates we actually used, not "corrected" ones.  (XXX this could
+        * be cleaned up if we moved more of the sortcolumn setup logic into Path
+        * creation, but that would add expense to creating Paths we might end up
+        * not using.)
+        */
+       if (is_projection_capable_path(best_path->subpath) ||
+               tlist_same_exprs(tlist, subplan->targetlist))
+       {
+               /* Don't need a separate Result, just assign tlist to subplan */
+               plan = subplan;
+               plan->targetlist = tlist;
+
+               /* Label plan with the estimated costs we actually used */
+               plan->startup_cost = best_path->path.startup_cost;
+               plan->total_cost = best_path->path.total_cost;
+               plan->plan_rows = best_path->path.rows;
+               plan->plan_width = best_path->path.pathtarget->width;
+               /* ... but be careful not to munge subplan's parallel-aware flag */
+       }
+       else
+       {
+               /* We need a Result node */
+               plan = (Plan *) make_result(tlist, NULL, subplan);
+
+               copy_generic_path_info(plan, (Path *) best_path);
+       }
+
+       return plan;
+}
+
+/*
+ * inject_projection_plan
+ *       Insert a Result node to do a projection step.
+ *
+ * This is used in a few places where we decide on-the-fly that we need a
+ * projection step as part of the tree generated for some Path node.
+ * We should try to get rid of this in favor of doing it more honestly.
+ */
+static Plan *
+inject_projection_plan(Plan *subplan, List *tlist)
+{
+       Plan       *plan;
+
+       plan = (Plan *) make_result(tlist, NULL, subplan);
+
+       /*
+        * In principle, we should charge tlist eval cost plus cpu_per_tuple per
+        * row for the Result node.  But the former has probably been factored in
+        * already and the latter was not accounted for during Path construction,
+        * so being formally correct might just make the EXPLAIN output look less
+        * consistent not more so.  Hence, just copy the subplan's cost.
+        */
+       copy_plan_costsize(plan, subplan);
+
+       return plan;
+}
+
+/*
+ * create_sort_plan
+ *
+ *       Create a Sort plan for 'best_path' and (recursively) plans
+ *       for its subpaths.
+ */
+static Sort *
+create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
+{
+       Sort       *plan;
+       Plan       *subplan;
+
+       /*
+        * We don't want any excess columns in the sorted tuples, so request a
+        * smaller tlist.  Otherwise, since Sort doesn't project, tlist
+        * requirements pass through.
+        */
+       subplan = create_plan_recurse(root, best_path->subpath,
+                                                                 flags | CP_SMALL_TLIST);
+
+       plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys);
+
+       copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+       return plan;
+}
+
+/*
+ * create_group_plan
+ *
+ *       Create a Group plan for 'best_path' and (recursively) plans
+ *       for its subpaths.
+ */
+static Group *
+create_group_plan(PlannerInfo *root, GroupPath *best_path)
+{
+       Group      *plan;
+       Plan       *subplan;
+       List       *tlist;
+       List       *quals;
+
+       /*
+        * Group can project, so no need to be terribly picky about child tlist,
+        * but we do need grouping columns to be available
+        */
+       subplan = create_plan_recurse(root, best_path->subpath, CP_LABEL_TLIST);
+
+       tlist = build_path_tlist(root, &best_path->path);
+
+       quals = order_qual_clauses(root, best_path->qual);
+
+       plan = make_group(tlist,
+                                         quals,
+                                         list_length(best_path->groupClause),
+                                         extract_grouping_cols(best_path->groupClause,
+                                                                                       subplan->targetlist),
+                                         extract_grouping_ops(best_path->groupClause),
+                                         subplan);
+
+       copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+       return plan;
+}
+
+/*
+ * create_upper_unique_plan
+ *
+ *       Create a Unique plan for 'best_path' and (recursively) plans
+ *       for its subpaths.
+ */
+static Unique *
+create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flags)
+{
+       Unique     *plan;
+       Plan       *subplan;
+
+       /*
+        * Unique doesn't project, so tlist requirements pass through; moreover we
+        * need grouping columns to be labeled.
+        */
+       subplan = create_plan_recurse(root, best_path->subpath,
+                                                                 flags | CP_LABEL_TLIST);
+
+       plan = make_unique_from_pathkeys(subplan,
+                                                                        best_path->path.pathkeys,
+                                                                        best_path->numkeys);
+
+       copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+       return plan;
+}
+
+/*
+ * create_agg_plan
+ *
+ *       Create an Agg plan for 'best_path' and (recursively) plans
+ *       for its subpaths.
+ */
+static Agg *
+create_agg_plan(PlannerInfo *root, AggPath *best_path)
+{
+       Agg                *plan;
+       Plan       *subplan;
+       List       *tlist;
+       List       *quals;
+
+       /*
+        * Agg can project, so no need to be terribly picky about child tlist, but
+        * we do need grouping columns to be available
+        */
+       subplan = create_plan_recurse(root, best_path->subpath, CP_LABEL_TLIST);
+
+       tlist = build_path_tlist(root, &best_path->path);
+
+       quals = order_qual_clauses(root, best_path->qual);
+
+       plan = make_agg(tlist, quals,
+                                       best_path->aggstrategy,
+                                       best_path->aggsplit,
+                                       list_length(best_path->groupClause),
+                                       extract_grouping_cols(best_path->groupClause,
+                                                                                 subplan->targetlist),
+                                       extract_grouping_ops(best_path->groupClause),
+                                       NIL,
+                                       NIL,
+                                       best_path->numGroups,
+                                       subplan);
+
+       copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+       return plan;
+}
+
+/*
+ * Given a groupclause for a collection of grouping sets, produce the
+ * corresponding groupColIdx.
+ *
+ * root->grouping_map maps the tleSortGroupRef to the actual column position in
+ * the input tuple. So we get the ref from the entries in the groupclause and
+ * look them up there.
+ */
+static AttrNumber *
+remap_groupColIdx(PlannerInfo *root, List *groupClause)
+{
+       AttrNumber *grouping_map = root->grouping_map;
+       AttrNumber *new_grpColIdx;
+       ListCell   *lc;
+       int                     i;
+
+       Assert(grouping_map);
+
+       new_grpColIdx = palloc0(sizeof(AttrNumber) * list_length(groupClause));
+
+       i = 0;
+       foreach(lc, groupClause)
+       {
+               SortGroupClause *clause = lfirst(lc);
+
+               new_grpColIdx[i++] = grouping_map[clause->tleSortGroupRef];
+       }
+
+       return new_grpColIdx;
+}
+
+/*
+ * create_groupingsets_plan
+ *       Create a plan for 'best_path' and (recursively) plans
+ *       for its subpaths.
+ *
+ *       What we emit is an Agg plan with some vestigial Agg and Sort nodes
+ *       hanging off the side.  The top Agg implements the last grouping set
+ *       specified in the GroupingSetsPath, and any additional grouping sets
+ *       each give rise to a subsidiary Agg and Sort node in the top Agg's
+ *       "chain" list.  These nodes don't participate in the plan directly,
+ *       but they are a convenient way to represent the required data for
+ *       the extra steps.
+ *
+ *       Returns a Plan node.
+ */
+static Plan *
+create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path)
+{
+       Agg                *plan;
+       Plan       *subplan;
+       List       *rollup_groupclauses = best_path->rollup_groupclauses;
+       List       *rollup_lists = best_path->rollup_lists;
+       AttrNumber *grouping_map;
+       int                     maxref;
+       List       *chain;
+       ListCell   *lc,
+                          *lc2;
+
+       /* Shouldn't get here without grouping sets */
+       Assert(root->parse->groupingSets);
+       Assert(rollup_lists != NIL);
+       Assert(list_length(rollup_lists) == list_length(rollup_groupclauses));
+
+       /*
+        * Agg can project, so no need to be terribly picky about child tlist, but
+        * we do need grouping columns to be available
+        */
+       subplan = create_plan_recurse(root, best_path->subpath, CP_LABEL_TLIST);
+
+       /*
+        * Compute the mapping from tleSortGroupRef to column index in the child's
+        * tlist.  First, identify max SortGroupRef in groupClause, for array
+        * sizing.
+        */
+       maxref = 0;
+       foreach(lc, root->parse->groupClause)
+       {
+               SortGroupClause *gc = (SortGroupClause *) lfirst(lc);
+
+               if (gc->tleSortGroupRef > maxref)
+                       maxref = gc->tleSortGroupRef;
+       }
+
+       grouping_map = (AttrNumber *) palloc0((maxref + 1) * sizeof(AttrNumber));
+
+       /* Now look up the column numbers in the child's tlist */
+       foreach(lc, root->parse->groupClause)
+       {
+               SortGroupClause *gc = (SortGroupClause *) lfirst(lc);
+               TargetEntry *tle = get_sortgroupclause_tle(gc, subplan->targetlist);
+
+               grouping_map[gc->tleSortGroupRef] = tle->resno;
+       }
+
+       /*
+        * During setrefs.c, we'll need the grouping_map to fix up the cols lists
+        * in GroupingFunc nodes.  Save it for setrefs.c to use.
+        *
+        * This doesn't work if we're in an inheritance subtree (see notes in
+        * create_modifytable_plan).  Fortunately we can't be because there would
+        * never be grouping in an UPDATE/DELETE; but let's Assert that.
+        */
+       Assert(!root->hasInheritedTarget);
+       Assert(root->grouping_map == NULL);
+       root->grouping_map = grouping_map;
+
+       /*
+        * Generate the side nodes that describe the other sort and group
+        * operations besides the top one.  Note that we don't worry about putting
+        * accurate cost estimates in the side nodes; only the topmost Agg node's
+        * costs will be shown by EXPLAIN.
+        */
+       chain = NIL;
+       if (list_length(rollup_groupclauses) > 1)
+       {
+               forboth(lc, rollup_groupclauses, lc2, rollup_lists)
+               {
+                       List       *groupClause = (List *) lfirst(lc);
+                       List       *gsets = (List *) lfirst(lc2);
+                       AttrNumber *new_grpColIdx;
+                       Plan       *sort_plan;
+                       Plan       *agg_plan;
+
+                       /* We want to iterate over all but the last rollup list elements */
+                       if (lnext(lc) == NULL)
+                               break;
+
+                       new_grpColIdx = remap_groupColIdx(root, groupClause);
+
+                       sort_plan = (Plan *)
+                               make_sort_from_groupcols(groupClause,
+                                                                                new_grpColIdx,
+                                                                                subplan);
+
+                       agg_plan = (Plan *) make_agg(NIL,
+                                                                                NIL,
+                                                                                AGG_SORTED,
+                                                                                AGGSPLIT_SIMPLE,
+                                                                          list_length((List *) linitial(gsets)),
+                                                                                new_grpColIdx,
+                                                                                extract_grouping_ops(groupClause),
+                                                                                gsets,
+                                                                                NIL,
+                                                                                0,             /* numGroups not needed */
+                                                                                sort_plan);
+
+                       /*
+                        * Nuke stuff we don't need to avoid bloating debug output.
+                        */
+                       sort_plan->targetlist = NIL;
+                       sort_plan->lefttree = NULL;
+
+                       chain = lappend(chain, agg_plan);
+               }
+       }
+
+       /*
+        * Now make the final Agg node
+        */
+       {
+               List       *groupClause = (List *) llast(rollup_groupclauses);
+               List       *gsets = (List *) llast(rollup_lists);
+               AttrNumber *top_grpColIdx;
+               int                     numGroupCols;
+
+               top_grpColIdx = remap_groupColIdx(root, groupClause);
+
+               numGroupCols = list_length((List *) linitial(gsets));
+
+               plan = make_agg(build_path_tlist(root, &best_path->path),
+                                               best_path->qual,
+                                               (numGroupCols > 0) ? AGG_SORTED : AGG_PLAIN,
+                                               AGGSPLIT_SIMPLE,
+                                               numGroupCols,
+                                               top_grpColIdx,
+                                               extract_grouping_ops(groupClause),
+                                               gsets,
+                                               chain,
+                                               0,              /* numGroups not needed */
+                                               subplan);
+
+               /* Copy cost data from Path to Plan */
+               copy_generic_path_info(&plan->plan, &best_path->path);
+       }
+
+       return (Plan *) plan;
+}
+
+/*
+ * create_minmaxagg_plan
+ *
+ *       Create a Result plan for 'best_path' and (recursively) plans
+ *       for its subpaths.
+ */
+static Result *
+create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path)
+{
+       Result     *plan;
+       List       *tlist;
+       ListCell   *lc;
+
+       /* Prepare an InitPlan for each aggregate's subquery. */
+       foreach(lc, best_path->mmaggregates)
+       {
+               MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
+               PlannerInfo *subroot = mminfo->subroot;
+               Query      *subparse = subroot->parse;
+               Plan       *plan;
+
+               /*
+                * Generate the plan for the subquery. We already have a Path, but we
+                * have to convert it to a Plan and attach a LIMIT node above it.
+                * Since we are entering a different planner context (subroot),
+                * recurse to create_plan not create_plan_recurse.
+                */
+               plan = create_plan(subroot, mminfo->path);
+
+               plan = (Plan *) make_limit(plan,
+                                                                  subparse->limitOffset,
+                                                                  subparse->limitCount);
+
+               /* Must apply correct cost/width data to Limit node */
+               plan->startup_cost = mminfo->path->startup_cost;
+               plan->total_cost = mminfo->pathcost;
+               plan->plan_rows = 1;
+               plan->plan_width = mminfo->path->pathtarget->width;
+               plan->parallel_aware = false;
+
+               /* Convert the plan into an InitPlan in the outer query. */
+               SS_make_initplan_from_plan(root, subroot, plan, mminfo->param);
+       }
+
+       /* Generate the output plan --- basically just a Result */
+       tlist = build_path_tlist(root, &best_path->path);
+
+       plan = make_result(tlist, (Node *) best_path->quals, NULL);
+
+       copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+       /*
+        * During setrefs.c, we'll need to replace references to the Agg nodes
+        * with InitPlan output params.  (We can't just do that locally in the
+        * MinMaxAgg node, because path nodes above here may have Agg references
+        * as well.)  Save the mmaggregates list to tell setrefs.c to do that.
+        *
+        * This doesn't work if we're in an inheritance subtree (see notes in
+        * create_modifytable_plan).  Fortunately we can't be because there would
+        * never be aggregates in an UPDATE/DELETE; but let's Assert that.
+        */
+       Assert(!root->hasInheritedTarget);
+       Assert(root->minmax_aggs == NIL);
+       root->minmax_aggs = best_path->mmaggregates;
+
+       return plan;
+}
+
+/*
+ * create_windowagg_plan
+ *
+ *       Create a WindowAgg plan for 'best_path' and (recursively) plans
+ *       for its subpaths.
+ */
+static WindowAgg *
+create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path)
+{
+       WindowAgg  *plan;
+       WindowClause *wc = best_path->winclause;
+       Plan       *subplan;
+       List       *tlist;
+       int                     numsortkeys;
+       AttrNumber *sortColIdx;
+       Oid                *sortOperators;
+       Oid                *collations;
+       bool       *nullsFirst;
+       int                     partNumCols;
+       AttrNumber *partColIdx;
+       Oid                *partOperators;
+       int                     ordNumCols;
+       AttrNumber *ordColIdx;
+       Oid                *ordOperators;
+
+       /*
+        * WindowAgg can project, so no need to be terribly picky about child
+        * tlist, but we do need grouping columns to be available
+        */
+       subplan = create_plan_recurse(root, best_path->subpath, CP_LABEL_TLIST);
+
+       tlist = build_path_tlist(root, &best_path->path);
+
+       /*
+        * We shouldn't need to actually sort, but it's convenient to use
+        * prepare_sort_from_pathkeys to identify the input's sort columns.
+        */
+       subplan = prepare_sort_from_pathkeys(subplan,
+                                                                                best_path->winpathkeys,
+                                                                                NULL,
+                                                                                NULL,
+                                                                                false,
+                                                                                &numsortkeys,
+                                                                                &sortColIdx,
+                                                                                &sortOperators,
+                                                                                &collations,
+                                                                                &nullsFirst);
+
+       /* Now deconstruct that into partition and ordering portions */
+       get_column_info_for_window(root,
+                                                          wc,
+                                                          subplan->targetlist,
+                                                          numsortkeys,
+                                                          sortColIdx,
+                                                          &partNumCols,
+                                                          &partColIdx,
+                                                          &partOperators,
+                                                          &ordNumCols,
+                                                          &ordColIdx,
+                                                          &ordOperators);
+
+       /* And finally we can make the WindowAgg node */
+       plan = make_windowagg(tlist,
+                                                 wc->winref,
+                                                 partNumCols,
+                                                 partColIdx,
+                                                 partOperators,
+                                                 ordNumCols,
+                                                 ordColIdx,
+                                                 ordOperators,
+                                                 wc->frameOptions,
+                                                 wc->startOffset,
+                                                 wc->endOffset,
+                                                 subplan);
+
+       copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+       return plan;
+}
+
+/*
+ * get_column_info_for_window
+ *             Get the partitioning/ordering column numbers and equality operators
+ *             for a WindowAgg node.
+ *
+ * This depends on the behavior of planner.c's make_pathkeys_for_window!
+ *
+ * We are given the target WindowClause and an array of the input column
+ * numbers associated with the resulting pathkeys.  In the easy case, there
+ * are the same number of pathkey columns as partitioning + ordering columns
+ * and we just have to copy some data around.  However, it's possible that
+ * some of the original partitioning + ordering columns were eliminated as
+ * redundant during the transformation to pathkeys.  (This can happen even
+ * though the parser gets rid of obvious duplicates.  A typical scenario is a
+ * window specification "PARTITION BY x ORDER BY y" coupled with a clause
+ * "WHERE x = y" that causes the two sort columns to be recognized as
+ * redundant.) In that unusual case, we have to work a lot harder to
+ * determine which keys are significant.
+ *
+ * The method used here is a bit brute-force: add the sort columns to a list
+ * one at a time and note when the resulting pathkey list gets longer.  But
+ * it's a sufficiently uncommon case that a faster way doesn't seem worth
+ * the amount of code refactoring that'd be needed.
+ */
+static void
+get_column_info_for_window(PlannerInfo *root, WindowClause *wc, List *tlist,
+                                                  int numSortCols, AttrNumber *sortColIdx,
+                                                  int *partNumCols,
+                                                  AttrNumber **partColIdx,
+                                                  Oid **partOperators,
+                                                  int *ordNumCols,
+                                                  AttrNumber **ordColIdx,
+                                                  Oid **ordOperators)
+{
+       int                     numPart = list_length(wc->partitionClause);
+       int                     numOrder = list_length(wc->orderClause);
+
+       if (numSortCols == numPart + numOrder)
+       {
+               /* easy case */
+               *partNumCols = numPart;
+               *partColIdx = sortColIdx;
+               *partOperators = extract_grouping_ops(wc->partitionClause);
+               *ordNumCols = numOrder;
+               *ordColIdx = sortColIdx + numPart;
+               *ordOperators = extract_grouping_ops(wc->orderClause);
+       }
+       else
+       {
+               List       *sortclauses;
+               List       *pathkeys;
+               int                     scidx;
+               ListCell   *lc;
+
+               /* first, allocate what's certainly enough space for the arrays */
+               *partNumCols = 0;
+               *partColIdx = (AttrNumber *) palloc(numPart * sizeof(AttrNumber));
+               *partOperators = (Oid *) palloc(numPart * sizeof(Oid));
+               *ordNumCols = 0;
+               *ordColIdx = (AttrNumber *) palloc(numOrder * sizeof(AttrNumber));
+               *ordOperators = (Oid *) palloc(numOrder * sizeof(Oid));
+               sortclauses = NIL;
+               pathkeys = NIL;
+               scidx = 0;
+               foreach(lc, wc->partitionClause)
+               {
+                       SortGroupClause *sgc = (SortGroupClause *) lfirst(lc);
+                       List       *new_pathkeys;
+
+                       sortclauses = lappend(sortclauses, sgc);
+                       new_pathkeys = make_pathkeys_for_sortclauses(root,
+                                                                                                                sortclauses,
+                                                                                                                tlist);
+                       if (list_length(new_pathkeys) > list_length(pathkeys))
+                       {
+                               /* this sort clause is actually significant */
+                               (*partColIdx)[*partNumCols] = sortColIdx[scidx++];
+                               (*partOperators)[*partNumCols] = sgc->eqop;
+                               (*partNumCols)++;
+                               pathkeys = new_pathkeys;
+                       }
+               }
+               foreach(lc, wc->orderClause)
+               {
+                       SortGroupClause *sgc = (SortGroupClause *) lfirst(lc);
+                       List       *new_pathkeys;
+
+                       sortclauses = lappend(sortclauses, sgc);
+                       new_pathkeys = make_pathkeys_for_sortclauses(root,
+                                                                                                                sortclauses,
+                                                                                                                tlist);
+                       if (list_length(new_pathkeys) > list_length(pathkeys))
+                       {
+                               /* this sort clause is actually significant */
+                               (*ordColIdx)[*ordNumCols] = sortColIdx[scidx++];
+                               (*ordOperators)[*ordNumCols] = sgc->eqop;
+                               (*ordNumCols)++;
+                               pathkeys = new_pathkeys;
+                       }
+               }
+               /* complain if we didn't eat exactly the right number of sort cols */
+               if (scidx != numSortCols)
+                       elog(ERROR, "failed to deconstruct sort operators into partitioning/ordering operators");
+       }
+}
+
+/*
+ * create_setop_plan
+ *
+ *       Create a SetOp plan for 'best_path' and (recursively) plans
+ *       for its subpaths.
+ */
+static SetOp *
+create_setop_plan(PlannerInfo *root, SetOpPath *best_path, int flags)
+{
+       SetOp      *plan;
+       Plan       *subplan;
+       long            numGroups;
+
+       /*
+        * SetOp doesn't project, so tlist requirements pass through; moreover we
+        * need grouping columns to be labeled.
+        */
+       subplan = create_plan_recurse(root, best_path->subpath,
+                                                                 flags | CP_LABEL_TLIST);
+
+       /* Convert numGroups to long int --- but 'ware overflow! */
+       numGroups = (long) Min(best_path->numGroups, (double) LONG_MAX);
+
+       plan = make_setop(best_path->cmd,
+                                         best_path->strategy,
+                                         subplan,
+                                         best_path->distinctList,
+                                         best_path->flagColIdx,
+                                         best_path->firstFlag,
+                                         numGroups);
+
+       copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+       return plan;
+}
+
+/*
+ * create_recursiveunion_plan
+ *
+ *       Create a RecursiveUnion plan for 'best_path' and (recursively) plans
+ *       for its subpaths.
+ */
+static RecursiveUnion *
+create_recursiveunion_plan(PlannerInfo *root, RecursiveUnionPath *best_path)
+{
+       RecursiveUnion *plan;
+       Plan       *leftplan;
+       Plan       *rightplan;
+       List       *tlist;
+       long            numGroups;
+
+       /* Need both children to produce same tlist, so force it */
+       leftplan = create_plan_recurse(root, best_path->leftpath, CP_EXACT_TLIST);
+       rightplan = create_plan_recurse(root, best_path->rightpath, CP_EXACT_TLIST);
+
+       tlist = build_path_tlist(root, &best_path->path);
+
+       /* Convert numGroups to long int --- but 'ware overflow! */
+       numGroups = (long) Min(best_path->numGroups, (double) LONG_MAX);
+
+       plan = make_recursive_union(tlist,
+                                                               leftplan,
+                                                               rightplan,
+                                                               best_path->wtParam,
+                                                               best_path->distinctList,
+                                                               numGroups);
+
+       copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+       return plan;
+}
+
+/*
+ * create_lockrows_plan
+ *
+ *       Create a LockRows plan for 'best_path' and (recursively) plans
+ *       for its subpaths.
+ */
+static LockRows *
+create_lockrows_plan(PlannerInfo *root, LockRowsPath *best_path,
+                                        int flags)
+{
+       LockRows   *plan;
+       Plan       *subplan;
+
+       /* LockRows doesn't project, so tlist requirements pass through */
+       subplan = create_plan_recurse(root, best_path->subpath, flags);
+
+       plan = make_lockrows(subplan, best_path->rowMarks, best_path->epqParam);
+
+       copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+       return plan;
+}
+
+/*
+ * create_modifytable_plan
+ *       Create a ModifyTable plan for 'best_path'.
+ *
+ *       Returns a Plan node.
+ */
+static ModifyTable *
+create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path)
+{
+       ModifyTable *plan;
+       List       *subplans = NIL;
+       ListCell   *subpaths,
+                          *subroots;
+
+       /* Build the plan for each input path */
+       forboth(subpaths, best_path->subpaths,
+                       subroots, best_path->subroots)
+       {
+               Path       *subpath = (Path *) lfirst(subpaths);
+               PlannerInfo *subroot = (PlannerInfo *) lfirst(subroots);
+               Plan       *subplan;
+
+               /*
+                * In an inherited UPDATE/DELETE, reference the per-child modified
+                * subroot while creating Plans from Paths for the child rel.  This is
+                * a kluge, but otherwise it's too hard to ensure that Plan creation
+                * functions (particularly in FDWs) don't depend on the contents of
+                * "root" matching what they saw at Path creation time.  The main
+                * downside is that creation functions for Plans that might appear
+                * below a ModifyTable cannot expect to modify the contents of "root"
+                * and have it "stick" for subsequent processing such as setrefs.c.
+                * That's not great, but it seems better than the alternative.
+                */
+               subplan = create_plan_recurse(subroot, subpath, CP_EXACT_TLIST);
+
+               /* Transfer resname/resjunk labeling, too, to keep executor happy */
+               apply_tlist_labeling(subplan->targetlist, subroot->processed_tlist);
+
+               subplans = lappend(subplans, subplan);
+       }
+
+       plan = make_modifytable(root,
+                                                       best_path->operation,
+                                                       best_path->canSetTag,
+                                                       best_path->nominalRelation,
+                                                       best_path->resultRelations,
+                                                       subplans,
+                                                       best_path->withCheckOptionLists,
+                                                       best_path->returningLists,
+                                                       best_path->rowMarks,
+                                                       best_path->onconflict,
+                                                       best_path->epqParam);
+
+       copy_generic_path_info(&plan->plan, &best_path->path);
+
+       return plan;
+}
+
+/*
+ * create_limit_plan
+ *
+ *       Create a Limit plan for 'best_path' and (recursively) plans
+ *       for its subpaths.
+ */
+static Limit *
+create_limit_plan(PlannerInfo *root, LimitPath *best_path, int flags)
+{
+       Limit      *plan;
+       Plan       *subplan;
+
+       /* Limit doesn't project, so tlist requirements pass through */
+       subplan = create_plan_recurse(root, best_path->subpath, flags);
+
+       plan = make_limit(subplan,
+                                         best_path->limitOffset,
+                                         best_path->limitCount);
+
+       copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+       return plan;
+}
+
+
+/*****************************************************************************
+ *
+ *     BASE-RELATION SCAN METHODS
+ *
+ *****************************************************************************/
+
+
+/*
+ * create_seqscan_plan
+ *      Returns a seqscan plan for the base relation scanned by 'best_path'
+ *      with restriction clauses 'scan_clauses' and targetlist 'tlist'.
+ */
+static SeqScan *
+create_seqscan_plan(PlannerInfo *root, Path *best_path,
+                                       List *tlist, List *scan_clauses)
+{
+       SeqScan    *scan_plan;
+       Index           scan_relid = best_path->parent->relid;
+
+       /* it should be a base rel... */
+       Assert(scan_relid > 0);
+       Assert(best_path->parent->rtekind == RTE_RELATION);
+
+       /* Sort clauses into best execution order */
+       scan_clauses = order_qual_clauses(root, scan_clauses);
+
+       /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
+       scan_clauses = extract_actual_clauses(scan_clauses, false);
+
+       /* Replace any outer-relation variables with nestloop params */
+       if (best_path->param_info)
+       {
+               scan_clauses = (List *)
+                       replace_nestloop_params(root, (Node *) scan_clauses);
+       }
+
+       scan_plan = make_seqscan(tlist,
+                                                        scan_clauses,
+                                                        scan_relid);
 
-/*****************************************************************************
- *
- *     BASE-RELATION SCAN METHODS
- *
- *****************************************************************************/
+       copy_generic_path_info(&scan_plan->plan, best_path);
 
+       return scan_plan;
+}
 
 /*
- * create_seqscan_plan
- *      Returns a seqscan plan for the base relation scanned by 'best_path'
+ * create_samplescan_plan
+ *      Returns a samplescan plan for the base relation scanned by 'best_path'
  *      with restriction clauses 'scan_clauses' and targetlist 'tlist'.
  */
-static SeqScan *
-create_seqscan_plan(PlannerInfo *root, Path *best_path,
-                                       List *tlist, List *scan_clauses)
+static SampleScan *
+create_samplescan_plan(PlannerInfo *root, Path *best_path,
+                                          List *tlist, List *scan_clauses)
 {
-       SeqScan    *scan_plan;
+       SampleScan *scan_plan;
        Index           scan_relid = best_path->parent->relid;
+       RangeTblEntry *rte;
+       TableSampleClause *tsc;
 
-       /* it should be a base rel... */
+       /* it should be a base rel with a tablesample clause... */
        Assert(scan_relid > 0);
-       Assert(best_path->parent->rtekind == RTE_RELATION);
+       rte = planner_rt_fetch(scan_relid, root);
+       Assert(rte->rtekind == RTE_RELATION);
+       tsc = rte->tablesample;
+       Assert(tsc != NULL);
 
        /* Sort clauses into best execution order */
        scan_clauses = order_qual_clauses(root, scan_clauses);
@@ -1118,13 +2429,16 @@ create_seqscan_plan(PlannerInfo *root, Path *best_path,
        {
                scan_clauses = (List *)
                        replace_nestloop_params(root, (Node *) scan_clauses);
+               tsc = (TableSampleClause *)
+                       replace_nestloop_params(root, (Node *) tsc);
        }
 
-       scan_plan = make_seqscan(tlist,
-                                                        scan_clauses,
-                                                        scan_relid);
+       scan_plan = make_samplescan(tlist,
+                                                               scan_clauses,
+                                                               scan_relid,
+                                                               tsc);
 
-       copy_path_costsize(&scan_plan->plan, best_path);
+       copy_generic_path_info(&scan_plan->scan.plan, best_path);
 
        return scan_plan;
 }
@@ -1155,6 +2469,7 @@ create_indexscan_plan(PlannerInfo *root,
        List       *stripped_indexquals;
        List       *fixed_indexquals;
        List       *fixed_indexorderbys;
+       List       *indexorderbyops = NIL;
        ListCell   *l;
 
        /* it should be a base rel... */
@@ -1203,38 +2518,23 @@ create_indexscan_plan(PlannerInfo *root,
         * first input contains only immutable functions, so we have to check
         * that.)
         *
-        * We can also discard quals that are implied by a partial index's
-        * predicate, but only in a plain SELECT; when scanning a target relation
-        * of UPDATE/DELETE/SELECT FOR UPDATE, we must leave such quals in the
-        * plan so that they'll be properly rechecked by EvalPlanQual testing.
+        * Note: if you change this bit of code you should also look at
+        * extract_nonindex_conditions() in costsize.c.
         */
        qpqual = NIL;
        foreach(l, scan_clauses)
        {
-               RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
+               RestrictInfo *rinfo = castNode(RestrictInfo, lfirst(l));
 
-               Assert(IsA(rinfo, RestrictInfo));
                if (rinfo->pseudoconstant)
                        continue;                       /* we may drop pseudoconstants here */
                if (list_member_ptr(indexquals, rinfo))
                        continue;                       /* simple duplicate */
                if (is_redundant_derived_clause(rinfo, indexquals))
                        continue;                       /* derived from same EquivalenceClass */
-               if (!contain_mutable_functions((Node *) rinfo->clause))
-               {
-                       List       *clausel = list_make1(rinfo->clause);
-
-                       if (predicate_implied_by(clausel, indexquals))
-                               continue;               /* provably implied by indexquals */
-                       if (best_path->indexinfo->indpred)
-                       {
-                               if (baserelid != root->parse->resultRelation &&
-                                       get_parse_rowmark(root->parse, baserelid) == NULL)
-                                       if (predicate_implied_by(clausel,
-                                                                                        best_path->indexinfo->indpred))
-                                               continue;               /* implied by index predicate */
-                       }
-               }
+               if (!contain_mutable_functions((Node *) rinfo->clause) &&
+                       predicate_implied_by(list_make1(rinfo->clause), indexquals))
+                       continue;                       /* provably implied by indexquals */
                qpqual = lappend(qpqual, rinfo);
        }
 
@@ -1263,6 +2563,39 @@ create_indexscan_plan(PlannerInfo *root,
                        replace_nestloop_params(root, (Node *) indexorderbys);
        }
 
+       /*
+        * If there are ORDER BY expressions, look up the sort operators for their
+        * result datatypes.
+        */
+       if (indexorderbys)
+       {
+               ListCell   *pathkeyCell,
+                                  *exprCell;
+
+               /*
+                * PathKey contains OID of the btree opfamily we're sorting by, but
+                * that's not quite enough because we need the expression's datatype
+                * to look up the sort operator in the operator family.
+                */
+               Assert(list_length(best_path->path.pathkeys) == list_length(indexorderbys));
+               forboth(pathkeyCell, best_path->path.pathkeys, exprCell, indexorderbys)
+               {
+                       PathKey    *pathkey = (PathKey *) lfirst(pathkeyCell);
+                       Node       *expr = (Node *) lfirst(exprCell);
+                       Oid                     exprtype = exprType(expr);
+                       Oid                     sortop;
+
+                       /* Get sort operator from opfamily */
+                       sortop = get_opfamily_member(pathkey->pk_opfamily,
+                                                                                exprtype,
+                                                                                exprtype,
+                                                                                pathkey->pk_strategy);
+                       if (!OidIsValid(sortop))
+                               elog(ERROR, "failed to find sort operator for ORDER BY expression");
+                       indexorderbyops = lappend_oid(indexorderbyops, sortop);
+               }
+       }
+
        /* Finally ready to build the plan node */
        if (indexonly)
                scan_plan = (Scan *) make_indexonlyscan(tlist,
@@ -1282,9 +2615,10 @@ create_indexscan_plan(PlannerInfo *root,
                                                                                        stripped_indexquals,
                                                                                        fixed_indexorderbys,
                                                                                        indexorderbys,
+                                                                                       indexorderbyops,
                                                                                        best_path->indexscandir);
 
-       copy_path_costsize(&scan_plan->plan, &best_path->path);
+       copy_generic_path_info(&scan_plan->plan, &best_path->path);
 
        return scan_plan;
 }
@@ -1318,6 +2652,9 @@ create_bitmap_scan_plan(PlannerInfo *root,
                                                                                   &bitmapqualorig, &indexquals,
                                                                                   &indexECs);
 
+       if (best_path->path.parallel_aware)
+               bitmap_subplan_mark_shared(bitmapqualplan);
+
        /*
         * The qpqual list must contain all restrictions not automatically handled
         * by the index, other than pseudoconstant clauses which will be handled
@@ -1337,32 +2674,28 @@ create_bitmap_scan_plan(PlannerInfo *root,
         * redundant with any top-level indexqual by virtue of being generated
         * from the same EC.  After that, try predicate_implied_by().
         *
-        * Unlike create_indexscan_plan(), we need take no special thought here
-        * for partial index predicates; this is because the predicate conditions
-        * are already listed in bitmapqualorig and indexquals.  Bitmap scans have
-        * to do it that way because predicate conditions need to be rechecked if
-        * the scan becomes lossy, so they have to be included in bitmapqualorig.
+        * Unlike create_indexscan_plan(), the predicate_implied_by() test here is
+        * useful for getting rid of qpquals that are implied by index predicates,
+        * because the predicate conditions are included in the "indexquals"
+        * returned by create_bitmap_subplan().  Bitmap scans have to do it that
+        * way because predicate conditions need to be rechecked if the scan
+        * becomes lossy, so they have to be included in bitmapqualorig.
         */
        qpqual = NIL;
        foreach(l, scan_clauses)
        {
-               RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
+               RestrictInfo *rinfo = castNode(RestrictInfo, lfirst(l));
                Node       *clause = (Node *) rinfo->clause;
 
-               Assert(IsA(rinfo, RestrictInfo));
                if (rinfo->pseudoconstant)
                        continue;                       /* we may drop pseudoconstants here */
                if (list_member(indexquals, clause))
                        continue;                       /* simple duplicate */
                if (rinfo->parent_ec && list_member_ptr(indexECs, rinfo->parent_ec))
                        continue;                       /* derived from same EquivalenceClass */
-               if (!contain_mutable_functions(clause))
-               {
-                       List       *clausel = list_make1(clause);
-
-                       if (predicate_implied_by(clausel, indexquals))
-                               continue;               /* provably implied by indexquals */
-               }
+               if (!contain_mutable_functions(clause) &&
+                       predicate_implied_by(list_make1(clause), indexquals))
+                       continue;                       /* provably implied by indexquals */
                qpqual = lappend(qpqual, rinfo);
        }
 
@@ -1400,7 +2733,7 @@ create_bitmap_scan_plan(PlannerInfo *root,
                                                                         bitmapqualorig,
                                                                         baserelid);
 
-       copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
+       copy_generic_path_info(&scan_plan->scan.plan, &best_path->path);
 
        return scan_plan;
 }
@@ -1469,6 +2802,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
                plan->plan_rows =
                        clamp_row_est(apath->bitmapselectivity * apath->path.parent->tuples);
                plan->plan_width = 0;   /* meaningless */
+               plan->parallel_aware = false;
                *qual = subquals;
                *indexqual = subindexquals;
                *indexECs = subindexECs;
@@ -1531,6 +2865,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
                        plan->plan_rows =
                                clamp_row_est(opath->bitmapselectivity * opath->path.parent->tuples);
                        plan->plan_width = 0;           /* meaningless */
+                       plan->parallel_aware = false;
                }
 
                /*
@@ -1560,19 +2895,21 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
                ListCell   *l;
 
                /* Use the regular indexscan plan build machinery... */
-               iscan = (IndexScan *) create_indexscan_plan(root, ipath,
-                                                                                                       NIL, NIL, false);
-               Assert(IsA(iscan, IndexScan));
+               iscan = castNode(IndexScan,
+                                                create_indexscan_plan(root, ipath,
+                                                                                          NIL, NIL, false));
                /* then convert to a bitmap indexscan */
                plan = (Plan *) make_bitmap_indexscan(iscan->scan.scanrelid,
                                                                                          iscan->indexid,
                                                                                          iscan->indexqual,
                                                                                          iscan->indexqualorig);
+               /* and set its cost/width fields appropriately */
                plan->startup_cost = 0.0;
                plan->total_cost = ipath->indextotalcost;
                plan->plan_rows =
                        clamp_row_est(ipath->indexselectivity * ipath->path.parent->tuples);
                plan->plan_width = 0;   /* meaningless */
+               plan->parallel_aware = false;
                *qual = get_actual_clauses(ipath->indexclauses);
                *indexqual = get_actual_clauses(ipath->indexquals);
                foreach(l, ipath->indexinfo->indpred)
@@ -1657,7 +2994,7 @@ create_tidscan_plan(PlannerInfo *root, TidPath *best_path,
                                                         scan_relid,
                                                         tidquals);
 
-       copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
+       copy_generic_path_info(&scan_plan->scan.plan, &best_path->path);
 
        return scan_plan;
 }
@@ -1668,15 +3005,24 @@ create_tidscan_plan(PlannerInfo *root, TidPath *best_path,
  *      with restriction clauses 'scan_clauses' and targetlist 'tlist'.
  */
 static SubqueryScan *
-create_subqueryscan_plan(PlannerInfo *root, Path *best_path,
+create_subqueryscan_plan(PlannerInfo *root, SubqueryScanPath *best_path,
                                                 List *tlist, List *scan_clauses)
 {
        SubqueryScan *scan_plan;
-       Index           scan_relid = best_path->parent->relid;
+       RelOptInfo *rel = best_path->path.parent;
+       Index           scan_relid = rel->relid;
+       Plan       *subplan;
 
        /* it should be a subquery base rel... */
        Assert(scan_relid > 0);
-       Assert(best_path->parent->rtekind == RTE_SUBQUERY);
+       Assert(rel->rtekind == RTE_SUBQUERY);
+
+       /*
+        * Recursively create Plan from Path for subquery.  Since we are entering
+        * a different planner context (subroot), recurse to create_plan not
+        * create_plan_recurse.
+        */
+       subplan = create_plan(rel->subroot, best_path->subpath);
 
        /* Sort clauses into best execution order */
        scan_clauses = order_qual_clauses(root, scan_clauses);
@@ -1685,20 +3031,20 @@ create_subqueryscan_plan(PlannerInfo *root, Path *best_path,
        scan_clauses = extract_actual_clauses(scan_clauses, false);
 
        /* Replace any outer-relation variables with nestloop params */
-       if (best_path->param_info)
+       if (best_path->path.param_info)
        {
                scan_clauses = (List *)
                        replace_nestloop_params(root, (Node *) scan_clauses);
                process_subquery_nestloop_params(root,
-                                                                                best_path->parent->subplan_params);
+                                                                                rel->subplan_params);
        }
 
        scan_plan = make_subqueryscan(tlist,
                                                                  scan_clauses,
                                                                  scan_relid,
-                                                                 best_path->parent->subplan);
+                                                                 subplan);
 
-       copy_path_costsize(&scan_plan->scan.plan, best_path);
+       copy_generic_path_info(&scan_plan->scan.plan, &best_path->path);
 
        return scan_plan;
 }
@@ -1741,7 +3087,50 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path,
        scan_plan = make_functionscan(tlist, scan_clauses, scan_relid,
                                                                  functions, rte->funcordinality);
 
-       copy_path_costsize(&scan_plan->scan.plan, best_path);
+       copy_generic_path_info(&scan_plan->scan.plan, best_path);
+
+       return scan_plan;
+}
+
+/*
+ * create_tablefuncscan_plan
+ *      Returns a tablefuncscan plan for the base relation scanned by 'best_path'
+ *      with restriction clauses 'scan_clauses' and targetlist 'tlist'.
+ */
+static TableFuncScan *
+create_tablefuncscan_plan(PlannerInfo *root, Path *best_path,
+                                                 List *tlist, List *scan_clauses)
+{
+       TableFuncScan *scan_plan;
+       Index           scan_relid = best_path->parent->relid;
+       RangeTblEntry *rte;
+       TableFunc  *tablefunc;
+
+       /* it should be a function base rel... */
+       Assert(scan_relid > 0);
+       rte = planner_rt_fetch(scan_relid, root);
+       Assert(rte->rtekind == RTE_TABLEFUNC);
+       tablefunc = rte->tablefunc;
+
+       /* Sort clauses into best execution order */
+       scan_clauses = order_qual_clauses(root, scan_clauses);
+
+       /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
+       scan_clauses = extract_actual_clauses(scan_clauses, false);
+
+       /* Replace any outer-relation variables with nestloop params */
+       if (best_path->param_info)
+       {
+               scan_clauses = (List *)
+                       replace_nestloop_params(root, (Node *) scan_clauses);
+               /* The function expressions could contain nestloop params, too */
+               tablefunc = (TableFunc *) replace_nestloop_params(root, (Node *) tablefunc);
+       }
+
+       scan_plan = make_tablefuncscan(tlist, scan_clauses, scan_relid,
+                                                                  tablefunc);
+
+       copy_generic_path_info(&scan_plan->scan.plan, best_path);
 
        return scan_plan;
 }
@@ -1785,7 +3174,7 @@ create_valuesscan_plan(PlannerInfo *root, Path *best_path,
        scan_plan = make_valuesscan(tlist, scan_clauses, scan_relid,
                                                                values_lists);
 
-       copy_path_costsize(&scan_plan->scan.plan, best_path);
+       copy_generic_path_info(&scan_plan->scan.plan, best_path);
 
        return scan_plan;
 }
@@ -1878,7 +3267,7 @@ create_ctescan_plan(PlannerInfo *root, Path *best_path,
        scan_plan = make_ctescan(tlist, scan_clauses, scan_relid,
                                                         plan_id, cte_param_id);
 
-       copy_path_costsize(&scan_plan->scan.plan, best_path);
+       copy_generic_path_info(&scan_plan->scan.plan, best_path);
 
        return scan_plan;
 }
@@ -1938,14 +3327,14 @@ create_worktablescan_plan(PlannerInfo *root, Path *best_path,
        scan_plan = make_worktablescan(tlist, scan_clauses, scan_relid,
                                                                   cteroot->wt_param_id);
 
-       copy_path_costsize(&scan_plan->scan.plan, best_path);
+       copy_generic_path_info(&scan_plan->scan.plan, best_path);
 
        return scan_plan;
 }
 
 /*
  * create_foreignscan_plan
- *      Returns a foreignscan plan for the base relation scanned by 'best_path'
+ *      Returns a foreignscan plan for the relation scanned by 'best_path'
  *      with restriction clauses 'scan_clauses' and targetlist 'tlist'.
  */
 static ForeignScan *
@@ -1955,14 +3344,29 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
        ForeignScan *scan_plan;
        RelOptInfo *rel = best_path->path.parent;
        Index           scan_relid = rel->relid;
-       RangeTblEntry *rte;
-       int                     i;
+       Oid                     rel_oid = InvalidOid;
+       Plan       *outer_plan = NULL;
 
-       /* it should be a base rel... */
-       Assert(scan_relid > 0);
-       Assert(rel->rtekind == RTE_RELATION);
-       rte = planner_rt_fetch(scan_relid, root);
-       Assert(rte->rtekind == RTE_RELATION);
+       Assert(rel->fdwroutine != NULL);
+
+       /* transform the child path if any */
+       if (best_path->fdw_outerpath)
+               outer_plan = create_plan_recurse(root, best_path->fdw_outerpath,
+                                                                                CP_EXACT_TLIST);
+
+       /*
+        * If we're scanning a base relation, fetch its OID.  (Irrelevant if
+        * scanning a join relation.)
+        */
+       if (scan_relid > 0)
+       {
+               RangeTblEntry *rte;
+
+               Assert(rel->rtekind == RTE_RELATION);
+               rte = planner_rt_fetch(scan_relid, root);
+               Assert(rte->rtekind == RTE_RELATION);
+               rel_oid = rte->relid;
+       }
 
        /*
         * Sort clauses into best execution order.  We do this first since the FDW
@@ -1977,19 +3381,42 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
         * has selected some join clauses for remote use but also wants them
         * rechecked locally).
         */
-       scan_plan = rel->fdwroutine->GetForeignPlan(root, rel, rte->relid,
+       scan_plan = rel->fdwroutine->GetForeignPlan(root, rel, rel_oid,
                                                                                                best_path,
-                                                                                               tlist, scan_clauses);
+                                                                                               tlist, scan_clauses,
+                                                                                               outer_plan);
 
        /* Copy cost data from Path to Plan; no need to make FDW do this */
-       copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
+       copy_generic_path_info(&scan_plan->scan.plan, &best_path->path);
+
+       /* Copy foreign server OID; likewise, no need to make FDW do this */
+       scan_plan->fs_server = rel->serverid;
 
        /*
-        * Replace any outer-relation variables with nestloop params in the qual
-        * and fdw_exprs expressions.  We do this last so that the FDW doesn't
-        * have to be involved.  (Note that parts of fdw_exprs could have come
-        * from join clauses, so doing this beforehand on the scan_clauses
-        * wouldn't work.)
+        * Likewise, copy the relids that are represented by this foreign scan. An
+        * upper rel doesn't have relids set, but it covers all the base relations
+        * participating in the underlying scan, so use root's all_baserels.
+        */
+       if (rel->reloptkind == RELOPT_UPPER_REL)
+               scan_plan->fs_relids = root->all_baserels;
+       else
+               scan_plan->fs_relids = best_path->path.parent->relids;
+
+       /*
+        * If this is a foreign join, and to make it valid to push down we had to
+        * assume that the current user is the same as some user explicitly named
+        * in the query, mark the finished plan as depending on the current user.
+        */
+       if (rel->useridiscurrent)
+               root->glob->dependsOnRole = true;
+
+       /*
+        * Replace any outer-relation variables with nestloop params in the qual,
+        * fdw_exprs and fdw_recheck_quals expressions.  We do this last so that
+        * the FDW doesn't have to be involved.  (Note that parts of fdw_exprs or
+        * fdw_recheck_quals could have come from join clauses, so doing this
+        * beforehand on the scan_clauses wouldn't work.)  We assume
+        * fdw_scan_tlist contains no such variables.
         */
        if (best_path->path.param_info)
        {
@@ -1997,21 +3424,53 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
                        replace_nestloop_params(root, (Node *) scan_plan->scan.plan.qual);
                scan_plan->fdw_exprs = (List *)
                        replace_nestloop_params(root, (Node *) scan_plan->fdw_exprs);
+               scan_plan->fdw_recheck_quals = (List *)
+                       replace_nestloop_params(root,
+                                                                       (Node *) scan_plan->fdw_recheck_quals);
        }
 
        /*
-        * Detect whether any system columns are requested from rel.  This is a
-        * bit of a kluge and might go away someday, so we intentionally leave it
-        * out of the API presented to FDWs.
+        * If rel is a base relation, detect whether any system columns are
+        * requested from the rel.  (If rel is a join relation, rel->relid will be
+        * 0, but there can be no Var with relid 0 in the rel's targetlist or the
+        * restriction clauses, so we skip this in that case.  Note that any such
+        * columns in base relations that were joined are assumed to be contained
+        * in fdw_scan_tlist.)  This is a bit of a kluge and might go away
+        * someday, so we intentionally leave it out of the API presented to FDWs.
         */
        scan_plan->fsSystemCol = false;
-       for (i = rel->min_attr; i < 0; i++)
+       if (scan_relid > 0)
        {
-               if (!bms_is_empty(rel->attr_needed[i - rel->min_attr]))
+               Bitmapset  *attrs_used = NULL;
+               ListCell   *lc;
+               int                     i;
+
+               /*
+                * First, examine all the attributes needed for joins or final output.
+                * Note: we must look at rel's targetlist, not the attr_needed data,
+                * because attr_needed isn't computed for inheritance child rels.
+                */
+               pull_varattnos((Node *) rel->reltarget->exprs, scan_relid, &attrs_used);
+
+               /* Add all the attributes used by restriction clauses. */
+               foreach(lc, rel->baserestrictinfo)
                {
-                       scan_plan->fsSystemCol = true;
-                       break;
+                       RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+
+                       pull_varattnos((Node *) rinfo->clause, scan_relid, &attrs_used);
+               }
+
+               /* Now, are any system columns requested from rel? */
+               for (i = FirstLowInvalidHeapAttributeNumber + 1; i < 0; i++)
+               {
+                       if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber, attrs_used))
+                       {
+                               scan_plan->fsSystemCol = true;
+                               break;
+                       }
                }
+
+               bms_free(attrs_used);
        }
 
        return scan_plan;
@@ -2022,18 +3481,23 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
  *
  * Transform a CustomPath into a Plan.
  */
-static Plan *
+static CustomScan *
 create_customscan_plan(PlannerInfo *root, CustomPath *best_path,
                                           List *tlist, List *scan_clauses)
 {
-       Plan       *plan;
+       CustomScan *cplan;
        RelOptInfo *rel = best_path->path.parent;
+       List       *custom_plans = NIL;
+       ListCell   *lc;
 
-       /*
-        * Right now, all we can support is CustomScan node which is associated
-        * with a particular base relation to be scanned.
-        */
-       Assert(rel && rel->reloptkind == RELOPT_BASEREL);
+       /* Recursively transform child paths. */
+       foreach(lc, best_path->custom_paths)
+       {
+               Plan       *plan = create_plan_recurse(root, (Path *) lfirst(lc),
+                                                                                          CP_EXACT_TLIST);
+
+               custom_plans = lappend(custom_plans, plan);
+       }
 
        /*
         * Sort clauses into the best execution order, although custom-scan
@@ -2045,23 +3509,40 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path,
         * Invoke custom plan provider to create the Plan node represented by the
         * CustomPath.
         */
-       plan = best_path->methods->PlanCustomPath(root, rel, best_path, tlist,
-                                                                                         scan_clauses);
+       cplan = castNode(CustomScan,
+                                        best_path->methods->PlanCustomPath(root,
+                                                                                                               rel,
+                                                                                                               best_path,
+                                                                                                               tlist,
+                                                                                                               scan_clauses,
+                                                                                                               custom_plans));
 
        /*
-        * NOTE: unlike create_foreignscan_plan(), it is the responsibility of the
-        * custom plan provider to replace outer-relation variables with nestloop
-        * params, because we cannot know what expression trees may be held in
-        * private fields.
+        * Copy cost data from Path to Plan; no need to make custom-plan providers
+        * do this
         */
+       copy_generic_path_info(&cplan->scan.plan, &best_path->path);
+
+       /* Likewise, copy the relids that are represented by this custom scan */
+       cplan->custom_relids = best_path->path.parent->relids;
 
        /*
-        * Copy cost data from Path to Plan; no need to make custom-plan providers
-        * do this
+        * Replace any outer-relation variables with nestloop params in the qual
+        * and custom_exprs expressions.  We do this last so that the custom-plan
+        * provider doesn't have to be involved.  (Note that parts of custom_exprs
+        * could have come from join clauses, so doing this beforehand on the
+        * scan_clauses wouldn't work.)  We assume custom_scan_tlist contains no
+        * such variables.
         */
-       copy_path_costsize(plan, &best_path->path);
+       if (best_path->path.param_info)
+       {
+               cplan->scan.plan.qual = (List *)
+                       replace_nestloop_params(root, (Node *) cplan->scan.plan.qual);
+               cplan->custom_exprs = (List *)
+                       replace_nestloop_params(root, (Node *) cplan->custom_exprs);
+       }
 
-       return plan;
+       return cplan;
 }
 
 
@@ -2073,21 +3554,35 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path,
 
 static NestLoop *
 create_nestloop_plan(PlannerInfo *root,
-                                        NestPath *best_path,
-                                        Plan *outer_plan,
-                                        Plan *inner_plan)
+                                        NestPath *best_path)
 {
        NestLoop   *join_plan;
+       Plan       *outer_plan;
+       Plan       *inner_plan;
        List       *tlist = build_path_tlist(root, &best_path->path);
        List       *joinrestrictclauses = best_path->joinrestrictinfo;
        List       *joinclauses;
        List       *otherclauses;
        Relids          outerrelids;
        List       *nestParams;
+       Relids          saveOuterRels = root->curOuterRels;
        ListCell   *cell;
        ListCell   *prev;
        ListCell   *next;
 
+       /* NestLoop can project, so no need to be picky about child tlists */
+       outer_plan = create_plan_recurse(root, best_path->outerjoinpath, 0);
+
+       /* For a nestloop, include outer relids in curOuterRels for inner side */
+       root->curOuterRels = bms_union(root->curOuterRels,
+                                                                  best_path->outerjoinpath->parent->relids);
+
+       inner_plan = create_plan_recurse(root, best_path->innerjoinpath, 0);
+
+       /* Restore curOuterRels */
+       bms_free(root->curOuterRels);
+       root->curOuterRels = saveOuterRels;
+
        /* Sort join qual clauses into best execution order */
        joinrestrictclauses = order_qual_clauses(root, joinrestrictclauses);
 
@@ -2157,17 +3652,18 @@ create_nestloop_plan(PlannerInfo *root,
                                                          inner_plan,
                                                          best_path->jointype);
 
-       copy_path_costsize(&join_plan->join.plan, &best_path->path);
+       copy_generic_path_info(&join_plan->join.plan, &best_path->path);
 
        return join_plan;
 }
 
 static MergeJoin *
 create_mergejoin_plan(PlannerInfo *root,
-                                         MergePath *best_path,
-                                         Plan *outer_plan,
-                                         Plan *inner_plan)
+                                         MergePath *best_path)
 {
+       MergeJoin  *join_plan;
+       Plan       *outer_plan;
+       Plan       *inner_plan;
        List       *tlist = build_path_tlist(root, &best_path->jpath.path);
        List       *joinclauses;
        List       *otherclauses;
@@ -2179,12 +3675,23 @@ create_mergejoin_plan(PlannerInfo *root,
        Oid                *mergecollations;
        int                *mergestrategies;
        bool       *mergenullsfirst;
-       MergeJoin  *join_plan;
        int                     i;
        ListCell   *lc;
        ListCell   *lop;
        ListCell   *lip;
 
+       /*
+        * MergeJoin can project, so we don't have to demand exact tlists from the
+        * inputs.  However, if we're intending to sort an input's result, it's
+        * best to request a small tlist so we aren't sorting more data than
+        * necessary.
+        */
+       outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath,
+                                        (best_path->outersortkeys != NIL) ? CP_SMALL_TLIST : 0);
+
+       inner_plan = create_plan_recurse(root, best_path->jpath.innerjoinpath,
+                                        (best_path->innersortkeys != NIL) ? CP_SMALL_TLIST : 0);
+
        /* Sort join qual clauses into best execution order */
        /* NB: do NOT reorder the mergeclauses */
        joinclauses = order_qual_clauses(root, best_path->jpath.joinrestrictinfo);
@@ -2232,16 +3739,14 @@ create_mergejoin_plan(PlannerInfo *root,
 
        /*
         * Create explicit sort nodes for the outer and inner paths if necessary.
-        * Make sure there are no excess columns in the inputs if sorting.
         */
        if (best_path->outersortkeys)
        {
-               disuse_physical_tlist(root, outer_plan, best_path->jpath.outerjoinpath);
-               outer_plan = (Plan *)
-                       make_sort_from_pathkeys(root,
-                                                                       outer_plan,
-                                                                       best_path->outersortkeys,
-                                                                       -1.0);
+               Sort       *sort = make_sort_from_pathkeys(outer_plan,
+                                                                                                  best_path->outersortkeys);
+
+               label_sort_with_costsize(root, sort, -1.0);
+               outer_plan = (Plan *) sort;
                outerpathkeys = best_path->outersortkeys;
        }
        else
@@ -2249,12 +3754,11 @@ create_mergejoin_plan(PlannerInfo *root,
 
        if (best_path->innersortkeys)
        {
-               disuse_physical_tlist(root, inner_plan, best_path->jpath.innerjoinpath);
-               inner_plan = (Plan *)
-                       make_sort_from_pathkeys(root,
-                                                                       inner_plan,
-                                                                       best_path->innersortkeys,
-                                                                       -1.0);
+               Sort       *sort = make_sort_from_pathkeys(inner_plan,
+                                                                                                  best_path->innersortkeys);
+
+               label_sort_with_costsize(root, sort, -1.0);
+               inner_plan = (Plan *) sort;
                innerpathkeys = best_path->innersortkeys;
        }
        else
@@ -2297,7 +3801,7 @@ create_mergejoin_plan(PlannerInfo *root,
        i = 0;
        foreach(lc, best_path->path_mergeclauses)
        {
-               RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+               RestrictInfo *rinfo = castNode(RestrictInfo, lfirst(lc));
                EquivalenceClass *oeclass;
                EquivalenceClass *ieclass;
                PathKey    *opathkey;
@@ -2307,7 +3811,6 @@ create_mergejoin_plan(PlannerInfo *root,
                ListCell   *l2;
 
                /* fetch outer/inner eclass from mergeclause */
-               Assert(IsA(rinfo, RestrictInfo));
                if (rinfo->outer_is_left)
                {
                        oeclass = rinfo->left_ec;
@@ -2452,17 +3955,19 @@ create_mergejoin_plan(PlannerInfo *root,
                                                           best_path->jpath.jointype);
 
        /* Costs of sort and material steps are included in path cost already */
-       copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
+       copy_generic_path_info(&join_plan->join.plan, &best_path->jpath.path);
 
        return join_plan;
 }
 
 static HashJoin *
 create_hashjoin_plan(PlannerInfo *root,
-                                        HashPath *best_path,
-                                        Plan *outer_plan,
-                                        Plan *inner_plan)
+                                        HashPath *best_path)
 {
+       HashJoin   *join_plan;
+       Hash       *hash_plan;
+       Plan       *outer_plan;
+       Plan       *inner_plan;
        List       *tlist = build_path_tlist(root, &best_path->jpath.path);
        List       *joinclauses;
        List       *otherclauses;
@@ -2472,8 +3977,19 @@ create_hashjoin_plan(PlannerInfo *root,
        bool            skewInherit = false;
        Oid                     skewColType = InvalidOid;
        int32           skewColTypmod = -1;
-       HashJoin   *join_plan;
-       Hash       *hash_plan;
+
+       /*
+        * HashJoin can project, so we don't have to demand exact tlists from the
+        * inputs.  However, it's best to request a small tlist from the inner
+        * side, so that we aren't storing more data than necessary.  Likewise, if
+        * we anticipate batching, request a small tlist from the outer side so
+        * that we don't put extra data in the outer batch files.
+        */
+       outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath,
+                                                 (best_path->num_batches > 1) ? CP_SMALL_TLIST : 0);
+
+       inner_plan = create_plan_recurse(root, best_path->jpath.innerjoinpath,
+                                                                        CP_SMALL_TLIST);
 
        /* Sort join qual clauses into best execution order */
        joinclauses = order_qual_clauses(root, best_path->jpath.joinrestrictinfo);
@@ -2519,13 +4035,6 @@ create_hashjoin_plan(PlannerInfo *root,
        hashclauses = get_switched_clauses(best_path->path_hashclauses,
                                                         best_path->jpath.outerjoinpath->parent->relids);
 
-       /* We don't want any excess columns in the hashed tuples */
-       disuse_physical_tlist(root, inner_plan, best_path->jpath.innerjoinpath);
-
-       /* If we expect batching, suppress excess columns in outer tuples too */
-       if (best_path->num_batches > 1)
-               disuse_physical_tlist(root, outer_plan, best_path->jpath.outerjoinpath);
-
        /*
         * If there is a single join clause and we can identify the outer variable
         * as a simple column reference, supply its identity for possible use in
@@ -2569,6 +4078,14 @@ create_hashjoin_plan(PlannerInfo *root,
                                                  skewInherit,
                                                  skewColType,
                                                  skewColTypmod);
+
+       /*
+        * Set Hash node's startup & total costs equal to total cost of input
+        * plan; this only affects EXPLAIN display not decisions.
+        */
+       copy_plan_costsize(&hash_plan->plan, inner_plan);
+       hash_plan->plan.startup_cost = hash_plan->plan.total_cost;
+
        join_plan = make_hashjoin(tlist,
                                                          joinclauses,
                                                          otherclauses,
@@ -2577,7 +4094,7 @@ create_hashjoin_plan(PlannerInfo *root,
                                                          (Plan *) hash_plan,
                                                          best_path->jpath.jointype);
 
-       copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
+       copy_generic_path_info(&join_plan->join.plan, &best_path->jpath.path);
 
        return join_plan;
 }
@@ -2598,7 +4115,7 @@ create_hashjoin_plan(PlannerInfo *root,
  * root->curOuterRels are replaced by Params, and entries are added to
  * root->curOuterParams if not already present.
  */
-Node *
+static Node *
 replace_nestloop_params(PlannerInfo *root, Node *expr)
 {
        /* No setup needed for tree walk, so away we go */
@@ -2828,12 +4345,10 @@ fix_indexqual_references(PlannerInfo *root, IndexPath *index_path)
 
        forboth(lcc, index_path->indexquals, lci, index_path->indexqualcols)
        {
-               RestrictInfo *rinfo = (RestrictInfo *) lfirst(lcc);
+               RestrictInfo *rinfo = castNode(RestrictInfo, lfirst(lcc));
                int                     indexcol = lfirst_int(lci);
                Node       *clause;
 
-               Assert(IsA(rinfo, RestrictInfo));
-
                /*
                 * Replace any outer-relation variables with nestloop params.
                 *
@@ -3067,7 +4582,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol)
                }
        }
 
-       /* Ooops... */
+       /* Oops... */
        elog(ERROR, "index key does not match expected index column");
        return NULL;                            /* keep compiler quiet */
 }
@@ -3131,21 +4646,32 @@ get_switched_clauses(List *clauses, Relids outerrelids)
  *             plan node, sort the list into the order we want to check the quals
  *             in at runtime.
  *
+ * When security barrier quals are used in the query, we may have quals with
+ * different security levels in the list.  Quals of lower security_level
+ * must go before quals of higher security_level, except that we can grant
+ * exceptions to move up quals that are leakproof.  When security level
+ * doesn't force the decision, we prefer to order clauses by estimated
+ * execution cost, cheapest first.
+ *
  * Ideally the order should be driven by a combination of execution cost and
  * selectivity, but it's not immediately clear how to account for both,
  * and given the uncertainty of the estimates the reliability of the decisions
- * would be doubtful anyway.  So we just order by estimated per-tuple cost,
- * being careful not to change the order when (as is often the case) the
- * estimates are identical.
+ * would be doubtful anyway.  So we just order by security level then
+ * estimated per-tuple cost, being careful not to change the order when
+ * (as is often the case) the estimates are identical.
  *
  * Although this will work on either bare clauses or RestrictInfos, it's
  * much faster to apply it to RestrictInfos, since it can re-use cost
- * information that is cached in RestrictInfos.
+ * information that is cached in RestrictInfos.  XXX in the bare-clause
+ * case, we are also not able to apply security considerations.  That is
+ * all right for the moment, because the bare-clause case doesn't occur
+ * anywhere that barrier quals could be present, but it would be better to
+ * get rid of it.
  *
  * Note: some callers pass lists that contain entries that will later be
  * removed; this is the easiest way to let this routine see RestrictInfos
- * instead of bare clauses.  It's OK because we only sort by cost, but
- * a cost/selectivity combination would likely do the wrong thing.
+ * instead of bare clauses.  This is another reason why trying to consider
+ * selectivity in the ordering would likely do the wrong thing.
  */
 static List *
 order_qual_clauses(PlannerInfo *root, List *clauses)
@@ -3154,6 +4680,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
        {
                Node       *clause;
                Cost            cost;
+               Index           security_level;
        } QualItem;
        int                     nitems = list_length(clauses);
        QualItem   *items;
@@ -3179,6 +4706,27 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
                cost_qual_eval_node(&qcost, clause, root);
                items[i].clause = clause;
                items[i].cost = qcost.per_tuple;
+               if (IsA(clause, RestrictInfo))
+               {
+                       RestrictInfo *rinfo = (RestrictInfo *) clause;
+
+                       /*
+                        * If a clause is leakproof, it doesn't have to be constrained by
+                        * its nominal security level.  If it's also reasonably cheap
+                        * (here defined as 10X cpu_operator_cost), pretend it has
+                        * security_level 0, which will allow it to go in front of
+                        * more-expensive quals of lower security levels.  Of course, that
+                        * will also force it to go in front of cheaper quals of its own
+                        * security level, which is not so great, but we can alleviate
+                        * that risk by applying the cost limit cutoff.
+                        */
+                       if (rinfo->leakproof && items[i].cost < 10 * cpu_operator_cost)
+                               items[i].security_level = 0;
+                       else
+                               items[i].security_level = rinfo->security_level;
+               }
+               else
+                       items[i].security_level = 0;
                i++;
        }
 
@@ -3195,9 +4743,13 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
                /* insert newitem into the already-sorted subarray */
                for (j = i; j > 0; j--)
                {
-                       if (newitem.cost >= items[j - 1].cost)
+                       QualItem   *olditem = &items[j - 1];
+
+                       if (newitem.security_level > olditem->security_level ||
+                               (newitem.security_level == olditem->security_level &&
+                                newitem.cost >= olditem->cost))
                                break;
-                       items[j] = items[j - 1];
+                       items[j] = *olditem;
                }
                items[j] = newitem;
        }
@@ -3213,24 +4765,16 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
 /*
  * Copy cost and size info from a Path node to the Plan node created from it.
  * The executor usually won't use this info, but it's needed by EXPLAIN.
+ * Also copy the parallel-aware flag, which the executor *will* use.
  */
 static void
-copy_path_costsize(Plan *dest, Path *src)
+copy_generic_path_info(Plan *dest, Path *src)
 {
-       if (src)
-       {
-               dest->startup_cost = src->startup_cost;
-               dest->total_cost = src->total_cost;
-               dest->plan_rows = src->rows;
-               dest->plan_width = src->parent->width;
-       }
-       else
-       {
-               dest->startup_cost = 0;
-               dest->total_cost = 0;
-               dest->plan_rows = 0;
-               dest->plan_width = 0;
-       }
+       dest->startup_cost = src->startup_cost;
+       dest->total_cost = src->total_cost;
+       dest->plan_rows = src->rows;
+       dest->plan_width = src->pathtarget->width;
+       dest->parallel_aware = src->parallel_aware;
 }
 
 /*
@@ -3240,29 +4784,73 @@ copy_path_costsize(Plan *dest, Path *src)
 static void
 copy_plan_costsize(Plan *dest, Plan *src)
 {
-       if (src)
-       {
-               dest->startup_cost = src->startup_cost;
-               dest->total_cost = src->total_cost;
-               dest->plan_rows = src->plan_rows;
-               dest->plan_width = src->plan_width;
-       }
-       else
-       {
-               dest->startup_cost = 0;
-               dest->total_cost = 0;
-               dest->plan_rows = 0;
-               dest->plan_width = 0;
-       }
+       dest->startup_cost = src->startup_cost;
+       dest->total_cost = src->total_cost;
+       dest->plan_rows = src->plan_rows;
+       dest->plan_width = src->plan_width;
+       /* Assume the inserted node is not parallel-aware. */
+       dest->parallel_aware = false;
+}
+
+/*
+ * Some places in this file build Sort nodes that don't have a directly
+ * corresponding Path node.  The cost of the sort is, or should have been,
+ * included in the cost of the Path node we're working from, but since it's
+ * not split out, we have to re-figure it using cost_sort().  This is just
+ * to label the Sort node nicely for EXPLAIN.
+ *
+ * limit_tuples is as for cost_sort (in particular, pass -1 if no limit)
+ */
+static void
+label_sort_with_costsize(PlannerInfo *root, Sort *plan, double limit_tuples)
+{
+       Plan       *lefttree = plan->plan.lefttree;
+       Path            sort_path;              /* dummy for result of cost_sort */
+
+       cost_sort(&sort_path, root, NIL,
+                         lefttree->total_cost,
+                         lefttree->plan_rows,
+                         lefttree->plan_width,
+                         0.0,
+                         work_mem,
+                         limit_tuples);
+       plan->plan.startup_cost = sort_path.startup_cost;
+       plan->plan.total_cost = sort_path.total_cost;
+       plan->plan.plan_rows = lefttree->plan_rows;
+       plan->plan.plan_width = lefttree->plan_width;
+       plan->plan.parallel_aware = false;
 }
 
+/*
+ * bitmap_subplan_mark_shared
+ *   Set isshared flag in bitmap subplan so that it will be created in
+ *      shared memory.
+ */
+static void
+bitmap_subplan_mark_shared(Plan *plan)
+{
+       if (IsA(plan, BitmapAnd))
+               bitmap_subplan_mark_shared(
+                                                               linitial(((BitmapAnd *) plan)->bitmapplans));
+       else if (IsA(plan, BitmapOr))
+               ((BitmapOr *) plan)->isshared = true;
+       else if (IsA(plan, BitmapIndexScan))
+               ((BitmapIndexScan *) plan)->isshared = true;
+       else
+               elog(ERROR, "unrecognized node type: %d", nodeTag(plan));
+}
 
 /*****************************************************************************
  *
  *     PLAN NODE BUILDING ROUTINES
  *
- * Some of these are exported because they are called to build plan nodes
- * in contexts where we're not deriving the plan node from a path node.
+ * In general, these functions are not passed the original Path and therefore
+ * leave it to the caller to fill in the cost/width fields from the Path,
+ * typically by calling copy_generic_path_info().  This convention is
+ * somewhat historical, but it does support a few places above where we build
+ * a plan node without having an exactly corresponding Path node.  Under no
+ * circumstances should one of these functions do its own cost calculations,
+ * as that would be redundant with calculations done while building Paths.
  *
  *****************************************************************************/
 
@@ -3274,7 +4862,6 @@ make_seqscan(List *qptlist,
        SeqScan    *node = makeNode(SeqScan);
        Plan       *plan = &node->plan;
 
-       /* cost should be inserted by caller */
        plan->targetlist = qptlist;
        plan->qual = qpqual;
        plan->lefttree = NULL;
@@ -3284,6 +4871,25 @@ make_seqscan(List *qptlist,
        return node;
 }
 
+static SampleScan *
+make_samplescan(List *qptlist,
+                               List *qpqual,
+                               Index scanrelid,
+                               TableSampleClause *tsc)
+{
+       SampleScan *node = makeNode(SampleScan);
+       Plan       *plan = &node->scan.plan;
+
+       plan->targetlist = qptlist;
+       plan->qual = qpqual;
+       plan->lefttree = NULL;
+       plan->righttree = NULL;
+       node->scan.scanrelid = scanrelid;
+       node->tablesample = tsc;
+
+       return node;
+}
+
 static IndexScan *
 make_indexscan(List *qptlist,
                           List *qpqual,
@@ -3293,12 +4899,12 @@ make_indexscan(List *qptlist,
                           List *indexqualorig,
                           List *indexorderby,
                           List *indexorderbyorig,
+                          List *indexorderbyops,
                           ScanDirection indexscandir)
 {
        IndexScan  *node = makeNode(IndexScan);
        Plan       *plan = &node->scan.plan;
 
-       /* cost should be inserted by caller */
        plan->targetlist = qptlist;
        plan->qual = qpqual;
        plan->lefttree = NULL;
@@ -3309,6 +4915,7 @@ make_indexscan(List *qptlist,
        node->indexqualorig = indexqualorig;
        node->indexorderby = indexorderby;
        node->indexorderbyorig = indexorderbyorig;
+       node->indexorderbyops = indexorderbyops;
        node->indexorderdir = indexscandir;
 
        return node;
@@ -3327,7 +4934,6 @@ make_indexonlyscan(List *qptlist,
        IndexOnlyScan *node = makeNode(IndexOnlyScan);
        Plan       *plan = &node->scan.plan;
 
-       /* cost should be inserted by caller */
        plan->targetlist = qptlist;
        plan->qual = qpqual;
        plan->lefttree = NULL;
@@ -3351,7 +4957,6 @@ make_bitmap_indexscan(Index scanrelid,
        BitmapIndexScan *node = makeNode(BitmapIndexScan);
        Plan       *plan = &node->scan.plan;
 
-       /* cost should be inserted by caller */
        plan->targetlist = NIL;         /* not used */
        plan->qual = NIL;                       /* not used */
        plan->lefttree = NULL;
@@ -3374,7 +4979,6 @@ make_bitmap_heapscan(List *qptlist,
        BitmapHeapScan *node = makeNode(BitmapHeapScan);
        Plan       *plan = &node->scan.plan;
 
-       /* cost should be inserted by caller */
        plan->targetlist = qptlist;
        plan->qual = qpqual;
        plan->lefttree = lefttree;
@@ -3394,7 +4998,6 @@ make_tidscan(List *qptlist,
        TidScan    *node = makeNode(TidScan);
        Plan       *plan = &node->scan.plan;
 
-       /* cost should be inserted by caller */
        plan->targetlist = qptlist;
        plan->qual = qpqual;
        plan->lefttree = NULL;
@@ -3405,7 +5008,7 @@ make_tidscan(List *qptlist,
        return node;
 }
 
-SubqueryScan *
+static SubqueryScan *
 make_subqueryscan(List *qptlist,
                                  List *qpqual,
                                  Index scanrelid,
@@ -3414,14 +5017,6 @@ make_subqueryscan(List *qptlist,
        SubqueryScan *node = makeNode(SubqueryScan);
        Plan       *plan = &node->scan.plan;
 
-       /*
-        * Cost is figured here for the convenience of prepunion.c.  Note this is
-        * only correct for the case where qpqual is empty; otherwise caller
-        * should overwrite cost with a better estimate.
-        */
-       copy_plan_costsize(plan, subplan);
-       plan->total_cost += cpu_tuple_cost * subplan->plan_rows;
-
        plan->targetlist = qptlist;
        plan->qual = qpqual;
        plan->lefttree = NULL;
@@ -3439,17 +5034,35 @@ make_functionscan(List *qptlist,
                                  List *functions,
                                  bool funcordinality)
 {
-       FunctionScan *node = makeNode(FunctionScan);
+       FunctionScan *node = makeNode(FunctionScan);
+       Plan       *plan = &node->scan.plan;
+
+       plan->targetlist = qptlist;
+       plan->qual = qpqual;
+       plan->lefttree = NULL;
+       plan->righttree = NULL;
+       node->scan.scanrelid = scanrelid;
+       node->functions = functions;
+       node->funcordinality = funcordinality;
+
+       return node;
+}
+
+static TableFuncScan *
+make_tablefuncscan(List *qptlist,
+                                  List *qpqual,
+                                  Index scanrelid,
+                                  TableFunc *tablefunc)
+{
+       TableFuncScan *node = makeNode(TableFuncScan);
        Plan       *plan = &node->scan.plan;
 
-       /* cost should be inserted by caller */
        plan->targetlist = qptlist;
        plan->qual = qpqual;
        plan->lefttree = NULL;
        plan->righttree = NULL;
        node->scan.scanrelid = scanrelid;
-       node->functions = functions;
-       node->funcordinality = funcordinality;
+       node->tablefunc = tablefunc;
 
        return node;
 }
@@ -3463,7 +5076,6 @@ make_valuesscan(List *qptlist,
        ValuesScan *node = makeNode(ValuesScan);
        Plan       *plan = &node->scan.plan;
 
-       /* cost should be inserted by caller */
        plan->targetlist = qptlist;
        plan->qual = qpqual;
        plan->lefttree = NULL;
@@ -3484,7 +5096,6 @@ make_ctescan(List *qptlist,
        CteScan    *node = makeNode(CteScan);
        Plan       *plan = &node->scan.plan;
 
-       /* cost should be inserted by caller */
        plan->targetlist = qptlist;
        plan->qual = qpqual;
        plan->lefttree = NULL;
@@ -3505,7 +5116,6 @@ make_worktablescan(List *qptlist,
        WorkTableScan *node = makeNode(WorkTableScan);
        Plan       *plan = &node->scan.plan;
 
-       /* cost should be inserted by caller */
        plan->targetlist = qptlist;
        plan->qual = qpqual;
        plan->lefttree = NULL;
@@ -3521,7 +5131,10 @@ make_foreignscan(List *qptlist,
                                 List *qpqual,
                                 Index scanrelid,
                                 List *fdw_exprs,
-                                List *fdw_private)
+                                List *fdw_private,
+                                List *fdw_scan_tlist,
+                                List *fdw_recheck_quals,
+                                Plan *outer_plan)
 {
        ForeignScan *node = makeNode(ForeignScan);
        Plan       *plan = &node->scan.plan;
@@ -3529,54 +5142,29 @@ make_foreignscan(List *qptlist,
        /* cost will be filled in by create_foreignscan_plan */
        plan->targetlist = qptlist;
        plan->qual = qpqual;
-       plan->lefttree = NULL;
+       plan->lefttree = outer_plan;
        plan->righttree = NULL;
        node->scan.scanrelid = scanrelid;
+       node->operation = CMD_SELECT;
+       /* fs_server will be filled in by create_foreignscan_plan */
+       node->fs_server = InvalidOid;
        node->fdw_exprs = fdw_exprs;
        node->fdw_private = fdw_private;
+       node->fdw_scan_tlist = fdw_scan_tlist;
+       node->fdw_recheck_quals = fdw_recheck_quals;
+       /* fs_relids will be filled in by create_foreignscan_plan */
+       node->fs_relids = NULL;
        /* fsSystemCol will be filled in by create_foreignscan_plan */
        node->fsSystemCol = false;
 
        return node;
 }
 
-Append *
+static Append *
 make_append(List *appendplans, List *tlist)
 {
        Append     *node = makeNode(Append);
        Plan       *plan = &node->plan;
-       double          total_size;
-       ListCell   *subnode;
-
-       /*
-        * Compute cost as sum of subplan costs.  We charge nothing extra for the
-        * Append itself, which perhaps is too optimistic, but since it doesn't do
-        * any selection or projection, it is a pretty cheap node.
-        *
-        * If you change this, see also create_append_path().  Also, the size
-        * calculations should match set_append_rel_pathlist().  It'd be better
-        * not to duplicate all this logic, but some callers of this function
-        * aren't working from an appendrel or AppendPath, so there's noplace to
-        * copy the data from.
-        */
-       plan->startup_cost = 0;
-       plan->total_cost = 0;
-       plan->plan_rows = 0;
-       total_size = 0;
-       foreach(subnode, appendplans)
-       {
-               Plan       *subplan = (Plan *) lfirst(subnode);
-
-               if (subnode == list_head(appendplans))  /* first node? */
-                       plan->startup_cost = subplan->startup_cost;
-               plan->total_cost += subplan->total_cost;
-               plan->plan_rows += subplan->plan_rows;
-               total_size += subplan->plan_width * subplan->plan_rows;
-       }
-       if (plan->plan_rows > 0)
-               plan->plan_width = rint(total_size / plan->plan_rows);
-       else
-               plan->plan_width = 0;
 
        plan->targetlist = tlist;
        plan->qual = NIL;
@@ -3587,7 +5175,7 @@ make_append(List *appendplans, List *tlist)
        return node;
 }
 
-RecursiveUnion *
+static RecursiveUnion *
 make_recursive_union(List *tlist,
                                         Plan *lefttree,
                                         Plan *righttree,
@@ -3599,8 +5187,6 @@ make_recursive_union(List *tlist,
        Plan       *plan = &node->plan;
        int                     numCols = list_length(distinctList);
 
-       cost_recursive_union(plan, lefttree, righttree);
-
        plan->targetlist = tlist;
        plan->qual = NIL;
        plan->lefttree = lefttree;
@@ -3647,7 +5233,6 @@ make_bitmap_and(List *bitmapplans)
        BitmapAnd  *node = makeNode(BitmapAnd);
        Plan       *plan = &node->plan;
 
-       /* cost should be inserted by caller */
        plan->targetlist = NIL;
        plan->qual = NIL;
        plan->lefttree = NULL;
@@ -3663,7 +5248,6 @@ make_bitmap_or(List *bitmapplans)
        BitmapOr   *node = makeNode(BitmapOr);
        Plan       *plan = &node->plan;
 
-       /* cost should be inserted by caller */
        plan->targetlist = NIL;
        plan->qual = NIL;
        plan->lefttree = NULL;
@@ -3685,7 +5269,6 @@ make_nestloop(List *tlist,
        NestLoop   *node = makeNode(NestLoop);
        Plan       *plan = &node->join.plan;
 
-       /* cost should be inserted by caller */
        plan->targetlist = tlist;
        plan->qual = otherclauses;
        plan->lefttree = lefttree;
@@ -3709,7 +5292,6 @@ make_hashjoin(List *tlist,
        HashJoin   *node = makeNode(HashJoin);
        Plan       *plan = &node->join.plan;
 
-       /* cost should be inserted by caller */
        plan->targetlist = tlist;
        plan->qual = otherclauses;
        plan->lefttree = lefttree;
@@ -3732,13 +5314,6 @@ make_hash(Plan *lefttree,
        Hash       *node = makeNode(Hash);
        Plan       *plan = &node->plan;
 
-       copy_plan_costsize(plan, lefttree);
-
-       /*
-        * For plausibility, make startup & total costs equal total cost of input
-        * plan; this only affects EXPLAIN display not decisions.
-        */
-       plan->startup_cost = plan->total_cost;
        plan->targetlist = lefttree->targetlist;
        plan->qual = NIL;
        plan->lefttree = lefttree;
@@ -3769,7 +5344,6 @@ make_mergejoin(List *tlist,
        MergeJoin  *node = makeNode(MergeJoin);
        Plan       *plan = &node->join.plan;
 
-       /* cost should be inserted by caller */
        plan->targetlist = tlist;
        plan->qual = otherclauses;
        plan->lefttree = lefttree;
@@ -3790,28 +5364,15 @@ make_mergejoin(List *tlist,
  *
  * Caller must have built the sortColIdx, sortOperators, collations, and
  * nullsFirst arrays already.
- * limit_tuples is as for cost_sort (in particular, pass -1 if no limit)
  */
 static Sort *
-make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
+make_sort(Plan *lefttree, int numCols,
                  AttrNumber *sortColIdx, Oid *sortOperators,
-                 Oid *collations, bool *nullsFirst,
-                 double limit_tuples)
+                 Oid *collations, bool *nullsFirst)
 {
        Sort       *node = makeNode(Sort);
        Plan       *plan = &node->plan;
-       Path            sort_path;              /* dummy for result of cost_sort */
 
-       copy_plan_costsize(plan, lefttree); /* only care about copying size */
-       cost_sort(&sort_path, root, NIL,
-                         lefttree->total_cost,
-                         lefttree->plan_rows,
-                         lefttree->plan_width,
-                         0.0,
-                         work_mem,
-                         limit_tuples);
-       plan->startup_cost = sort_path.startup_cost;
-       plan->total_cost = sort_path.total_cost;
        plan->targetlist = lefttree->targetlist;
        plan->qual = NIL;
        plan->lefttree = lefttree;
@@ -3829,9 +5390,9 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
  * prepare_sort_from_pathkeys
  *       Prepare to sort according to given pathkeys
  *
- * This is used to set up for both Sort and MergeAppend nodes.  It calculates
- * the executor's representation of the sort key information, and adjusts the
- * plan targetlist if needed to add resjunk sort columns.
+ * This is used to set up for Sort, MergeAppend, and Gather Merge nodes.  It
+ * calculates the executor's representation of the sort key information, and
+ * adjusts the plan targetlist if needed to add resjunk sort columns.
  *
  * Input parameters:
  *       'lefttree' is the plan node which yields input tuples
@@ -3855,7 +5416,7 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
  *
  * If the pathkeys include expressions that aren't simple Vars, we will
  * usually need to add resjunk items to the input plan's targetlist to
- * compute these expressions, since the Sort/MergeAppend node itself won't
+ * compute these expressions, since a Sort or MergeAppend node itself won't
  * do any such calculations.  If the input plan type isn't one that can do
  * projections, this means adding a Result node just to do the projection.
  * However, the caller can pass adjust_tlist_in_place = TRUE to force the
@@ -3866,7 +5427,7 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
  * or a Result stacked atop lefttree).
  */
 static Plan *
-prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
+prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
                                                   Relids relids,
                                                   const AttrNumber *reqColIdx,
                                                   bool adjust_tlist_in_place,
@@ -3982,7 +5543,8 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
                         * that we treat Aggrefs as if they were variables; this is
                         * necessary when attempting to sort the output from an Agg node
                         * for use in a WindowFunc (since grouping_planner will have
-                        * treated the Aggrefs as variables, too).
+                        * treated the Aggrefs as variables, too).  Likewise, if we find a
+                        * WindowFunc in a sort expression, treat it as a variable.
                         */
                        Expr       *sortexpr = NULL;
 
@@ -4010,7 +5572,8 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 
                                sortexpr = em->em_expr;
                                exprvars = pull_var_clause((Node *) sortexpr,
-                                                                                  PVC_INCLUDE_AGGREGATES,
+                                                                                  PVC_INCLUDE_AGGREGATES |
+                                                                                  PVC_INCLUDE_WINDOWFUNCS |
                                                                                   PVC_INCLUDE_PLACEHOLDERS);
                                foreach(k, exprvars)
                                {
@@ -4035,8 +5598,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
                        {
                                /* copy needed so we don't modify input's tlist below */
                                tlist = copyObject(tlist);
-                               lefttree = (Plan *) make_result(root, tlist, NULL,
-                                                                                               lefttree);
+                               lefttree = inject_projection_plan(lefttree, tlist);
                        }
 
                        /* Don't bother testing is_projection_capable_plan again */
@@ -4140,12 +5702,9 @@ find_ec_member_for_tle(EquivalenceClass *ec,
  *
  *       'lefttree' is the node which yields input tuples
  *       'pathkeys' is the list of pathkeys by which the result is to be sorted
- *       'limit_tuples' is the bound on the number of output tuples;
- *                             -1 if no bound
  */
-Sort *
-make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
-                                               double limit_tuples)
+static Sort *
+make_sort_from_pathkeys(Plan *lefttree, List *pathkeys)
 {
        int                     numsortkeys;
        AttrNumber *sortColIdx;
@@ -4154,7 +5713,7 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
        bool       *nullsFirst;
 
        /* Compute sort column info, and adjust lefttree as needed */
-       lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
+       lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
                                                                                  NULL,
                                                                                  NULL,
                                                                                  false,
@@ -4165,9 +5724,9 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
                                                                                  &nullsFirst);
 
        /* Now build the Sort node */
-       return make_sort(root, lefttree, numsortkeys,
-                                        sortColIdx, sortOperators, collations,
-                                        nullsFirst, limit_tuples);
+       return make_sort(lefttree, numsortkeys,
+                                        sortColIdx, sortOperators,
+                                        collations, nullsFirst);
 }
 
 /*
@@ -4178,7 +5737,7 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
  *       'lefttree' is the node which yields input tuples
  */
 Sort *
-make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree)
+make_sort_from_sortclauses(List *sortcls, Plan *lefttree)
 {
        List       *sub_tlist = lefttree->targetlist;
        ListCell   *l;
@@ -4208,9 +5767,9 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree)
                numsortkeys++;
        }
 
-       return make_sort(root, lefttree, numsortkeys,
-                                        sortColIdx, sortOperators, collations,
-                                        nullsFirst, -1.0);
+       return make_sort(lefttree, numsortkeys,
+                                        sortColIdx, sortOperators,
+                                        collations, nullsFirst);
 }
 
 /*
@@ -4226,9 +5785,8 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree)
  * appropriate to the grouping node.  So, only the sort ordering info
  * is used from the SortGroupClause entries.
  */
-Sort *
-make_sort_from_groupcols(PlannerInfo *root,
-                                                List *groupcls,
+static Sort *
+make_sort_from_groupcols(List *groupcls,
                                                 AttrNumber *grpColIdx,
                                                 Plan *lefttree)
 {
@@ -4254,7 +5812,7 @@ make_sort_from_groupcols(PlannerInfo *root,
                TargetEntry *tle = get_tle_by_resno(sub_tlist, grpColIdx[numsortkeys]);
 
                if (!tle)
-                       elog(ERROR, "could not retrive tle for sort-from-groupcols");
+                       elog(ERROR, "could not retrieve tle for sort-from-groupcols");
 
                sortColIdx[numsortkeys] = tle->resno;
                sortOperators[numsortkeys] = grpcl->sortop;
@@ -4263,9 +5821,9 @@ make_sort_from_groupcols(PlannerInfo *root,
                numsortkeys++;
        }
 
-       return make_sort(root, lefttree, numsortkeys,
-                                        sortColIdx, sortOperators, collations,
-                                        nullsFirst, -1.0);
+       return make_sort(lefttree, numsortkeys,
+                                        sortColIdx, sortOperators,
+                                        collations, nullsFirst);
 }
 
 static Material *
@@ -4274,7 +5832,6 @@ make_material(Plan *lefttree)
        Material   *node = makeNode(Material);
        Plan       *plan = &node->plan;
 
-       /* cost should be inserted by caller */
        plan->targetlist = lefttree->targetlist;
        plan->qual = NIL;
        plan->lefttree = lefttree;
@@ -4287,11 +5844,9 @@ make_material(Plan *lefttree)
  * materialize_finished_plan: stick a Material node atop a completed plan
  *
  * There are a couple of places where we want to attach a Material node
- * after completion of subquery_planner().  This currently requires hackery.
- * Since subquery_planner has already run SS_finalize_plan on the subplan
- * tree, we have to kluge up parameter lists for the Material node.
- * Possibly this could be fixed by postponing SS_finalize_plan processing
- * until setrefs.c is run?
+ * after completion of create_plan(), without any MaterialPath path.
+ * Those places should probably be refactored someday to do this on the
+ * Path representation, but it's not worth the trouble yet.
  */
 Plan *
 materialize_finished_plan(Plan *subplan)
@@ -4301,6 +5856,16 @@ materialize_finished_plan(Plan *subplan)
 
        matplan = (Plan *) make_material(subplan);
 
+       /*
+        * XXX horrid kluge: if there are any initPlans attached to the subplan,
+        * move them up to the Material node, which is now effectively the top
+        * plan node in its query level.  This prevents failure in
+        * SS_finalize_plan(), which see for comments.  We don't bother adjusting
+        * the subplan's cost estimate for this.
+        */
+       matplan->initPlan = subplan->initPlan;
+       subplan->initPlan = NIL;
+
        /* Set cost data */
        cost_material(&matpath,
                                  subplan->startup_cost,
@@ -4311,68 +5876,34 @@ materialize_finished_plan(Plan *subplan)
        matplan->total_cost = matpath.total_cost;
        matplan->plan_rows = subplan->plan_rows;
        matplan->plan_width = subplan->plan_width;
-
-       /* parameter kluge --- see comments above */
-       matplan->extParam = bms_copy(subplan->extParam);
-       matplan->allParam = bms_copy(subplan->allParam);
+       matplan->parallel_aware = false;
 
        return matplan;
 }
 
 Agg *
-make_agg(PlannerInfo *root, List *tlist, List *qual,
-                AggStrategy aggstrategy, const AggClauseCosts *aggcosts,
+make_agg(List *tlist, List *qual,
+                AggStrategy aggstrategy, AggSplit aggsplit,
                 int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators,
-                long numGroups,
-                Plan *lefttree)
+                List *groupingSets, List *chain,
+                double dNumGroups, Plan *lefttree)
 {
        Agg                *node = makeNode(Agg);
        Plan       *plan = &node->plan;
-       Path            agg_path;               /* dummy for result of cost_agg */
-       QualCost        qual_cost;
+       long            numGroups;
+
+       /* Reduce to long, but 'ware overflow! */
+       numGroups = (long) Min(dNumGroups, (double) LONG_MAX);
 
        node->aggstrategy = aggstrategy;
+       node->aggsplit = aggsplit;
        node->numCols = numGroupCols;
        node->grpColIdx = grpColIdx;
        node->grpOperators = grpOperators;
        node->numGroups = numGroups;
-
-       copy_plan_costsize(plan, lefttree); /* only care about copying size */
-       cost_agg(&agg_path, root,
-                        aggstrategy, aggcosts,
-                        numGroupCols, numGroups,
-                        lefttree->startup_cost,
-                        lefttree->total_cost,
-                        lefttree->plan_rows);
-       plan->startup_cost = agg_path.startup_cost;
-       plan->total_cost = agg_path.total_cost;
-
-       /*
-        * We will produce a single output tuple if not grouping, and a tuple per
-        * group otherwise.
-        */
-       if (aggstrategy == AGG_PLAIN)
-               plan->plan_rows = 1;
-       else
-               plan->plan_rows = numGroups;
-
-       /*
-        * We also need to account for the cost of evaluation of the qual (ie, the
-        * HAVING clause) and the tlist.  Note that cost_qual_eval doesn't charge
-        * anything for Aggref nodes; this is okay since they are really
-        * comparable to Vars.
-        *
-        * See notes in add_tlist_costs_to_plan about why only make_agg,
-        * make_windowagg and make_group worry about tlist eval cost.
-        */
-       if (qual)
-       {
-               cost_qual_eval(&qual_cost, qual, root);
-               plan->startup_cost += qual_cost.startup;
-               plan->total_cost += qual_cost.startup;
-               plan->total_cost += qual_cost.per_tuple * plan->plan_rows;
-       }
-       add_tlist_costs_to_plan(root, plan, tlist);
+       node->aggParams = NULL;         /* SS_finalize_plan() will fill this */
+       node->groupingSets = groupingSets;
+       node->chain = chain;
 
        plan->qual = qual;
        plan->targetlist = tlist;
@@ -4382,9 +5913,8 @@ make_agg(PlannerInfo *root, List *tlist, List *qual,
        return node;
 }
 
-WindowAgg *
-make_windowagg(PlannerInfo *root, List *tlist,
-                          List *windowFuncs, Index winref,
+static WindowAgg *
+make_windowagg(List *tlist, Index winref,
                           int partNumCols, AttrNumber *partColIdx, Oid *partOperators,
                           int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators,
                           int frameOptions, Node *startOffset, Node *endOffset,
@@ -4392,7 +5922,6 @@ make_windowagg(PlannerInfo *root, List *tlist,
 {
        WindowAgg  *node = makeNode(WindowAgg);
        Plan       *plan = &node->plan;
-       Path            windowagg_path; /* dummy for result of cost_windowagg */
 
        node->winref = winref;
        node->partNumCols = partNumCols;
@@ -4405,23 +5934,6 @@ make_windowagg(PlannerInfo *root, List *tlist,
        node->startOffset = startOffset;
        node->endOffset = endOffset;
 
-       copy_plan_costsize(plan, lefttree); /* only care about copying size */
-       cost_windowagg(&windowagg_path, root,
-                                  windowFuncs, partNumCols, ordNumCols,
-                                  lefttree->startup_cost,
-                                  lefttree->total_cost,
-                                  lefttree->plan_rows);
-       plan->startup_cost = windowagg_path.startup_cost;
-       plan->total_cost = windowagg_path.total_cost;
-
-       /*
-        * We also need to account for the cost of evaluation of the tlist.
-        *
-        * See notes in add_tlist_costs_to_plan about why only make_agg,
-        * make_windowagg and make_group worry about tlist eval cost.
-        */
-       add_tlist_costs_to_plan(root, plan, tlist);
-
        plan->targetlist = tlist;
        plan->lefttree = lefttree;
        plan->righttree = NULL;
@@ -4431,58 +5943,21 @@ make_windowagg(PlannerInfo *root, List *tlist,
        return node;
 }
 
-Group *
-make_group(PlannerInfo *root,
-                  List *tlist,
+static Group *
+make_group(List *tlist,
                   List *qual,
                   int numGroupCols,
                   AttrNumber *grpColIdx,
                   Oid *grpOperators,
-                  double numGroups,
                   Plan *lefttree)
 {
        Group      *node = makeNode(Group);
        Plan       *plan = &node->plan;
-       Path            group_path;             /* dummy for result of cost_group */
-       QualCost        qual_cost;
 
        node->numCols = numGroupCols;
        node->grpColIdx = grpColIdx;
        node->grpOperators = grpOperators;
 
-       copy_plan_costsize(plan, lefttree); /* only care about copying size */
-       cost_group(&group_path, root,
-                          numGroupCols, numGroups,
-                          lefttree->startup_cost,
-                          lefttree->total_cost,
-                          lefttree->plan_rows);
-       plan->startup_cost = group_path.startup_cost;
-       plan->total_cost = group_path.total_cost;
-
-       /* One output tuple per estimated result group */
-       plan->plan_rows = numGroups;
-
-       /*
-        * We also need to account for the cost of evaluation of the qual (ie, the
-        * HAVING clause) and the tlist.
-        *
-        * XXX this double-counts the cost of evaluation of any expressions used
-        * for grouping, since in reality those will have been evaluated at a
-        * lower plan level and will only be copied by the Group node. Worth
-        * fixing?
-        *
-        * See notes in add_tlist_costs_to_plan about why only make_agg,
-        * make_windowagg and make_group worry about tlist eval cost.
-        */
-       if (qual)
-       {
-               cost_qual_eval(&qual_cost, qual, root);
-               plan->startup_cost += qual_cost.startup;
-               plan->total_cost += qual_cost.startup;
-               plan->total_cost += qual_cost.per_tuple * plan->plan_rows;
-       }
-       add_tlist_costs_to_plan(root, plan, tlist);
-
        plan->qual = qual;
        plan->targetlist = tlist;
        plan->lefttree = lefttree;
@@ -4496,8 +5971,8 @@ make_group(PlannerInfo *root,
  * that should be considered by the Unique filter.  The input path must
  * already be sorted accordingly.
  */
-Unique *
-make_unique(Plan *lefttree, List *distinctList)
+static Unique *
+make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
 {
        Unique     *node = makeNode(Unique);
        Plan       *plan = &node->plan;
@@ -4507,21 +5982,6 @@ make_unique(Plan *lefttree, List *distinctList)
        Oid                *uniqOperators;
        ListCell   *slitem;
 
-       copy_plan_costsize(plan, lefttree);
-
-       /*
-        * Charge one cpu_operator_cost per comparison per input tuple. We assume
-        * all columns get compared at most of the tuples.  (XXX probably this is
-        * an overestimate.)
-        */
-       plan->total_cost += cpu_operator_cost * plan->plan_rows * numCols;
-
-       /*
-        * plan->plan_rows is left as a copy of the input subplan's plan_rows; ie,
-        * we assume the filter removes nothing.  The caller must alter this if he
-        * has a better idea.
-        */
-
        plan->targetlist = lefttree->targetlist;
        plan->qual = NIL;
        plan->lefttree = lefttree;
@@ -4553,15 +6013,141 @@ make_unique(Plan *lefttree, List *distinctList)
        return node;
 }
 
+/*
+ * as above, but use pathkeys to identify the sort columns and semantics
+ */
+static Unique *
+make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
+{
+       Unique     *node = makeNode(Unique);
+       Plan       *plan = &node->plan;
+       int                     keyno = 0;
+       AttrNumber *uniqColIdx;
+       Oid                *uniqOperators;
+       ListCell   *lc;
+
+       plan->targetlist = lefttree->targetlist;
+       plan->qual = NIL;
+       plan->lefttree = lefttree;
+       plan->righttree = NULL;
+
+       /*
+        * Convert pathkeys list into arrays of attr indexes and equality
+        * operators, as wanted by executor.  This has a lot in common with
+        * prepare_sort_from_pathkeys ... maybe unify sometime?
+        */
+       Assert(numCols >= 0 && numCols <= list_length(pathkeys));
+       uniqColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
+       uniqOperators = (Oid *) palloc(sizeof(Oid) * numCols);
+
+       foreach(lc, pathkeys)
+       {
+               PathKey    *pathkey = (PathKey *) lfirst(lc);
+               EquivalenceClass *ec = pathkey->pk_eclass;
+               EquivalenceMember *em;
+               TargetEntry *tle = NULL;
+               Oid                     pk_datatype = InvalidOid;
+               Oid                     eqop;
+               ListCell   *j;
+
+               /* Ignore pathkeys beyond the specified number of columns */
+               if (keyno >= numCols)
+                       break;
+
+               if (ec->ec_has_volatile)
+               {
+                       /*
+                        * If the pathkey's EquivalenceClass is volatile, then it must
+                        * have come from an ORDER BY clause, and we have to match it to
+                        * that same targetlist entry.
+                        */
+                       if (ec->ec_sortref == 0)        /* can't happen */
+                               elog(ERROR, "volatile EquivalenceClass has no sortref");
+                       tle = get_sortgroupref_tle(ec->ec_sortref, plan->targetlist);
+                       Assert(tle);
+                       Assert(list_length(ec->ec_members) == 1);
+                       pk_datatype = ((EquivalenceMember *) linitial(ec->ec_members))->em_datatype;
+               }
+               else
+               {
+                       /*
+                        * Otherwise, we can use any non-constant expression listed in the
+                        * pathkey's EquivalenceClass.  For now, we take the first tlist
+                        * item found in the EC.
+                        */
+                       foreach(j, plan->targetlist)
+                       {
+                               tle = (TargetEntry *) lfirst(j);
+                               em = find_ec_member_for_tle(ec, tle, NULL);
+                               if (em)
+                               {
+                                       /* found expr already in tlist */
+                                       pk_datatype = em->em_datatype;
+                                       break;
+                               }
+                               tle = NULL;
+                       }
+               }
+
+               if (!tle)
+                       elog(ERROR, "could not find pathkey item to sort");
+
+               /*
+                * Look up the correct equality operator from the PathKey's slightly
+                * abstracted representation.
+                */
+               eqop = get_opfamily_member(pathkey->pk_opfamily,
+                                                                  pk_datatype,
+                                                                  pk_datatype,
+                                                                  BTEqualStrategyNumber);
+               if (!OidIsValid(eqop))  /* should not happen */
+                       elog(ERROR, "could not find member %d(%u,%u) of opfamily %u",
+                                BTEqualStrategyNumber, pk_datatype, pk_datatype,
+                                pathkey->pk_opfamily);
+
+               uniqColIdx[keyno] = tle->resno;
+               uniqOperators[keyno] = eqop;
+
+               keyno++;
+       }
+
+       node->numCols = numCols;
+       node->uniqColIdx = uniqColIdx;
+       node->uniqOperators = uniqOperators;
+
+       return node;
+}
+
+static Gather *
+make_gather(List *qptlist,
+                       List *qpqual,
+                       int nworkers,
+                       bool single_copy,
+                       Plan *subplan)
+{
+       Gather     *node = makeNode(Gather);
+       Plan       *plan = &node->plan;
+
+       plan->targetlist = qptlist;
+       plan->qual = qpqual;
+       plan->lefttree = subplan;
+       plan->righttree = NULL;
+       node->num_workers = nworkers;
+       node->single_copy = single_copy;
+       node->invisible = false;
+
+       return node;
+}
+
 /*
  * distinctList is a list of SortGroupClauses, identifying the targetlist
  * items that should be considered by the SetOp filter.  The input path must
  * already be sorted accordingly.
  */
-SetOp *
+static SetOp *
 make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
                   List *distinctList, AttrNumber flagColIdx, int firstFlag,
-                  long numGroups, double outputRows)
+                  long numGroups)
 {
        SetOp      *node = makeNode(SetOp);
        Plan       *plan = &node->plan;
@@ -4571,15 +6157,6 @@ make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
        Oid                *dupOperators;
        ListCell   *slitem;
 
-       copy_plan_costsize(plan, lefttree);
-       plan->plan_rows = outputRows;
-
-       /*
-        * Charge one cpu_operator_cost per comparison per input tuple. We assume
-        * all columns get compared at most of the tuples.
-        */
-       plan->total_cost += cpu_operator_cost * lefttree->plan_rows * numCols;
-
        plan->targetlist = lefttree->targetlist;
        plan->qual = NIL;
        plan->lefttree = lefttree;
@@ -4620,17 +6197,12 @@ make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
  * make_lockrows
  *       Build a LockRows plan node
  */
-LockRows *
+static LockRows *
 make_lockrows(Plan *lefttree, List *rowMarks, int epqParam)
 {
        LockRows   *node = makeNode(LockRows);
        Plan       *plan = &node->plan;
 
-       copy_plan_costsize(plan, lefttree);
-
-       /* charge cpu_tuple_cost to reflect locking costs (underestimate?) */
-       plan->total_cost += cpu_tuple_cost * plan->plan_rows;
-
        plan->targetlist = lefttree->targetlist;
        plan->qual = NIL;
        plan->lefttree = lefttree;
@@ -4643,68 +6215,15 @@ make_lockrows(Plan *lefttree, List *rowMarks, int epqParam)
 }
 
 /*
- * Note: offset_est and count_est are passed in to save having to repeat
- * work already done to estimate the values of the limitOffset and limitCount
- * expressions.  Their values are as returned by preprocess_limit (0 means
- * "not relevant", -1 means "couldn't estimate").  Keep the code below in sync
- * with that function!
+ * make_limit
+ *       Build a Limit plan node
  */
 Limit *
-make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount,
-                  int64 offset_est, int64 count_est)
+make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount)
 {
        Limit      *node = makeNode(Limit);
        Plan       *plan = &node->plan;
 
-       copy_plan_costsize(plan, lefttree);
-
-       /*
-        * Adjust the output rows count and costs according to the offset/limit.
-        * This is only a cosmetic issue if we are at top level, but if we are
-        * building a subquery then it's important to report correct info to the
-        * outer planner.
-        *
-        * When the offset or count couldn't be estimated, use 10% of the
-        * estimated number of rows emitted from the subplan.
-        */
-       if (offset_est != 0)
-       {
-               double          offset_rows;
-
-               if (offset_est > 0)
-                       offset_rows = (double) offset_est;
-               else
-                       offset_rows = clamp_row_est(lefttree->plan_rows * 0.10);
-               if (offset_rows > plan->plan_rows)
-                       offset_rows = plan->plan_rows;
-               if (plan->plan_rows > 0)
-                       plan->startup_cost +=
-                               (plan->total_cost - plan->startup_cost)
-                               * offset_rows / plan->plan_rows;
-               plan->plan_rows -= offset_rows;
-               if (plan->plan_rows < 1)
-                       plan->plan_rows = 1;
-       }
-
-       if (count_est != 0)
-       {
-               double          count_rows;
-
-               if (count_est > 0)
-                       count_rows = (double) count_est;
-               else
-                       count_rows = clamp_row_est(lefttree->plan_rows * 0.10);
-               if (count_rows > plan->plan_rows)
-                       count_rows = plan->plan_rows;
-               if (plan->plan_rows > 0)
-                       plan->total_cost = plan->startup_cost +
-                               (plan->total_cost - plan->startup_cost)
-                               * count_rows / plan->plan_rows;
-               plan->plan_rows = count_rows;
-               if (plan->plan_rows < 1)
-                       plan->plan_rows = 1;
-       }
-
        plan->targetlist = lefttree->targetlist;
        plan->qual = NIL;
        plan->lefttree = lefttree;
@@ -4719,45 +6238,39 @@ make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount,
 /*
  * make_result
  *       Build a Result plan node
- *
- * If we have a subplan, assume that any evaluation costs for the gating qual
- * were already factored into the subplan's startup cost, and just copy the
- * subplan cost.  If there's no subplan, we should include the qual eval
- * cost.  In either case, tlist eval cost is not to be included here.
  */
-Result *
-make_result(PlannerInfo *root,
-                       List *tlist,
+static Result *
+make_result(List *tlist,
                        Node *resconstantqual,
                        Plan *subplan)
 {
        Result     *node = makeNode(Result);
        Plan       *plan = &node->plan;
 
-       if (subplan)
-               copy_plan_costsize(plan, subplan);
-       else
-       {
-               plan->startup_cost = 0;
-               plan->total_cost = cpu_tuple_cost;
-               plan->plan_rows = 1;    /* wrong if we have a set-valued function? */
-               plan->plan_width = 0;   /* XXX is it worth being smarter? */
-               if (resconstantqual)
-               {
-                       QualCost        qual_cost;
+       plan->targetlist = tlist;
+       plan->qual = NIL;
+       plan->lefttree = subplan;
+       plan->righttree = NULL;
+       node->resconstantqual = resconstantqual;
 
-                       cost_qual_eval(&qual_cost, (List *) resconstantqual, root);
-                       /* resconstantqual is evaluated once at startup */
-                       plan->startup_cost += qual_cost.startup + qual_cost.per_tuple;
-                       plan->total_cost += qual_cost.startup + qual_cost.per_tuple;
-               }
-       }
+       return node;
+}
+
+/*
+ * make_project_set
+ *       Build a ProjectSet plan node
+ */
+static ProjectSet *
+make_project_set(List *tlist,
+                                Plan *subplan)
+{
+       ProjectSet *node = makeNode(ProjectSet);
+       Plan       *plan = &node->plan;
 
        plan->targetlist = tlist;
        plan->qual = NIL;
        plan->lefttree = subplan;
        plan->righttree = NULL;
-       node->resconstantqual = resconstantqual;
 
        return node;
 }
@@ -4765,25 +6278,18 @@ make_result(PlannerInfo *root,
 /*
  * make_modifytable
  *       Build a ModifyTable plan node
- *
- * Currently, we don't charge anything extra for the actual table modification
- * work, nor for the WITH CHECK OPTIONS or RETURNING expressions if any.  It
- * would only be window dressing, since these are always top-level nodes and
- * there is no way for the costs to change any higher-level planning choices.
- * But we might want to make it look better sometime.
  */
-ModifyTable *
+static ModifyTable *
 make_modifytable(PlannerInfo *root,
                                 CmdType operation, bool canSetTag,
+                                Index nominalRelation,
                                 List *resultRelations, List *subplans,
                                 List *withCheckOptionLists, List *returningLists,
-                                List *rowMarks, int epqParam)
+                                List *rowMarks, OnConflictExpr *onconflict, int epqParam)
 {
        ModifyTable *node = makeNode(ModifyTable);
-       Plan       *plan = &node->plan;
-       double          total_size;
        List       *fdw_private_list;
-       ListCell   *subnode;
+       Bitmapset  *direct_modify_plans;
        ListCell   *lc;
        int                     i;
 
@@ -4793,28 +6299,6 @@ make_modifytable(PlannerInfo *root,
        Assert(returningLists == NIL ||
                   list_length(resultRelations) == list_length(returningLists));
 
-       /*
-        * Compute cost as sum of subplan costs.
-        */
-       plan->startup_cost = 0;
-       plan->total_cost = 0;
-       plan->plan_rows = 0;
-       total_size = 0;
-       foreach(subnode, subplans)
-       {
-               Plan       *subplan = (Plan *) lfirst(subnode);
-
-               if (subnode == list_head(subplans))             /* first node? */
-                       plan->startup_cost = subplan->startup_cost;
-               plan->total_cost += subplan->total_cost;
-               plan->plan_rows += subplan->plan_rows;
-               total_size += subplan->plan_width * subplan->plan_rows;
-       }
-       if (plan->plan_rows > 0)
-               plan->plan_width = rint(total_size / plan->plan_rows);
-       else
-               plan->plan_width = 0;
-
        node->plan.lefttree = NULL;
        node->plan.righttree = NULL;
        node->plan.qual = NIL;
@@ -4823,9 +6307,36 @@ make_modifytable(PlannerInfo *root,
 
        node->operation = operation;
        node->canSetTag = canSetTag;
+       node->nominalRelation = nominalRelation;
        node->resultRelations = resultRelations;
        node->resultRelIndex = -1;      /* will be set correctly in setrefs.c */
        node->plans = subplans;
+       if (!onconflict)
+       {
+               node->onConflictAction = ONCONFLICT_NONE;
+               node->onConflictSet = NIL;
+               node->onConflictWhere = NULL;
+               node->arbiterIndexes = NIL;
+               node->exclRelRTI = 0;
+               node->exclRelTlist = NIL;
+       }
+       else
+       {
+               node->onConflictAction = onconflict->action;
+               node->onConflictSet = onconflict->onConflictSet;
+               node->onConflictWhere = onconflict->onConflictWhere;
+
+               /*
+                * If a set of unique index inference elements was provided (an
+                * INSERT...ON CONFLICT "inference specification"), then infer
+                * appropriate unique indexes (or throw an error if none are
+                * available).
+                */
+               node->arbiterIndexes = infer_arbiter_indexes(root);
+
+               node->exclRelRTI = onconflict->exclRelIndex;
+               node->exclRelTlist = onconflict->exclRelTlist;
+       }
        node->withCheckOptionLists = withCheckOptionLists;
        node->returningLists = returningLists;
        node->rowMarks = rowMarks;
@@ -4836,12 +6347,14 @@ make_modifytable(PlannerInfo *root,
         * construct private plan data, and accumulate it all into a list.
         */
        fdw_private_list = NIL;
+       direct_modify_plans = NULL;
        i = 0;
        foreach(lc, resultRelations)
        {
                Index           rti = lfirst_int(lc);
                FdwRoutine *fdwroutine;
                List       *fdw_private;
+               bool            direct_modify;
 
                /*
                 * If possible, we want to get the FdwRoutine from our RelOptInfo for
@@ -4868,7 +6381,23 @@ make_modifytable(PlannerInfo *root,
                                fdwroutine = NULL;
                }
 
+               /*
+                * If the target foreign table has any row-level triggers, we can't
+                * modify the foreign table directly.
+                */
+               direct_modify = false;
                if (fdwroutine != NULL &&
+                       fdwroutine->PlanDirectModify != NULL &&
+                       fdwroutine->BeginDirectModify != NULL &&
+                       fdwroutine->IterateDirectModify != NULL &&
+                       fdwroutine->EndDirectModify != NULL &&
+                       !has_row_triggers(root, rti, operation))
+                       direct_modify = fdwroutine->PlanDirectModify(root, node, rti, i);
+               if (direct_modify)
+                       direct_modify_plans = bms_add_member(direct_modify_plans, i);
+
+               if (!direct_modify &&
+                       fdwroutine != NULL &&
                        fdwroutine->PlanForeignModify != NULL)
                        fdw_private = fdwroutine->PlanForeignModify(root, node, rti, i);
                else
@@ -4877,10 +6406,56 @@ make_modifytable(PlannerInfo *root,
                i++;
        }
        node->fdwPrivLists = fdw_private_list;
+       node->fdwDirectModifyPlans = direct_modify_plans;
 
        return node;
 }
 
+/*
+ * is_projection_capable_path
+ *             Check whether a given Path node is able to do projection.
+ */
+bool
+is_projection_capable_path(Path *path)
+{
+       /* Most plan types can project, so just list the ones that can't */
+       switch (path->pathtype)
+       {
+               case T_Hash:
+               case T_Material:
+               case T_Sort:
+               case T_Unique:
+               case T_SetOp:
+               case T_LockRows:
+               case T_Limit:
+               case T_ModifyTable:
+               case T_MergeAppend:
+               case T_RecursiveUnion:
+                       return false;
+               case T_Append:
+
+                       /*
+                        * Append can't project, but if it's being used to represent a
+                        * dummy path, claim that it can project.  This prevents us from
+                        * converting a rel from dummy to non-dummy status by applying a
+                        * projection to its dummy path.
+                        */
+                       return IS_DUMMY_PATH(path);
+               case T_ProjectSet:
+
+                       /*
+                        * Although ProjectSet certainly projects, say "no" because we
+                        * don't want the planner to randomly replace its tlist with
+                        * something else; the SRFs have to stay at top level.  This might
+                        * get relaxed later.
+                        */
+                       return false;
+               default:
+                       break;
+       }
+       return true;
+}
+
 /*
  * is_projection_capable_plan
  *             Check whether a given Plan node is able to do projection.
@@ -4903,6 +6478,15 @@ is_projection_capable_plan(Plan *plan)
                case T_MergeAppend:
                case T_RecursiveUnion:
                        return false;
+               case T_ProjectSet:
+
+                       /*
+                        * Although ProjectSet certainly projects, say "no" because we
+                        * don't want the planner to randomly replace its tlist with
+                        * something else; the SRFs have to stay at top level.  This might
+                        * get relaxed later.
+                        */
+                       return false;
                default:
                        break;
        }