]> granicus.if.org Git - postgresql/blobdiff - src/backend/optimizer/plan/createplan.c
Update copyright for 2019
[postgresql] / src / backend / optimizer / plan / createplan.c
index ed06a8de7873ed71abeaa03d0454699f3ad27928..066685c3c7b44b2c5f21bc53ae88621ab689b728 100644 (file)
@@ -5,7 +5,7 @@
  *       Planning is complete, we just need to convert the selected
  *       Path into a Plan.
  *
- * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
@@ -41,6 +41,7 @@
 #include "optimizer/var.h"
 #include "parser/parse_clause.h"
 #include "parser/parsetree.h"
+#include "partitioning/partprune.h"
 #include "utils/lsyscache.h"
 
 
  * 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.
+ *
+ * CP_IGNORE_TLIST specifies that the caller plans to replace the targetlist,
+ * and therefore it doesn't matter a bit what target list gets generated.
  */
-#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 */
+#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 */
+#define CP_IGNORE_TLIST                0x0008  /* caller will replace tlist */
 
 
 static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path,
@@ -87,8 +92,10 @@ static Material *create_material_plan(PlannerInfo *root, MaterialPath *best_path
 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 Plan *create_projection_plan(PlannerInfo *root,
+                                          ProjectionPath *best_path,
+                                          int flags);
+static Plan *inject_projection_plan(Plan *subplan, List *tlist, bool parallel_safe);
 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,
@@ -100,15 +107,6 @@ static WindowAgg *create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_p
 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);
@@ -139,6 +137,8 @@ static TableFuncScan *create_tablefuncscan_plan(PlannerInfo *root, Path *best_pa
                                                  List *tlist, List *scan_clauses);
 static CteScan *create_ctescan_plan(PlannerInfo *root, Path *best_path,
                                        List *tlist, List *scan_clauses);
+static NamedTuplestoreScan *create_namedtuplestorescan_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,
@@ -197,9 +197,12 @@ 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 NamedTuplestoreScan *make_namedtuplestorescan(List *qptlist, List *qpqual,
+                                                Index scanrelid, char *enrname);
 static WorkTableScan *make_worktablescan(List *qptlist, List *qpqual,
                                   Index scanrelid, int wtParam);
-static Append *make_append(List *appendplans, List *tlist, List *partitioned_rels);
+static Append *make_append(List *appendplans, int first_partial_plan,
+                       List *tlist, PartitionPruneInfo *partpruneinfo);
 static RecursiveUnion *make_recursive_union(List *tlist,
                                         Plan *lefttree,
                                         Plan *righttree,
@@ -211,18 +214,16 @@ static BitmapOr *make_bitmap_or(List *bitmapplans);
 static NestLoop *make_nestloop(List *tlist,
                          List *joinclauses, List *otherclauses, List *nestParams,
                          Plan *lefttree, Plan *righttree,
-                         JoinType jointype);
+                         JoinType jointype, bool inner_unique);
 static HashJoin *make_hashjoin(List *tlist,
                          List *joinclauses, List *otherclauses,
                          List *hashclauses,
                          Plan *lefttree, Plan *righttree,
-                         JoinType jointype);
+                         JoinType jointype, bool inner_unique);
 static Hash *make_hash(Plan *lefttree,
                  Oid skewTable,
                  AttrNumber skewColumn,
-                 bool skewInherit,
-                 Oid skewColType,
-                 int32 skewColTypmod);
+                 bool skewInherit);
 static MergeJoin *make_mergejoin(List *tlist,
                           List *joinclauses, List *otherclauses,
                           List *mergeclauses,
@@ -231,7 +232,8 @@ static MergeJoin *make_mergejoin(List *tlist,
                           int *mergestrategies,
                           bool *mergenullsfirst,
                           Plan *lefttree, Plan *righttree,
-                          JoinType jointype);
+                          JoinType jointype, bool inner_unique,
+                          bool skip_mark_restore);
 static Sort *make_sort(Plan *lefttree, int numCols,
                  AttrNumber *sortColIdx, Oid *sortOperators,
                  Oid *collations, bool *nullsFirst);
@@ -247,7 +249,8 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 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_pathkeys(Plan *lefttree, List *pathkeys,
+                                               Relids relids);
 static Sort *make_sort_from_groupcols(List *groupcls,
                                                 AttrNumber *grpColIdx,
                                                 Plan *lefttree);
@@ -256,6 +259,8 @@ 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,
+                          Oid startInRangeFunc, Oid endInRangeFunc,
+                          Oid inRangeColl, bool inRangeAsc, bool inRangeNullsFirst,
                           Plan *lefttree);
 static Group *make_group(List *tlist, List *qual, int numGroupCols,
                   AttrNumber *grpColIdx, Oid *grpOperators,
@@ -264,7 +269,7 @@ 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);
+                       int nworkers, int rescan_param, bool single_copy, Plan *subplan);
 static SetOp *make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
                   List *distinctList, AttrNumber flagColIdx, int firstFlag,
                   long numGroups);
@@ -273,8 +278,9 @@ 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 *partitioned_rels,
-                                List *resultRelations, List *subplans,
+                                Index nominalRelation, Index rootRelation,
+                                bool partColsUpdated,
+                                List *resultRelations, List *subplans, List *subroots,
                                 List *withCheckOptionLists, List *returningLists,
                                 List *rowMarks, OnConflictExpr *onconflict, int epqParam);
 static GatherMerge *create_gather_merge_plan(PlannerInfo *root,
@@ -352,6 +358,9 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
 {
        Plan       *plan;
 
+       /* Guard against stack overflow due to overly complex plans */
+       check_stack_depth();
+
        switch (best_path->pathtype)
        {
                case T_SeqScan:
@@ -366,6 +375,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
                case T_ValuesScan:
                case T_CteScan:
                case T_WorkTableScan:
+               case T_NamedTuplestoreScan:
                case T_ForeignScan:
                case T_CustomScan:
                        plan = create_scan_plan(root, best_path, flags);
@@ -388,12 +398,13 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
                        if (IsA(best_path, ProjectionPath))
                        {
                                plan = create_projection_plan(root,
-                                                                                         (ProjectionPath *) best_path);
+                                                                                         (ProjectionPath *) best_path,
+                                                                                         flags);
                        }
                        else if (IsA(best_path, MinMaxAggPath))
                        {
                                plan = (Plan *) create_minmaxagg_plan(root,
-                                                                                               (MinMaxAggPath *) best_path);
+                                                                                                         (MinMaxAggPath *) best_path);
                        }
                        else
                        {
@@ -404,7 +415,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
                        break;
                case T_ProjectSet:
                        plan = (Plan *) create_project_set_plan(root,
-                                                                                          (ProjectSetPath *) best_path);
+                                                                                                       (ProjectSetPath *) best_path);
                        break;
                case T_Material:
                        plan = (Plan *) create_material_plan(root,
@@ -415,7 +426,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
                        if (IsA(best_path, UpperUniquePath))
                        {
                                plan = (Plan *) create_upper_unique_plan(root,
-                                                                                          (UpperUniquePath *) best_path,
+                                                                                                                (UpperUniquePath *) best_path,
                                                                                                                 flags);
                        }
                        else
@@ -442,7 +453,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
                case T_Agg:
                        if (IsA(best_path, GroupingSetsPath))
                                plan = create_groupingsets_plan(root,
-                                                                                        (GroupingSetsPath *) best_path);
+                                                                                               (GroupingSetsPath *) best_path);
                        else
                        {
                                Assert(IsA(best_path, AggPath));
@@ -452,7 +463,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
                        break;
                case T_WindowAgg:
                        plan = (Plan *) create_windowagg_plan(root,
-                                                                                               (WindowAggPath *) best_path);
+                                                                                                 (WindowAggPath *) best_path);
                        break;
                case T_SetOp:
                        plan = (Plan *) create_setop_plan(root,
@@ -461,7 +472,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
                        break;
                case T_RecursiveUnion:
                        plan = (Plan *) create_recursiveunion_plan(root,
-                                                                                  (RecursiveUnionPath *) best_path);
+                                                                                                          (RecursiveUnionPath *) best_path);
                        break;
                case T_LockRows:
                        plan = (Plan *) create_lockrows_plan(root,
@@ -470,7 +481,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
                        break;
                case T_ModifyTable:
                        plan = (Plan *) create_modifytable_plan(root,
-                                                                                         (ModifyTablePath *) best_path);
+                                                                                                       (ModifyTablePath *) best_path);
                        break;
                case T_Limit:
                        plan = (Plan *) create_limit_plan(root,
@@ -479,7 +490,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
                        break;
                case T_GatherMerge:
                        plan = (Plan *) create_gather_merge_plan(root,
-                                                                                         (GatherMergePath *) best_path);
+                                                                                                        (GatherMergePath *) best_path);
                        break;
                default:
                        elog(ERROR, "unrecognized node type: %d",
@@ -551,8 +562,16 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
         * 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.
+        *
+        * But if the caller is going to ignore our tlist anyway, then don't
+        * bother generating one at all.  We use an exact equality test here, so
+        * that this only applies when CP_IGNORE_TLIST is the only flag set.
         */
-       if (use_physical_tlist(root, best_path, flags))
+       if (flags == CP_IGNORE_TLIST)
+       {
+               tlist = NULL;
+       }
+       else if (use_physical_tlist(root, best_path, flags))
        {
                if (best_path->pathtype == T_IndexOnlyScan)
                {
@@ -560,10 +579,10 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
                        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.
+                        * Transfer sortgroupref data to the replacement tlist, if
+                        * requested (use_physical_tlist checked that this will work).
                         */
-                       if (!gating_clauses)
+                       if (flags & CP_LABEL_TLIST)
                                apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
                }
                else
@@ -577,7 +596,7 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
                        else
                        {
                                /* As above, transfer sortgroupref data to replacement tlist */
-                               if (!gating_clauses)
+                               if (flags & CP_LABEL_TLIST)
                                        apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
                        }
                }
@@ -621,7 +640,7 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
 
                case T_BitmapHeapScan:
                        plan = (Plan *) create_bitmap_scan_plan(root,
-                                                                                               (BitmapHeapPath *) best_path,
+                                                                                                       (BitmapHeapPath *) best_path,
                                                                                                        tlist,
                                                                                                        scan_clauses);
                        break;
@@ -635,7 +654,7 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
 
                case T_SubqueryScan:
                        plan = (Plan *) create_subqueryscan_plan(root,
-                                                                                         (SubqueryScanPath *) best_path,
+                                                                                                        (SubqueryScanPath *) best_path,
                                                                                                         tlist,
                                                                                                         scan_clauses);
                        break;
@@ -668,6 +687,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
                                                                                                scan_clauses);
                        break;
 
+               case T_NamedTuplestoreScan:
+                       plan = (Plan *) create_namedtuplestorescan_plan(root,
+                                                                                                                       best_path,
+                                                                                                                       tlist,
+                                                                                                                       scan_clauses);
+                       break;
+
                case T_WorkTableScan:
                        plan = (Plan *) create_worktablescan_plan(root,
                                                                                                          best_path,
@@ -786,6 +812,24 @@ use_physical_tlist(PlannerInfo *root, Path *path, int flags)
        if (rel->reloptkind != RELOPT_BASEREL)
                return false;
 
+       /*
+        * Also, don't do it to a CustomPath; the premise that we're extracting
+        * columns from a simple physical tuple is unlikely to hold for those.
+        * (When it does make sense, the custom path creator can set up the path's
+        * pathtarget that way.)
+        */
+       if (IsA(path, CustomPath))
+               return false;
+
+       /*
+        * If a bitmap scan's tlist is empty, keep it as-is.  This may allow the
+        * executor to skip heap page fetches, and in any case, the benefit of
+        * using a physical tlist instead would be minimal.
+        */
+       if (IsA(path, BitmapHeapPath) &&
+               path->pathtarget->exprs == NIL)
+               return false;
+
        /*
         * Can't do it if any system columns or whole-row Vars are requested.
         * (This could possibly be fixed but would take some fragile assumptions
@@ -907,6 +951,9 @@ create_gating_plan(PlannerInfo *root, Path *path, Plan *plan,
         */
        copy_plan_costsize(gplan, plan);
 
+       /* Gating quals could be unsafe, so better use the Path's safety flag */
+       gplan->parallel_safe = path->parallel_safe;
+
        return gplan;
 }
 
@@ -962,7 +1009,7 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path)
        if (get_loc_restrictinfo(best_path) != NIL)
                set_qpqual((Plan) plan,
                                   list_concat(get_qpqual((Plan) plan),
-                                          get_actual_clauses(get_loc_restrictinfo(best_path))));
+                                                          get_actual_clauses(get_loc_restrictinfo(best_path))));
 #endif
 
        return plan;
@@ -982,6 +1029,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
        List       *tlist = build_path_tlist(root, &best_path->path);
        List       *subplans = NIL;
        ListCell   *subpaths;
+       RelOptInfo *rel = best_path->path.parent;
+       PartitionPruneInfo *partpruneinfo = NULL;
 
        /*
         * The subpaths list could be empty, if every child was proven empty by
@@ -1019,6 +1068,38 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
                subplans = lappend(subplans, subplan);
        }
 
+       /*
+        * If any quals exist, they may be useful to perform further partition
+        * pruning during execution.  Gather information needed by the executor to
+        * do partition pruning.
+        */
+       if (enable_partition_pruning &&
+               rel->reloptkind == RELOPT_BASEREL &&
+               best_path->partitioned_rels != NIL)
+       {
+               List       *prunequal;
+
+               prunequal = extract_actual_clauses(rel->baserestrictinfo, false);
+
+               if (best_path->path.param_info)
+               {
+                       List       *prmquals = best_path->path.param_info->ppi_clauses;
+
+                       prmquals = extract_actual_clauses(prmquals, false);
+                       prmquals = (List *) replace_nestloop_params(root,
+                                                                                                               (Node *) prmquals);
+
+                       prunequal = list_concat(prunequal, prmquals);
+               }
+
+               if (prunequal != NIL)
+                       partpruneinfo =
+                               make_partition_pruneinfo(root, rel,
+                                                                                best_path->subpaths,
+                                                                                best_path->partitioned_rels,
+                                                                                prunequal);
+       }
+
        /*
         * XXX ideally, if there's just one child, we'd not bother to generate an
         * Append node but just return the single child.  At the moment this does
@@ -1026,7 +1107,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
         * parent-rel Vars it'll be asked to emit.
         */
 
-       plan = make_append(subplans, tlist, best_path->partitioned_rels);
+       plan = make_append(subplans, best_path->first_partial_path,
+                                          tlist, partpruneinfo);
 
        copy_generic_path_info(&plan->plan, (Path *) best_path);
 
@@ -1049,6 +1131,8 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
        List       *pathkeys = best_path->path.pathkeys;
        List       *subplans = NIL;
        ListCell   *subpaths;
+       RelOptInfo *rel = best_path->path.parent;
+       PartitionPruneInfo *partpruneinfo = NULL;
 
        /*
         * We don't have the actual creation of the MergeAppend node split out
@@ -1134,8 +1218,40 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
                subplans = lappend(subplans, subplan);
        }
 
-       node->partitioned_rels = best_path->partitioned_rels;
+       /*
+        * If any quals exist, they may be useful to perform further partition
+        * pruning during execution.  Gather information needed by the executor to
+        * do partition pruning.
+        */
+       if (enable_partition_pruning &&
+               rel->reloptkind == RELOPT_BASEREL &&
+               best_path->partitioned_rels != NIL)
+       {
+               List       *prunequal;
+
+               prunequal = extract_actual_clauses(rel->baserestrictinfo, false);
+
+               if (best_path->path.param_info)
+               {
+
+                       List       *prmquals = best_path->path.param_info->ppi_clauses;
+
+                       prmquals = extract_actual_clauses(prmquals, false);
+                       prmquals = (List *) replace_nestloop_params(root,
+                                                                                                               (Node *) prmquals);
+
+                       prunequal = list_concat(prunequal, prmquals);
+               }
+
+               if (prunequal != NIL)
+                       partpruneinfo = make_partition_pruneinfo(root, rel,
+                                                                                                        best_path->subpaths,
+                                                                                                        best_path->partitioned_rels,
+                                                                                                        prunequal);
+       }
+
        node->mergeplans = subplans;
+       node->part_prune_info = partpruneinfo;
 
        return (Plan *) node;
 }
@@ -1291,19 +1407,10 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
                }
        }
 
+       /* Use change_plan_targetlist in case we need to insert a Result node */
        if (newitems || best_path->umethod == UNIQUE_PATH_SORT)
-       {
-               /*
-                * If the top plan node can't do projections and its existing target
-                * list isn't already what we need, we need to add a Result node to
-                * help it along.
-                */
-               if (!is_projection_capable_plan(subplan) &&
-                       !tlist_same_exprs(newtlist, subplan->targetlist))
-                       subplan = inject_projection_plan(subplan, newtlist);
-               else
-                       subplan->targetlist = newtlist;
-       }
+               subplan = change_plan_targetlist(subplan, newtlist,
+                                                                                best_path->path.parallel_safe);
 
        /*
         * Build control information showing which subplan output columns are to
@@ -1394,7 +1501,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
                         * for the IN clause operators' RHS datatype.
                         */
                        eqop = get_equality_op_for_ordering_op(sortop, NULL);
-                       if (!OidIsValid(eqop))          /* shouldn't happen */
+                       if (!OidIsValid(eqop))  /* shouldn't happen */
                                elog(ERROR, "could not find equality operator for ordering operator %u",
                                         sortop);
 
@@ -1447,6 +1554,7 @@ create_gather_plan(PlannerInfo *root, GatherPath *best_path)
        gather_plan = make_gather(tlist,
                                                          NIL,
                                                          best_path->num_workers,
+                                                         SS_assign_special_param(root),
                                                          best_path->single_copy,
                                                          subplan);
 
@@ -1481,6 +1589,9 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
        gm_plan->num_workers = best_path->num_workers;
        copy_generic_path_info(&gm_plan->plan, &best_path->path);
 
+       /* Assign the rescan Param. */
+       gm_plan->rescan_param = SS_assign_special_param(root);
+
        /* Gather Merge is pointless with no pathkeys; use Gather instead. */
        Assert(pathkeys != NIL);
 
@@ -1521,34 +1632,71 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
  *       but sometimes we can just let the subplan do the work.
  */
 static Plan *
-create_projection_plan(PlannerInfo *root, ProjectionPath *best_path)
+create_projection_plan(PlannerInfo *root, ProjectionPath *best_path, int flags)
 {
        Plan       *plan;
        Plan       *subplan;
        List       *tlist;
+       bool            needs_result_node = false;
 
-       /* 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);
+       /*
+        * Convert our subpath to a Plan and determine whether we need a Result
+        * node.
+        *
+        * In most cases where we don't need to project, creation_projection_path
+        * will have set dummypp, but not always.  First, 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.)  Second, create_projection_path has no way of knowing
+        * what path node will be placed on top of the projection path and
+        * therefore can't predict whether it will require an exact tlist. For
+        * both of these reasons, we have to recheck here.
+        */
+       if (use_physical_tlist(root, &best_path->path, flags))
+       {
+               /*
+                * Our caller doesn't really care what tlist we return, so we don't
+                * actually need to project.  However, we may still need to ensure
+                * proper sortgroupref labels, if the caller cares about those.
+                */
+               subplan = create_plan_recurse(root, best_path->subpath, 0);
+               tlist = subplan->targetlist;
+               if (flags & CP_LABEL_TLIST)
+                       apply_pathtarget_labeling_to_tlist(tlist,
+                                                                                          best_path->path.pathtarget);
+       }
+       else if (is_projection_capable_path(best_path->subpath))
+       {
+               /*
+                * Our caller requires that we return the exact tlist, but no separate
+                * result node is needed because the subpath is projection-capable.
+                * Tell create_plan_recurse that we're going to ignore the tlist it
+                * produces.
+                */
+               subplan = create_plan_recurse(root, best_path->subpath,
+                                                                         CP_IGNORE_TLIST);
+               tlist = build_path_tlist(root, &best_path->path);
+       }
+       else
+       {
+               /*
+                * It looks like we need a result node, unless by good fortune the
+                * requested tlist is exactly the one the child wants to produce.
+                */
+               subplan = create_plan_recurse(root, best_path->subpath, 0);
+               tlist = build_path_tlist(root, &best_path->path);
+               needs_result_node = !tlist_same_exprs(tlist, subplan->targetlist);
+       }
 
        /*
-        * 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 we make a different decision about whether to include a Result node
+        * 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))
+       if (!needs_result_node)
        {
                /* Don't need a separate Result, just assign tlist to subplan */
                plan = subplan;
@@ -1559,7 +1707,8 @@ create_projection_plan(PlannerInfo *root, ProjectionPath *best_path)
                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 */
+               plan->parallel_safe = best_path->path.parallel_safe;
+               /* ... but don't change subplan's parallel_aware flag */
        }
        else
        {
@@ -1579,9 +1728,12 @@ create_projection_plan(PlannerInfo *root, ProjectionPath *best_path)
  * 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.
+ *
+ * One reason it's ugly is we have to be told the right parallel_safe marking
+ * to apply (since the tlist might be unsafe even if the child plan is safe).
  */
 static Plan *
-inject_projection_plan(Plan *subplan, List *tlist)
+inject_projection_plan(Plan *subplan, List *tlist, bool parallel_safe)
 {
        Plan       *plan;
 
@@ -1595,10 +1747,45 @@ inject_projection_plan(Plan *subplan, List *tlist)
         * consistent not more so.  Hence, just copy the subplan's cost.
         */
        copy_plan_costsize(plan, subplan);
+       plan->parallel_safe = parallel_safe;
 
        return plan;
 }
 
+/*
+ * change_plan_targetlist
+ *       Externally available wrapper for inject_projection_plan.
+ *
+ * This is meant for use by FDW plan-generation functions, which might
+ * want to adjust the tlist computed by some subplan tree.  In general,
+ * a Result node is needed to compute the new tlist, but we can optimize
+ * some cases.
+ *
+ * In most cases, tlist_parallel_safe can just be passed as the parallel_safe
+ * flag of the FDW's own Path node.
+ */
+Plan *
+change_plan_targetlist(Plan *subplan, List *tlist, bool tlist_parallel_safe)
+{
+       /*
+        * If the top plan node can't do projections and its existing target list
+        * isn't already what we need, we need to add a Result node to help it
+        * along.
+        */
+       if (!is_projection_capable_plan(subplan) &&
+               !tlist_same_exprs(tlist, subplan->targetlist))
+               subplan = inject_projection_plan(subplan, tlist,
+                                                                                subplan->parallel_safe &&
+                                                                                tlist_parallel_safe);
+       else
+       {
+               /* Else we can just replace the plan node's tlist */
+               subplan->targetlist = tlist;
+               subplan->parallel_safe &= tlist_parallel_safe;
+       }
+       return subplan;
+}
+
 /*
  * create_sort_plan
  *
@@ -1619,7 +1806,15 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
        subplan = create_plan_recurse(root, best_path->subpath,
                                                                  flags | CP_SMALL_TLIST);
 
-       plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys);
+       /*
+        * make_sort_from_pathkeys() indirectly calls find_ec_member_for_tle(),
+        * which will ignore any child EC members that don't belong to the given
+        * relids. Thus, if this sort path is based on a child relation, we must
+        * pass its relids.
+        */
+       plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
+                                                                  IS_OTHER_REL(best_path->subpath->parent) ?
+                                                                  best_path->path.parent->relids : NULL);
 
        copy_generic_path_info(&plan->plan, (Path *) best_path);
 
@@ -1832,7 +2027,7 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path)
         * 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->inhTargetKind == INHKIND_NONE);
        Assert(root->grouping_map == NULL);
        root->grouping_map = grouping_map;
 
@@ -1880,9 +2075,9 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path)
                                                                                 NIL,
                                                                                 strat,
                                                                                 AGGSPLIT_SIMPLE,
-                                                          list_length((List *) linitial(rollup->gsets)),
+                                                                                list_length((List *) linitial(rollup->gsets)),
                                                                                 new_grpColIdx,
-                                                                  extract_grouping_ops(rollup->groupClause),
+                                                                                extract_grouping_ops(rollup->groupClause),
                                                                                 rollup->gsets,
                                                                                 NIL,
                                                                                 rollup->numGroups,
@@ -1971,6 +2166,7 @@ create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path)
                plan->plan_rows = 1;
                plan->plan_width = mminfo->path->pathtarget->width;
                plan->parallel_aware = false;
+               plan->parallel_safe = mminfo->path->parallel_safe;
 
                /* Convert the plan into an InitPlan in the outer query. */
                SS_make_initplan_from_plan(root, subroot, plan, mminfo->param);
@@ -1993,7 +2189,7 @@ create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path)
         * 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->inhTargetKind == INHKIND_NONE);
        Assert(root->minmax_aggs == NIL);
        root->minmax_aggs = best_path->mmaggregates;
 
@@ -2011,19 +2207,17 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path)
 {
        WindowAgg  *plan;
        WindowClause *wc = best_path->winclause;
+       int                     numPart = list_length(wc->partitionClause);
+       int                     numOrder = list_length(wc->orderClause);
        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;
+       ListCell   *lc;
 
        /*
         * WindowAgg can project, so no need to be terribly picky about child
@@ -2034,32 +2228,43 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path)
        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.
+        * Convert SortGroupClause lists into arrays of attr indexes and equality
+        * operators, as wanted by executor.  (Note: in principle, it's possible
+        * to drop some of the sort columns, if they were proved redundant by
+        * pathkey logic.  However, it doesn't seem worth going out of our way to
+        * optimize such cases.  In any case, we must *not* remove the ordering
+        * column for RANGE OFFSET cases, as the executor needs that for in_range
+        * tests even if it's known to be equal to some partitioning column.)
         */
-       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);
+       partColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numPart);
+       partOperators = (Oid *) palloc(sizeof(Oid) * numPart);
+
+       partNumCols = 0;
+       foreach(lc, wc->partitionClause)
+       {
+               SortGroupClause *sgc = (SortGroupClause *) lfirst(lc);
+               TargetEntry *tle = get_sortgroupclause_tle(sgc, subplan->targetlist);
+
+               Assert(OidIsValid(sgc->eqop));
+               partColIdx[partNumCols] = tle->resno;
+               partOperators[partNumCols] = sgc->eqop;
+               partNumCols++;
+       }
+
+       ordColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numOrder);
+       ordOperators = (Oid *) palloc(sizeof(Oid) * numOrder);
+
+       ordNumCols = 0;
+       foreach(lc, wc->orderClause)
+       {
+               SortGroupClause *sgc = (SortGroupClause *) lfirst(lc);
+               TargetEntry *tle = get_sortgroupclause_tle(sgc, subplan->targetlist);
+
+               Assert(OidIsValid(sgc->eqop));
+               ordColIdx[ordNumCols] = tle->resno;
+               ordOperators[ordNumCols] = sgc->eqop;
+               ordNumCols++;
+       }
 
        /* And finally we can make the WindowAgg node */
        plan = make_windowagg(tlist,
@@ -2073,6 +2278,11 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path)
                                                  wc->frameOptions,
                                                  wc->startOffset,
                                                  wc->endOffset,
+                                                 wc->startInRangeFunc,
+                                                 wc->endInRangeFunc,
+                                                 wc->inRangeColl,
+                                                 wc->inRangeAsc,
+                                                 wc->inRangeNullsFirst,
                                                  subplan);
 
        copy_generic_path_info(&plan->plan, (Path *) best_path);
@@ -2080,112 +2290,6 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *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
  *
@@ -2326,9 +2430,11 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path)
                                                        best_path->operation,
                                                        best_path->canSetTag,
                                                        best_path->nominalRelation,
-                                                       best_path->partitioned_rels,
+                                                       best_path->rootRelation,
+                                                       best_path->partColsUpdated,
                                                        best_path->resultRelations,
                                                        subplans,
+                                                       best_path->subroots,
                                                        best_path->withCheckOptionLists,
                                                        best_path->returningLists,
                                                        best_path->rowMarks,
@@ -2537,7 +2643,7 @@ create_indexscan_plan(PlannerInfo *root,
        qpqual = NIL;
        foreach(l, scan_clauses)
        {
-               RestrictInfo *rinfo = castNode(RestrictInfo, lfirst(l));
+               RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
 
                if (rinfo->pseudoconstant)
                        continue;                       /* we may drop pseudoconstants here */
@@ -2546,7 +2652,7 @@ create_indexscan_plan(PlannerInfo *root,
                if (is_redundant_derived_clause(rinfo, indexquals))
                        continue;                       /* derived from same EquivalenceClass */
                if (!contain_mutable_functions((Node *) rinfo->clause) &&
-                       predicate_implied_by(list_make1(rinfo->clause), indexquals))
+                       predicate_implied_by(list_make1(rinfo->clause), indexquals, false))
                        continue;                       /* provably implied by indexquals */
                qpqual = lappend(qpqual, rinfo);
        }
@@ -2604,7 +2710,8 @@ create_indexscan_plan(PlannerInfo *root,
                                                                                 exprtype,
                                                                                 pathkey->pk_strategy);
                        if (!OidIsValid(sortop))
-                               elog(ERROR, "failed to find sort operator for ORDER BY expression");
+                               elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
+                                        pathkey->pk_strategy, exprtype, exprtype, pathkey->pk_opfamily);
                        indexorderbyops = lappend_oid(indexorderbyops, sortop);
                }
        }
@@ -2617,7 +2724,7 @@ create_indexscan_plan(PlannerInfo *root,
                                                                                                indexoid,
                                                                                                fixed_indexquals,
                                                                                                fixed_indexorderbys,
-                                                                                       best_path->indexinfo->indextlist,
+                                                                                               best_path->indexinfo->indextlist,
                                                                                                best_path->indexscandir);
        else
                scan_plan = (Scan *) make_indexscan(tlist,
@@ -2697,7 +2804,7 @@ create_bitmap_scan_plan(PlannerInfo *root,
        qpqual = NIL;
        foreach(l, scan_clauses)
        {
-               RestrictInfo *rinfo = castNode(RestrictInfo, lfirst(l));
+               RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
                Node       *clause = (Node *) rinfo->clause;
 
                if (rinfo->pseudoconstant)
@@ -2707,7 +2814,7 @@ create_bitmap_scan_plan(PlannerInfo *root,
                if (rinfo->parent_ec && list_member_ptr(indexECs, rinfo->parent_ec))
                        continue;                       /* derived from same EquivalenceClass */
                if (!contain_mutable_functions(clause) &&
-                       predicate_implied_by(list_make1(clause), indexquals))
+                       predicate_implied_by(list_make1(clause), indexquals, false))
                        continue;                       /* provably implied by indexquals */
                qpqual = lappend(qpqual, rinfo);
        }
@@ -2816,6 +2923,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
                        clamp_row_est(apath->bitmapselectivity * apath->path.parent->tuples);
                plan->plan_width = 0;   /* meaningless */
                plan->parallel_aware = false;
+               plan->parallel_safe = apath->path.parallel_safe;
                *qual = subquals;
                *indexqual = subindexquals;
                *indexECs = subindexECs;
@@ -2877,8 +2985,9 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
                        plan->total_cost = opath->path.total_cost;
                        plan->plan_rows =
                                clamp_row_est(opath->bitmapselectivity * opath->path.parent->tuples);
-                       plan->plan_width = 0;           /* meaningless */
+                       plan->plan_width = 0;   /* meaningless */
                        plan->parallel_aware = false;
+                       plan->parallel_safe = opath->path.parallel_safe;
                }
 
                /*
@@ -2923,6 +3032,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
                        clamp_row_est(ipath->indexselectivity * ipath->path.parent->tuples);
                plan->plan_width = 0;   /* meaningless */
                plan->parallel_aware = false;
+               plan->parallel_safe = ipath->path.parallel_safe;
                *qual = get_actual_clauses(ipath->indexclauses);
                *indexqual = get_actual_clauses(ipath->indexquals);
                foreach(l, ipath->indexinfo->indpred)
@@ -2935,7 +3045,8 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
                         * the conditions that got pushed into the bitmapqual.  Avoid
                         * generating redundant conditions.
                         */
-                       if (!predicate_implied_by(list_make1(pred), ipath->indexclauses))
+                       if (!predicate_implied_by(list_make1(pred), ipath->indexclauses,
+                                                                         false))
                        {
                                *qual = lappend(*qual, pred);
                                *indexqual = lappend(*indexqual, pred);
@@ -2972,18 +3083,72 @@ create_tidscan_plan(PlannerInfo *root, TidPath *best_path,
        TidScan    *scan_plan;
        Index           scan_relid = best_path->path.parent->relid;
        List       *tidquals = best_path->tidquals;
-       List       *ortidquals;
 
        /* it should be a base rel... */
        Assert(scan_relid > 0);
        Assert(best_path->path.parent->rtekind == RTE_RELATION);
 
+       /*
+        * The qpqual list must contain all restrictions not enforced by the
+        * tidquals list.  Since tidquals has OR semantics, we have to be careful
+        * about matching it up to scan_clauses.  It's convenient to handle the
+        * single-tidqual case separately from the multiple-tidqual case.  In the
+        * single-tidqual case, we look through the scan_clauses while they are
+        * still in RestrictInfo form, and drop any that are redundant with the
+        * tidqual.
+        *
+        * In normal cases simple pointer equality checks will be enough to spot
+        * duplicate RestrictInfos, so we try that first.
+        *
+        * Another common case is that a scan_clauses entry is generated from the
+        * same EquivalenceClass as some tidqual, and is therefore redundant with
+        * it, though not equal.
+        *
+        * Unlike indexpaths, we don't bother with predicate_implied_by(); the
+        * number of cases where it could win are pretty small.
+        */
+       if (list_length(tidquals) == 1)
+       {
+               List       *qpqual = NIL;
+               ListCell   *l;
+
+               foreach(l, scan_clauses)
+               {
+                       RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
+
+                       if (rinfo->pseudoconstant)
+                               continue;               /* we may drop pseudoconstants here */
+                       if (list_member_ptr(tidquals, rinfo))
+                               continue;               /* simple duplicate */
+                       if (is_redundant_derived_clause(rinfo, tidquals))
+                               continue;               /* derived from same EquivalenceClass */
+                       qpqual = lappend(qpqual, rinfo);
+               }
+               scan_clauses = qpqual;
+       }
+
        /* Sort clauses into best execution order */
        scan_clauses = order_qual_clauses(root, scan_clauses);
 
-       /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
+       /* Reduce RestrictInfo lists to bare expressions; ignore pseudoconstants */
+       tidquals = extract_actual_clauses(tidquals, false);
        scan_clauses = extract_actual_clauses(scan_clauses, false);
 
+       /*
+        * If we have multiple tidquals, it's more convenient to remove duplicate
+        * scan_clauses after stripping the RestrictInfos.  In this situation,
+        * because the tidquals represent OR sub-clauses, they could not have come
+        * from EquivalenceClasses so we don't have to worry about matching up
+        * non-identical clauses.  On the other hand, because tidpath.c will have
+        * extracted those sub-clauses from some OR clause and built its own list,
+        * we will certainly not have pointer equality to any scan clause.  So
+        * convert the tidquals list to an explicit OR clause and see if we can
+        * match it via equal() to any scan clause.
+        */
+       if (list_length(tidquals) > 1)
+               scan_clauses = list_difference(scan_clauses,
+                                                                          list_make1(make_orclause(tidquals)));
+
        /* Replace any outer-relation variables with nestloop params */
        if (best_path->path.param_info)
        {
@@ -2993,15 +3158,6 @@ create_tidscan_plan(PlannerInfo *root, TidPath *best_path,
                        replace_nestloop_params(root, (Node *) scan_clauses);
        }
 
-       /*
-        * Remove any clauses that are TID quals.  This is a bit tricky since the
-        * tidquals list has implicit OR semantics.
-        */
-       ortidquals = tidquals;
-       if (list_length(ortidquals) > 1)
-               ortidquals = list_make1(make_orclause(ortidquals));
-       scan_clauses = list_difference(scan_clauses, ortidquals);
-
        scan_plan = make_tidscan(tlist,
                                                         scan_clauses,
                                                         scan_relid,
@@ -3285,6 +3441,45 @@ create_ctescan_plan(PlannerInfo *root, Path *best_path,
        return scan_plan;
 }
 
+/*
+ * create_namedtuplestorescan_plan
+ *      Returns a tuplestorescan plan for the base relation scanned by
+ *     'best_path' with restriction clauses 'scan_clauses' and targetlist
+ *     'tlist'.
+ */
+static NamedTuplestoreScan *
+create_namedtuplestorescan_plan(PlannerInfo *root, Path *best_path,
+                                                               List *tlist, List *scan_clauses)
+{
+       NamedTuplestoreScan *scan_plan;
+       Index           scan_relid = best_path->parent->relid;
+       RangeTblEntry *rte;
+
+       Assert(scan_relid > 0);
+       rte = planner_rt_fetch(scan_relid, root);
+       Assert(rte->rtekind == RTE_NAMEDTUPLESTORE);
+
+       /* 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_namedtuplestorescan(tlist, scan_clauses, scan_relid,
+                                                                                rte->enrname);
+
+       copy_generic_path_info(&scan_plan->scan.plan, best_path);
+
+       return scan_plan;
+}
+
 /*
  * create_worktablescan_plan
  *      Returns a worktablescan plan for the base relation scanned by 'best_path'
@@ -3321,7 +3516,7 @@ create_worktablescan_plan(PlannerInfo *root, Path *best_path,
                if (!cteroot)                   /* shouldn't happen */
                        elog(ERROR, "bad levelsup for CTE \"%s\"", rte->ctename);
        }
-       if (cteroot->wt_param_id < 0)           /* shouldn't happen */
+       if (cteroot->wt_param_id < 0)   /* shouldn't happen */
                elog(ERROR, "could not find param ID for CTE \"%s\"", rte->ctename);
 
        /* Sort clauses into best execution order */
@@ -3604,6 +3799,7 @@ create_nestloop_plan(PlannerInfo *root,
        if (IS_OUTER_JOIN(best_path->jointype))
        {
                extract_actual_join_clauses(joinrestrictclauses,
+                                                                       best_path->path.parent->relids,
                                                                        &joinclauses, &otherclauses);
        }
        else
@@ -3645,7 +3841,7 @@ create_nestloop_plan(PlannerInfo *root,
                                 bms_overlap(((PlaceHolderVar *) nlp->paramval)->phrels,
                                                         outerrelids) &&
                                 bms_is_subset(find_placeholder_info(root,
-                                                                                       (PlaceHolderVar *) nlp->paramval,
+                                                                                                        (PlaceHolderVar *) nlp->paramval,
                                                                                                         false)->ph_eval_at,
                                                           outerrelids))
                {
@@ -3663,7 +3859,8 @@ create_nestloop_plan(PlannerInfo *root,
                                                          nestParams,
                                                          outer_plan,
                                                          inner_plan,
-                                                         best_path->jointype);
+                                                         best_path->jointype,
+                                                         best_path->inner_unique);
 
        copy_generic_path_info(&join_plan->join.plan, &best_path->path);
 
@@ -3688,10 +3885,14 @@ create_mergejoin_plan(PlannerInfo *root,
        Oid                *mergecollations;
        int                *mergestrategies;
        bool       *mergenullsfirst;
+       PathKey    *opathkey;
+       EquivalenceClass *opeclass;
        int                     i;
        ListCell   *lc;
        ListCell   *lop;
        ListCell   *lip;
+       Path       *outer_path = best_path->jpath.outerjoinpath;
+       Path       *inner_path = best_path->jpath.innerjoinpath;
 
        /*
         * MergeJoin can project, so we don't have to demand exact tlists from the
@@ -3700,10 +3901,10 @@ create_mergejoin_plan(PlannerInfo *root,
         * necessary.
         */
        outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath,
-                                        (best_path->outersortkeys != NIL) ? CP_SMALL_TLIST : 0);
+                                                                        (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);
+                                                                        (best_path->innersortkeys != NIL) ? CP_SMALL_TLIST : 0);
 
        /* Sort join qual clauses into best execution order */
        /* NB: do NOT reorder the mergeclauses */
@@ -3714,6 +3915,7 @@ create_mergejoin_plan(PlannerInfo *root,
        if (IS_OUTER_JOIN(best_path->jpath.jointype))
        {
                extract_actual_join_clauses(joinclauses,
+                                                                       best_path->jpath.path.parent->relids,
                                                                        &joinclauses, &otherclauses);
        }
        else
@@ -3748,15 +3950,17 @@ create_mergejoin_plan(PlannerInfo *root,
         * outer_is_left status.
         */
        mergeclauses = get_switched_clauses(best_path->path_mergeclauses,
-                                                        best_path->jpath.outerjoinpath->parent->relids);
+                                                                               best_path->jpath.outerjoinpath->parent->relids);
 
        /*
         * Create explicit sort nodes for the outer and inner paths if necessary.
         */
        if (best_path->outersortkeys)
        {
+               Relids          outer_relids = outer_path->parent->relids;
                Sort       *sort = make_sort_from_pathkeys(outer_plan,
-                                                                                                  best_path->outersortkeys);
+                                                                                                  best_path->outersortkeys,
+                                                                                                  outer_relids);
 
                label_sort_with_costsize(root, sort, -1.0);
                outer_plan = (Plan *) sort;
@@ -3767,8 +3971,10 @@ create_mergejoin_plan(PlannerInfo *root,
 
        if (best_path->innersortkeys)
        {
+               Relids          inner_relids = inner_path->parent->relids;
                Sort       *sort = make_sort_from_pathkeys(inner_plan,
-                                                                                                  best_path->innersortkeys);
+                                                                                                  best_path->innersortkeys,
+                                                                                                  inner_relids);
 
                label_sort_with_costsize(root, sort, -1.0);
                inner_plan = (Plan *) sort;
@@ -3800,7 +4006,8 @@ create_mergejoin_plan(PlannerInfo *root,
         * Compute the opfamily/collation/strategy/nullsfirst arrays needed by the
         * executor.  The information is in the pathkeys for the two inputs, but
         * we need to be careful about the possibility of mergeclauses sharing a
-        * pathkey (compare find_mergeclauses_for_pathkeys()).
+        * pathkey, as well as the possibility that the inner pathkeys are not in
+        * an order matching the mergeclauses.
         */
        nClauses = list_length(mergeclauses);
        Assert(nClauses == list_length(best_path->path_mergeclauses));
@@ -3809,19 +4016,19 @@ create_mergejoin_plan(PlannerInfo *root,
        mergestrategies = (int *) palloc(nClauses * sizeof(int));
        mergenullsfirst = (bool *) palloc(nClauses * sizeof(bool));
 
+       opathkey = NULL;
+       opeclass = NULL;
        lop = list_head(outerpathkeys);
        lip = list_head(innerpathkeys);
        i = 0;
        foreach(lc, best_path->path_mergeclauses)
        {
-               RestrictInfo *rinfo = castNode(RestrictInfo, lfirst(lc));
+               RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
                EquivalenceClass *oeclass;
                EquivalenceClass *ieclass;
-               PathKey    *opathkey;
-               PathKey    *ipathkey;
-               EquivalenceClass *opeclass;
-               EquivalenceClass *ipeclass;
-               ListCell   *l2;
+               PathKey    *ipathkey = NULL;
+               EquivalenceClass *ipeclass = NULL;
+               bool            first_inner_match = false;
 
                /* fetch outer/inner eclass from mergeclause */
                if (rinfo->outer_is_left)
@@ -3838,104 +4045,96 @@ create_mergejoin_plan(PlannerInfo *root,
                Assert(ieclass != NULL);
 
                /*
-                * For debugging purposes, we check that the eclasses match the paths'
-                * pathkeys.  In typical cases the merge clauses are one-to-one with
-                * the pathkeys, but when dealing with partially redundant query
-                * conditions, we might have clauses that re-reference earlier path
-                * keys.  The case that we need to reject is where a pathkey is
-                * entirely skipped over.
+                * We must identify the pathkey elements associated with this clause
+                * by matching the eclasses (which should give a unique match, since
+                * the pathkey lists should be canonical).  In typical cases the merge
+                * clauses are one-to-one with the pathkeys, but when dealing with
+                * partially redundant query conditions, things are more complicated.
+                *
+                * lop and lip reference the first as-yet-unmatched pathkey elements.
+                * If they're NULL then all pathkey elements have been matched.
                 *
-                * lop and lip reference the first as-yet-unused pathkey elements;
-                * it's okay to match them, or any element before them.  If they're
-                * NULL then we have found all pathkey elements to be used.
+                * The ordering of the outer pathkeys should match the mergeclauses,
+                * by construction (see find_mergeclauses_for_outer_pathkeys()). There
+                * could be more than one mergeclause for the same outer pathkey, but
+                * no pathkey may be entirely skipped over.
                 */
-               if (lop)
+               if (oeclass != opeclass)        /* multiple matches are not interesting */
                {
+                       /* doesn't match the current opathkey, so must match the next */
+                       if (lop == NULL)
+                               elog(ERROR, "outer pathkeys do not match mergeclauses");
                        opathkey = (PathKey *) lfirst(lop);
                        opeclass = opathkey->pk_eclass;
-                       if (oeclass == opeclass)
-                       {
-                               /* fast path for typical case */
-                               lop = lnext(lop);
-                       }
-                       else
-                       {
-                               /* redundant clauses ... must match something before lop */
-                               foreach(l2, outerpathkeys)
-                               {
-                                       if (l2 == lop)
-                                               break;
-                                       opathkey = (PathKey *) lfirst(l2);
-                                       opeclass = opathkey->pk_eclass;
-                                       if (oeclass == opeclass)
-                                               break;
-                               }
-                               if (oeclass != opeclass)
-                                       elog(ERROR, "outer pathkeys do not match mergeclauses");
-                       }
-               }
-               else
-               {
-                       /* redundant clauses ... must match some already-used pathkey */
-                       opathkey = NULL;
-                       opeclass = NULL;
-                       foreach(l2, outerpathkeys)
-                       {
-                               opathkey = (PathKey *) lfirst(l2);
-                               opeclass = opathkey->pk_eclass;
-                               if (oeclass == opeclass)
-                                       break;
-                       }
-                       if (l2 == NULL)
+                       lop = lnext(lop);
+                       if (oeclass != opeclass)
                                elog(ERROR, "outer pathkeys do not match mergeclauses");
                }
 
+               /*
+                * The inner pathkeys likewise should not have skipped-over keys, but
+                * it's possible for a mergeclause to reference some earlier inner
+                * pathkey if we had redundant pathkeys.  For example we might have
+                * mergeclauses like "o.a = i.x AND o.b = i.y AND o.c = i.x".  The
+                * implied inner ordering is then "ORDER BY x, y, x", but the pathkey
+                * mechanism drops the second sort by x as redundant, and this code
+                * must cope.
+                *
+                * It's also possible for the implied inner-rel ordering to be like
+                * "ORDER BY x, y, x DESC".  We still drop the second instance of x as
+                * redundant; but this means that the sort ordering of a redundant
+                * inner pathkey should not be considered significant.  So we must
+                * detect whether this is the first clause matching an inner pathkey.
+                */
                if (lip)
                {
                        ipathkey = (PathKey *) lfirst(lip);
                        ipeclass = ipathkey->pk_eclass;
                        if (ieclass == ipeclass)
                        {
-                               /* fast path for typical case */
+                               /* successful first match to this inner pathkey */
                                lip = lnext(lip);
-                       }
-                       else
-                       {
-                               /* redundant clauses ... must match something before lip */
-                               foreach(l2, innerpathkeys)
-                               {
-                                       if (l2 == lip)
-                                               break;
-                                       ipathkey = (PathKey *) lfirst(l2);
-                                       ipeclass = ipathkey->pk_eclass;
-                                       if (ieclass == ipeclass)
-                                               break;
-                               }
-                               if (ieclass != ipeclass)
-                                       elog(ERROR, "inner pathkeys do not match mergeclauses");
+                               first_inner_match = true;
                        }
                }
-               else
+               if (!first_inner_match)
                {
-                       /* redundant clauses ... must match some already-used pathkey */
-                       ipathkey = NULL;
-                       ipeclass = NULL;
+                       /* redundant clause ... must match something before lip */
+                       ListCell   *l2;
+
                        foreach(l2, innerpathkeys)
                        {
+                               if (l2 == lip)
+                                       break;
                                ipathkey = (PathKey *) lfirst(l2);
                                ipeclass = ipathkey->pk_eclass;
                                if (ieclass == ipeclass)
                                        break;
                        }
-                       if (l2 == NULL)
+                       if (ieclass != ipeclass)
                                elog(ERROR, "inner pathkeys do not match mergeclauses");
                }
 
-               /* pathkeys should match each other too (more debugging) */
+               /*
+                * The pathkeys should always match each other as to opfamily and
+                * collation (which affect equality), but if we're considering a
+                * redundant inner pathkey, its sort ordering might not match.  In
+                * such cases we may ignore the inner pathkey's sort ordering and use
+                * the outer's.  (In effect, we're lying to the executor about the
+                * sort direction of this inner column, but it does not matter since
+                * the run-time row comparisons would only reach this column when
+                * there's equality for the earlier column containing the same eclass.
+                * There could be only one value in this column for the range of inner
+                * rows having a given value in the earlier column, so it does not
+                * matter which way we imagine this column to be ordered.)  But a
+                * non-redundant inner pathkey had better match outer's ordering too.
+                */
                if (opathkey->pk_opfamily != ipathkey->pk_opfamily ||
-                       opathkey->pk_eclass->ec_collation != ipathkey->pk_eclass->ec_collation ||
-                       opathkey->pk_strategy != ipathkey->pk_strategy ||
-                       opathkey->pk_nulls_first != ipathkey->pk_nulls_first)
+                       opathkey->pk_eclass->ec_collation != ipathkey->pk_eclass->ec_collation)
+                       elog(ERROR, "left and right pathkeys do not match in mergejoin");
+               if (first_inner_match &&
+                       (opathkey->pk_strategy != ipathkey->pk_strategy ||
+                        opathkey->pk_nulls_first != ipathkey->pk_nulls_first))
                        elog(ERROR, "left and right pathkeys do not match in mergejoin");
 
                /* OK, save info for executor */
@@ -3965,7 +4164,9 @@ create_mergejoin_plan(PlannerInfo *root,
                                                           mergenullsfirst,
                                                           outer_plan,
                                                           inner_plan,
-                                                          best_path->jpath.jointype);
+                                                          best_path->jpath.jointype,
+                                                          best_path->jpath.inner_unique,
+                                                          best_path->skip_mark_restore);
 
        /* Costs of sort and material steps are included in path cost already */
        copy_generic_path_info(&join_plan->join.plan, &best_path->jpath.path);
@@ -3988,8 +4189,6 @@ create_hashjoin_plan(PlannerInfo *root,
        Oid                     skewTable = InvalidOid;
        AttrNumber      skewColumn = InvalidAttrNumber;
        bool            skewInherit = false;
-       Oid                     skewColType = InvalidOid;
-       int32           skewColTypmod = -1;
 
        /*
         * HashJoin can project, so we don't have to demand exact tlists from the
@@ -3999,7 +4198,7 @@ create_hashjoin_plan(PlannerInfo *root,
         * 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);
+                                                                        (best_path->num_batches > 1) ? CP_SMALL_TLIST : 0);
 
        inner_plan = create_plan_recurse(root, best_path->jpath.innerjoinpath,
                                                                         CP_SMALL_TLIST);
@@ -4013,6 +4212,7 @@ create_hashjoin_plan(PlannerInfo *root,
        if (IS_OUTER_JOIN(best_path->jpath.jointype))
        {
                extract_actual_join_clauses(joinclauses,
+                                                                       best_path->jpath.path.parent->relids,
                                                                        &joinclauses, &otherclauses);
        }
        else
@@ -4046,7 +4246,7 @@ create_hashjoin_plan(PlannerInfo *root,
         * on the left.
         */
        hashclauses = get_switched_clauses(best_path->path_hashclauses,
-                                                        best_path->jpath.outerjoinpath->parent->relids);
+                                                                          best_path->jpath.outerjoinpath->parent->relids);
 
        /*
         * If there is a single join clause and we can identify the outer variable
@@ -4076,8 +4276,6 @@ create_hashjoin_plan(PlannerInfo *root,
                                skewTable = rte->relid;
                                skewColumn = var->varattno;
                                skewInherit = rte->inh;
-                               skewColType = var->vartype;
-                               skewColTypmod = var->vartypmod;
                        }
                }
        }
@@ -4088,9 +4286,7 @@ create_hashjoin_plan(PlannerInfo *root,
        hash_plan = make_hash(inner_plan,
                                                  skewTable,
                                                  skewColumn,
-                                                 skewInherit,
-                                                 skewColType,
-                                                 skewColTypmod);
+                                                 skewInherit);
 
        /*
         * Set Hash node's startup & total costs equal to total cost of input
@@ -4099,13 +4295,25 @@ create_hashjoin_plan(PlannerInfo *root,
        copy_plan_costsize(&hash_plan->plan, inner_plan);
        hash_plan->plan.startup_cost = hash_plan->plan.total_cost;
 
+       /*
+        * If parallel-aware, the executor will also need an estimate of the total
+        * number of rows expected from all participants so that it can size the
+        * shared hash table.
+        */
+       if (best_path->jpath.path.parallel_aware)
+       {
+               hash_plan->plan.parallel_aware = true;
+               hash_plan->rows_total = best_path->inner_rows_total;
+       }
+
        join_plan = make_hashjoin(tlist,
                                                          joinclauses,
                                                          otherclauses,
                                                          hashclauses,
                                                          outer_plan,
                                                          (Plan *) hash_plan,
-                                                         best_path->jpath.jointype);
+                                                         best_path->jpath.jointype,
+                                                         best_path->jpath.inner_unique);
 
        copy_generic_path_info(&join_plan->join.plan, &best_path->jpath.path);
 
@@ -4189,8 +4397,8 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root)
                 * rels, and then grab its PlaceHolderInfo to tell for sure.
                 */
                if (!bms_overlap(phv->phrels, root->curOuterRels) ||
-                 !bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at,
-                                                root->curOuterRels))
+                       !bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at,
+                                                  root->curOuterRels))
                {
                        /*
                         * We can't replace the whole PHV, but we might still need to
@@ -4358,7 +4566,7 @@ fix_indexqual_references(PlannerInfo *root, IndexPath *index_path)
 
        forboth(lcc, index_path->indexquals, lci, index_path->indexqualcols)
        {
-               RestrictInfo *rinfo = castNode(RestrictInfo, lfirst(lcc));
+               RestrictInfo *rinfo = lfirst_node(RestrictInfo, lcc);
                int                     indexcol = lfirst_int(lci);
                Node       *clause;
 
@@ -4778,7 +4986,7 @@ 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.
+ * Also copy the parallel-related flags, which the executor *will* use.
  */
 static void
 copy_generic_path_info(Plan *dest, Path *src)
@@ -4788,6 +4996,7 @@ copy_generic_path_info(Plan *dest, Path *src)
        dest->plan_rows = src->rows;
        dest->plan_width = src->pathtarget->width;
        dest->parallel_aware = src->parallel_aware;
+       dest->parallel_safe = src->parallel_safe;
 }
 
 /*
@@ -4803,6 +5012,8 @@ copy_plan_costsize(Plan *dest, Plan *src)
        dest->plan_width = src->plan_width;
        /* Assume the inserted node is not parallel-aware. */
        dest->parallel_aware = false;
+       /* Assume the inserted node is parallel-safe, if child plan is. */
+       dest->parallel_safe = src->parallel_safe;
 }
 
 /*
@@ -4832,11 +5043,12 @@ label_sort_with_costsize(PlannerInfo *root, Sort *plan, double limit_tuples)
        plan->plan.plan_rows = lefttree->plan_rows;
        plan->plan.plan_width = lefttree->plan_width;
        plan->plan.parallel_aware = false;
+       plan->plan.parallel_safe = lefttree->parallel_safe;
 }
 
 /*
  * bitmap_subplan_mark_shared
- *   Set isshared flag in bitmap subplan so that it will be created in
+ *      Set isshared flag in bitmap subplan so that it will be created in
  *      shared memory.
  */
 static void
@@ -4844,9 +5056,13 @@ bitmap_subplan_mark_shared(Plan *plan)
 {
        if (IsA(plan, BitmapAnd))
                bitmap_subplan_mark_shared(
-                                                               linitial(((BitmapAnd *) plan)->bitmapplans));
+                                                                  linitial(((BitmapAnd *) plan)->bitmapplans));
        else if (IsA(plan, BitmapOr))
+       {
                ((BitmapOr *) plan)->isshared = true;
+               bitmap_subplan_mark_shared(
+                                                                  linitial(((BitmapOr *) plan)->bitmapplans));
+       }
        else if (IsA(plan, BitmapIndexScan))
                ((BitmapIndexScan *) plan)->isshared = true;
        else
@@ -5120,6 +5336,26 @@ make_ctescan(List *qptlist,
        return node;
 }
 
+static NamedTuplestoreScan *
+make_namedtuplestorescan(List *qptlist,
+                                                List *qpqual,
+                                                Index scanrelid,
+                                                char *enrname)
+{
+       NamedTuplestoreScan *node = makeNode(NamedTuplestoreScan);
+       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->enrname = enrname;
+
+       return node;
+}
+
 static WorkTableScan *
 make_worktablescan(List *qptlist,
                                   List *qpqual,
@@ -5174,7 +5410,8 @@ make_foreignscan(List *qptlist,
 }
 
 static Append *
-make_append(List *appendplans, List *tlist, List *partitioned_rels)
+make_append(List *appendplans, int first_partial_plan,
+                       List *tlist, PartitionPruneInfo *partpruneinfo)
 {
        Append     *node = makeNode(Append);
        Plan       *plan = &node->plan;
@@ -5183,9 +5420,9 @@ make_append(List *appendplans, List *tlist, List *partitioned_rels)
        plan->qual = NIL;
        plan->lefttree = NULL;
        plan->righttree = NULL;
-       node->partitioned_rels = partitioned_rels;
        node->appendplans = appendplans;
-
+       node->first_partial_plan = first_partial_plan;
+       node->part_prune_info = partpruneinfo;
        return node;
 }
 
@@ -5278,7 +5515,8 @@ make_nestloop(List *tlist,
                          List *nestParams,
                          Plan *lefttree,
                          Plan *righttree,
-                         JoinType jointype)
+                         JoinType jointype,
+                         bool inner_unique)
 {
        NestLoop   *node = makeNode(NestLoop);
        Plan       *plan = &node->join.plan;
@@ -5288,6 +5526,7 @@ make_nestloop(List *tlist,
        plan->lefttree = lefttree;
        plan->righttree = righttree;
        node->join.jointype = jointype;
+       node->join.inner_unique = inner_unique;
        node->join.joinqual = joinclauses;
        node->nestParams = nestParams;
 
@@ -5301,7 +5540,8 @@ make_hashjoin(List *tlist,
                          List *hashclauses,
                          Plan *lefttree,
                          Plan *righttree,
-                         JoinType jointype)
+                         JoinType jointype,
+                         bool inner_unique)
 {
        HashJoin   *node = makeNode(HashJoin);
        Plan       *plan = &node->join.plan;
@@ -5312,6 +5552,7 @@ make_hashjoin(List *tlist,
        plan->righttree = righttree;
        node->hashclauses = hashclauses;
        node->join.jointype = jointype;
+       node->join.inner_unique = inner_unique;
        node->join.joinqual = joinclauses;
 
        return node;
@@ -5321,9 +5562,7 @@ static Hash *
 make_hash(Plan *lefttree,
                  Oid skewTable,
                  AttrNumber skewColumn,
-                 bool skewInherit,
-                 Oid skewColType,
-                 int32 skewColTypmod)
+                 bool skewInherit)
 {
        Hash       *node = makeNode(Hash);
        Plan       *plan = &node->plan;
@@ -5336,8 +5575,6 @@ make_hash(Plan *lefttree,
        node->skewTable = skewTable;
        node->skewColumn = skewColumn;
        node->skewInherit = skewInherit;
-       node->skewColType = skewColType;
-       node->skewColTypmod = skewColTypmod;
 
        return node;
 }
@@ -5353,7 +5590,9 @@ make_mergejoin(List *tlist,
                           bool *mergenullsfirst,
                           Plan *lefttree,
                           Plan *righttree,
-                          JoinType jointype)
+                          JoinType jointype,
+                          bool inner_unique,
+                          bool skip_mark_restore)
 {
        MergeJoin  *node = makeNode(MergeJoin);
        Plan       *plan = &node->join.plan;
@@ -5362,12 +5601,14 @@ make_mergejoin(List *tlist,
        plan->qual = otherclauses;
        plan->lefttree = lefttree;
        plan->righttree = righttree;
+       node->skip_mark_restore = skip_mark_restore;
        node->mergeclauses = mergeclauses;
        node->mergeFamilies = mergefamilies;
        node->mergeCollations = mergecollations;
        node->mergeStrategies = mergestrategies;
        node->mergeNullsFirst = mergenullsfirst;
        node->join.jointype = jointype;
+       node->join.inner_unique = inner_unique;
        node->join.joinqual = joinclauses;
 
        return node;
@@ -5413,7 +5654,7 @@ make_sort(Plan *lefttree, int numCols,
  *       'pathkeys' is the list of pathkeys by which the result is to be sorted
  *       'relids' identifies the child relation being sorted, if any
  *       'reqColIdx' is NULL or an array of required sort key column numbers
- *       'adjust_tlist_in_place' is TRUE if lefttree must be modified in-place
+ *       'adjust_tlist_in_place' is true if lefttree must be modified in-place
  *
  * We must convert the pathkey information into arrays of sort key column
  * numbers, sort operator OIDs, collation OIDs, and nulls-first flags,
@@ -5421,8 +5662,9 @@ make_sort(Plan *lefttree, int numCols,
  * the output parameters *p_numsortkeys etc.
  *
  * When looking for matches to an EquivalenceClass's members, we will only
- * consider child EC members if they match 'relids'.  This protects against
- * possible incorrect matches to child expressions that contain no Vars.
+ * consider child EC members if they belong to given 'relids'.  This protects
+ * against possible incorrect matches to child expressions that contain no
+ * Vars.
  *
  * If reqColIdx isn't NULL then it contains sort key column numbers that
  * we should match.  This is used when making child plans for a MergeAppend;
@@ -5433,7 +5675,7 @@ make_sort(Plan *lefttree, int numCols,
  * 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
+ * However, the caller can pass adjust_tlist_in_place = true to force the
  * lefttree tlist to be modified in-place regardless of whether the node type
  * can project --- we use this for fixing the tlist of MergeAppend itself.
  *
@@ -5577,11 +5819,11 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
                                        continue;
 
                                /*
-                                * Ignore child members unless they match the rel being
+                                * Ignore child members unless they belong to the rel being
                                 * sorted.
                                 */
                                if (em->em_is_child &&
-                                       !bms_equal(em->em_relids, relids))
+                                       !bms_is_subset(em->em_relids, relids))
                                        continue;
 
                                sortexpr = em->em_expr;
@@ -5612,7 +5854,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
                        {
                                /* copy needed so we don't modify input's tlist below */
                                tlist = copyObject(tlist);
-                               lefttree = inject_projection_plan(lefttree, tlist);
+                               lefttree = inject_projection_plan(lefttree, tlist,
+                                                                                                 lefttree->parallel_safe);
                        }
 
                        /* Don't bother testing is_projection_capable_plan again */
@@ -5626,7 +5869,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
                                                                  NULL,
                                                                  true);
                        tlist = lappend(tlist, tle);
-                       lefttree->targetlist = tlist;           /* just in case NIL before */
+                       lefttree->targetlist = tlist;   /* just in case NIL before */
                }
 
                /*
@@ -5638,7 +5881,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
                                                                         pk_datatype,
                                                                         pathkey->pk_strategy);
                if (!OidIsValid(sortop))        /* should not happen */
-                       elog(ERROR, "could not find member %d(%u,%u) of opfamily %u",
+                       elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
                                 pathkey->pk_strategy, pk_datatype, pk_datatype,
                                 pathkey->pk_opfamily);
 
@@ -5664,7 +5907,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
  * find_ec_member_for_tle
  *             Locate an EquivalenceClass member matching the given TLE, if any
  *
- * Child EC members are ignored unless they match 'relids'.
+ * Child EC members are ignored unless they belong to given 'relids'.
  */
 static EquivalenceMember *
 find_ec_member_for_tle(EquivalenceClass *ec,
@@ -5692,10 +5935,10 @@ find_ec_member_for_tle(EquivalenceClass *ec,
                        continue;
 
                /*
-                * Ignore child members unless they match the rel being sorted.
+                * Ignore child members unless they belong to the rel being sorted.
                 */
                if (em->em_is_child &&
-                       !bms_equal(em->em_relids, relids))
+                       !bms_is_subset(em->em_relids, relids))
                        continue;
 
                /* Match if same expression (after stripping relabel) */
@@ -5716,9 +5959,10 @@ 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
+ *       'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys)
+make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 {
        int                     numsortkeys;
        AttrNumber *sortColIdx;
@@ -5728,7 +5972,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys)
 
        /* Compute sort column info, and adjust lefttree as needed */
        lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
-                                                                                 NULL,
+                                                                                 relids,
                                                                                  NULL,
                                                                                  false,
                                                                                  &numsortkeys,
@@ -5891,6 +6135,7 @@ materialize_finished_plan(Plan *subplan)
        matplan->plan_rows = subplan->plan_rows;
        matplan->plan_width = subplan->plan_width;
        matplan->parallel_aware = false;
+       matplan->parallel_safe = subplan->parallel_safe;
 
        return matplan;
 }
@@ -5932,6 +6177,8 @@ make_windowagg(List *tlist, Index winref,
                           int partNumCols, AttrNumber *partColIdx, Oid *partOperators,
                           int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators,
                           int frameOptions, Node *startOffset, Node *endOffset,
+                          Oid startInRangeFunc, Oid endInRangeFunc,
+                          Oid inRangeColl, bool inRangeAsc, bool inRangeNullsFirst,
                           Plan *lefttree)
 {
        WindowAgg  *node = makeNode(WindowAgg);
@@ -5947,6 +6194,11 @@ make_windowagg(List *tlist, Index winref,
        node->frameOptions = frameOptions;
        node->startOffset = startOffset;
        node->endOffset = endOffset;
+       node->startInRangeFunc = startInRangeFunc;
+       node->endInRangeFunc = endInRangeFunc;
+       node->inRangeColl = inRangeColl;
+       node->inRangeAsc = inRangeAsc;
+       node->inRangeNullsFirst = inRangeNullsFirst;
 
        plan->targetlist = tlist;
        plan->lefttree = lefttree;
@@ -6115,7 +6367,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
                                                                   pk_datatype,
                                                                   BTEqualStrategyNumber);
                if (!OidIsValid(eqop))  /* should not happen */
-                       elog(ERROR, "could not find member %d(%u,%u) of opfamily %u",
+                       elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
                                 BTEqualStrategyNumber, pk_datatype, pk_datatype,
                                 pathkey->pk_opfamily);
 
@@ -6136,6 +6388,7 @@ static Gather *
 make_gather(List *qptlist,
                        List *qpqual,
                        int nworkers,
+                       int rescan_param,
                        bool single_copy,
                        Plan *subplan)
 {
@@ -6147,8 +6400,10 @@ make_gather(List *qptlist,
        plan->lefttree = subplan;
        plan->righttree = NULL;
        node->num_workers = nworkers;
+       node->rescan_param = rescan_param;
        node->single_copy = single_copy;
        node->invisible = false;
+       node->initParam = NULL;
 
        return node;
 }
@@ -6180,7 +6435,6 @@ make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
         * convert SortGroupClause list into arrays of attr indexes and equality
         * operators, as wanted by executor
         */
-       Assert(numCols > 0);
        dupColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
        dupOperators = (Oid *) palloc(sizeof(Oid) * numCols);
 
@@ -6296,8 +6550,9 @@ make_project_set(List *tlist,
 static ModifyTable *
 make_modifytable(PlannerInfo *root,
                                 CmdType operation, bool canSetTag,
-                                Index nominalRelation, List *partitioned_rels,
-                                List *resultRelations, List *subplans,
+                                Index nominalRelation, Index rootRelation,
+                                bool partColsUpdated,
+                                List *resultRelations, List *subplans, List *subroots,
                                 List *withCheckOptionLists, List *returningLists,
                                 List *rowMarks, OnConflictExpr *onconflict, int epqParam)
 {
@@ -6305,9 +6560,11 @@ make_modifytable(PlannerInfo *root,
        List       *fdw_private_list;
        Bitmapset  *direct_modify_plans;
        ListCell   *lc;
+       ListCell   *lc2;
        int                     i;
 
        Assert(list_length(resultRelations) == list_length(subplans));
+       Assert(list_length(resultRelations) == list_length(subroots));
        Assert(withCheckOptionLists == NIL ||
                   list_length(resultRelations) == list_length(withCheckOptionLists));
        Assert(returningLists == NIL ||
@@ -6322,9 +6579,11 @@ make_modifytable(PlannerInfo *root,
        node->operation = operation;
        node->canSetTag = canSetTag;
        node->nominalRelation = nominalRelation;
-       node->partitioned_rels = partitioned_rels;
+       node->rootRelation = rootRelation;
+       node->partColsUpdated = partColsUpdated;
        node->resultRelations = resultRelations;
        node->resultRelIndex = -1;      /* will be set correctly in setrefs.c */
+       node->rootResultRelIndex = -1;  /* will be set correctly in setrefs.c */
        node->plans = subplans;
        if (!onconflict)
        {
@@ -6364,9 +6623,10 @@ make_modifytable(PlannerInfo *root,
        fdw_private_list = NIL;
        direct_modify_plans = NULL;
        i = 0;
-       foreach(lc, resultRelations)
+       forboth(lc, resultRelations, lc2, subroots)
        {
                Index           rti = lfirst_int(lc);
+               PlannerInfo *subroot = lfirst_node(PlannerInfo, lc2);
                FdwRoutine *fdwroutine;
                List       *fdw_private;
                bool            direct_modify;
@@ -6378,16 +6638,16 @@ make_modifytable(PlannerInfo *root,
                 * so it's not a baserel; and there are also corner cases for
                 * updatable views where the target rel isn't a baserel.)
                 */
-               if (rti < root->simple_rel_array_size &&
-                       root->simple_rel_array[rti] != NULL)
+               if (rti < subroot->simple_rel_array_size &&
+                       subroot->simple_rel_array[rti] != NULL)
                {
-                       RelOptInfo *resultRel = root->simple_rel_array[rti];
+                       RelOptInfo *resultRel = subroot->simple_rel_array[rti];
 
                        fdwroutine = resultRel->fdwroutine;
                }
                else
                {
-                       RangeTblEntry *rte = planner_rt_fetch(rti, root);
+                       RangeTblEntry *rte = planner_rt_fetch(rti, subroot);
 
                        Assert(rte->rtekind == RTE_RELATION);
                        if (rte->relkind == RELKIND_FOREIGN_TABLE)
@@ -6397,8 +6657,10 @@ make_modifytable(PlannerInfo *root,
                }
 
                /*
-                * If the target foreign table has any row-level triggers, we can't
-                * modify the foreign table directly.
+                * Try to modify the foreign table directly if (1) the FDW provides
+                * callback functions needed for that, (2) there are no row-level
+                * triggers on the foreign table, and (3) there are no WITH CHECK
+                * OPTIONs from parent views.
                 */
                direct_modify = false;
                if (fdwroutine != NULL &&
@@ -6406,15 +6668,16 @@ make_modifytable(PlannerInfo *root,
                        fdwroutine->BeginDirectModify != NULL &&
                        fdwroutine->IterateDirectModify != NULL &&
                        fdwroutine->EndDirectModify != NULL &&
-                       !has_row_triggers(root, rti, operation))
-                       direct_modify = fdwroutine->PlanDirectModify(root, node, rti, i);
+                       withCheckOptionLists == NIL &&
+                       !has_row_triggers(subroot, rti, operation))
+                       direct_modify = fdwroutine->PlanDirectModify(subroot, 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);
+                       fdw_private = fdwroutine->PlanForeignModify(subroot, node, rti, i);
                else
                        fdw_private = NIL;
                fdw_private_list = lappend(fdw_private_list, fdw_private);