]> granicus.if.org Git - postgresql/blobdiff - src/backend/optimizer/plan/createplan.c
Repair issues with faulty generation of merge-append plans.
[postgresql] / src / backend / optimizer / plan / createplan.c
index bd19f43d586db497514c17630644788d0707a6d2..270c11901b78f569fa1df95c0702ca38c85fb7e9 100644 (file)
@@ -5,7 +5,7 @@
  *       Planning is complete, we just need to convert the selected
  *       Path into a Plan.
  *
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
@@ -19,7 +19,6 @@
 #include <limits.h>
 #include <math.h>
 
-#include "access/stratnum.h"
 #include "access/sysattr.h"
 #include "catalog/pg_class.h"
 #include "foreign/fdwapi.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
+#include "optimizer/optimizer.h"
+#include "optimizer/paramassign.h"
 #include "optimizer/paths.h"
 #include "optimizer/placeholder.h"
 #include "optimizer/plancat.h"
 #include "optimizer/planmain.h"
-#include "optimizer/planner.h"
-#include "optimizer/predtest.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/subselect.h"
 #include "optimizer/tlist.h"
-#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,
@@ -78,16 +81,22 @@ static List *get_gating_quals(PlannerInfo *root, List *quals);
 static Plan *create_gating_plan(PlannerInfo *root, Path *path, Plan *plan,
                                   List *gating_quals);
 static Plan *create_join_plan(PlannerInfo *root, JoinPath *best_path);
-static Plan *create_append_plan(PlannerInfo *root, AppendPath *best_path);
-static Plan *create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path);
-static Result *create_result_plan(PlannerInfo *root, ResultPath *best_path);
+static Plan *create_append_plan(PlannerInfo *root, AppendPath *best_path,
+                                  int flags);
+static Plan *create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
+                                                int flags);
+static Result *create_group_result_plan(PlannerInfo *root,
+                                                GroupResultPath *best_path);
+static ProjectSet *create_project_set_plan(PlannerInfo *root, ProjectSetPath *best_path);
 static Material *create_material_plan(PlannerInfo *root, MaterialPath *best_path,
                                         int flags);
 static Plan *create_unique_plan(PlannerInfo *root, UniquePath *best_path,
                                   int flags);
 static Gather *create_gather_plan(PlannerInfo *root, GatherPath *best_path);
-static Plan *create_projection_plan(PlannerInfo *root, ProjectionPath *best_path);
-static Plan *inject_projection_plan(Plan *subplan, List *tlist);
+static 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,
@@ -99,15 +108,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);
@@ -124,6 +124,7 @@ static BitmapHeapScan *create_bitmap_scan_plan(PlannerInfo *root,
                                                List *tlist, List *scan_clauses);
 static Plan *create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
                                          List **qual, List **indexqual, List **indexECs);
+static void bitmap_subplan_mark_shared(Plan *plan);
 static TidScan *create_tidscan_plan(PlannerInfo *root, TidPath *best_path,
                                        List *tlist, List *scan_clauses);
 static SubqueryScan *create_subqueryscan_plan(PlannerInfo *root,
@@ -133,8 +134,14 @@ static FunctionScan *create_functionscan_plan(PlannerInfo *root, Path *best_path
                                                 List *tlist, List *scan_clauses);
 static ValuesScan *create_valuesscan_plan(PlannerInfo *root, Path *best_path,
                                           List *tlist, List *scan_clauses);
+static TableFuncScan *create_tablefuncscan_plan(PlannerInfo *root, Path *best_path,
+                                                 List *tlist, List *scan_clauses);
 static CteScan *create_ctescan_plan(PlannerInfo *root, Path *best_path,
                                        List *tlist, List *scan_clauses);
+static NamedTuplestoreScan *create_namedtuplestorescan_plan(PlannerInfo *root,
+                                                               Path *best_path, List *tlist, List *scan_clauses);
+static Result *create_resultscan_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,
@@ -147,10 +154,13 @@ static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path)
 static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path);
 static Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
 static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root);
-static void process_subquery_nestloop_params(PlannerInfo *root,
-                                                                List *subplan_params);
-static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path);
+static void fix_indexqual_references(PlannerInfo *root, IndexPath *index_path,
+                                                List **stripped_indexquals_p,
+                                                List **fixed_indexquals_p);
 static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path);
+static Node *fix_indexqual_clause(PlannerInfo *root,
+                                        IndexOptInfo *index, int indexcol,
+                                        Node *clause, List *indexcolnos);
 static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol);
 static List *get_switched_clauses(List *clauses, Relids outerrelids);
 static List *order_qual_clauses(PlannerInfo *root, List *clauses);
@@ -189,11 +199,14 @@ static FunctionScan *make_functionscan(List *qptlist, List *qpqual,
                                  Index scanrelid, List *functions, bool funcordinality);
 static ValuesScan *make_valuesscan(List *qptlist, List *qpqual,
                                Index scanrelid, List *values_lists);
+static TableFuncScan *make_tablefuncscan(List *qptlist, List *qpqual,
+                                  Index scanrelid, TableFunc *tablefunc);
 static CteScan *make_ctescan(List *qptlist, List *qpqual,
                         Index scanrelid, int ctePlanId, int cteParam);
+static 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);
 static RecursiveUnion *make_recursive_union(List *tlist,
                                         Plan *lefttree,
                                         Plan *righttree,
@@ -205,18 +218,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,
@@ -225,7 +236,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);
@@ -241,35 +253,42 @@ 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);
 static Material *make_material(Plan *lefttree);
 static WindowAgg *make_windowagg(List *tlist, Index winref,
-                          int partNumCols, AttrNumber *partColIdx, Oid *partOperators,
-                          int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators,
+                          int partNumCols, AttrNumber *partColIdx, Oid *partOperators, Oid *partCollations,
+                          int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators, Oid *ordCollations,
                           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,
+                  AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
                   Plan *lefttree);
 static Unique *make_unique_from_sortclauses(Plan *lefttree, List *distinctList);
 static Unique *make_unique_from_pathkeys(Plan *lefttree,
                                                  List *pathkeys, int numCols);
 static Gather *make_gather(List *qptlist, List *qpqual,
-                       int nworkers, bool single_copy, Plan *subplan);
+                       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);
 static LockRows *make_lockrows(Plan *lefttree, List *rowMarks, int epqParam);
 static Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
+static ProjectSet *make_project_set(List *tlist, Plan *subplan);
 static ModifyTable *make_modifytable(PlannerInfo *root,
                                 CmdType operation, bool canSetTag,
-                                Index nominalRelation,
-                                List *resultRelations, List *subplans,
+                                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,
+                                                GatherMergePath *best_path);
 
 
 /*
@@ -295,7 +314,7 @@ create_plan(PlannerInfo *root, Path *best_path)
        /* plan_params should not be in use in current query level */
        Assert(root->plan_params == NIL);
 
-       /* Initialize this module's private workspace in PlannerInfo */
+       /* Initialize this module's workspace in PlannerInfo */
        root->curOuterRels = NULL;
        root->curOuterParams = NIL;
 
@@ -314,16 +333,13 @@ create_plan(PlannerInfo *root, Path *best_path)
 
        /*
         * Attach any initPlans created in this query level to the topmost plan
-        * node.  (The initPlans could actually go in any plan node at or above
-        * where they're referenced, but there seems no reason to put them any
-        * lower than the topmost node for the query level.)
+        * node.  (In principle the initplans could go in any plan node at or
+        * above where they're referenced, but there seems no reason to put them
+        * any lower than the topmost node for the query level.  Also, see
+        * comments for SS_finalize_plan before you try to change this.)
         */
        SS_attach_initplans(root, plan);
 
-       /* Update parallel safety information if needed. */
-       if (!best_path->parallel_safe)
-               root->glob->wholePlanParallelSafe = false;
-
        /* Check we successfully assigned all NestLoopParams to plan nodes */
        if (root->curOuterParams != NIL)
                elog(ERROR, "failed to assign all NestLoopParams to plan nodes");
@@ -346,6 +362,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:
@@ -356,9 +375,11 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
                case T_TidScan:
                case T_SubqueryScan:
                case T_FunctionScan:
+               case T_TableFuncScan:
                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);
@@ -371,30 +392,42 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
                        break;
                case T_Append:
                        plan = create_append_plan(root,
-                                                                         (AppendPath *) best_path);
+                                                                         (AppendPath *) best_path,
+                                                                         flags);
                        break;
                case T_MergeAppend:
                        plan = create_merge_append_plan(root,
-                                                                                       (MergeAppendPath *) best_path);
+                                                                                       (MergeAppendPath *) best_path,
+                                                                                       flags);
                        break;
                case T_Result:
                        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 if (IsA(best_path, GroupResultPath))
+                       {
+                               plan = (Plan *) create_group_result_plan(root,
+                                                                                                                (GroupResultPath *) best_path);
                        }
                        else
                        {
-                               Assert(IsA(best_path, ResultPath));
-                               plan = (Plan *) create_result_plan(root,
-                                                                                                  (ResultPath *) best_path);
+                               /* Simple RTE_RESULT base relation */
+                               Assert(IsA(best_path, Path));
+                               plan = create_scan_plan(root, best_path, flags);
                        }
                        break;
+               case T_ProjectSet:
+                       plan = (Plan *) create_project_set_plan(root,
+                                                                                                       (ProjectSetPath *) best_path);
+                       break;
                case T_Material:
                        plan = (Plan *) create_material_plan(root,
                                                                                                 (MaterialPath *) best_path,
@@ -404,7 +437,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
@@ -431,7 +464,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));
@@ -441,7 +474,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,
@@ -450,7 +483,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,
@@ -459,13 +492,17 @@ 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,
                                                                                          (LimitPath *) best_path,
                                                                                          flags);
                        break;
+               case T_GatherMerge:
+                       plan = (Plan *) create_gather_merge_plan(root,
+                                                                                                        (GatherMergePath *) best_path);
+                       break;
                default:
                        elog(ERROR, "unrecognized node type: %d",
                                 (int) best_path->pathtype);
@@ -505,8 +542,7 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
        {
                case T_IndexScan:
                case T_IndexOnlyScan:
-                       Assert(IsA(best_path, IndexPath));
-                       scan_clauses = ((IndexPath *) best_path)->indexinfo->indrestrictinfo;
+                       scan_clauses = castNode(IndexPath, best_path)->indexinfo->indrestrictinfo;
                        break;
                default:
                        scan_clauses = rel->baserestrictinfo;
@@ -537,15 +573,28 @@ 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)
                {
                        /* For index-only scan, the preferred tlist is the index's */
                        tlist = copyObject(((IndexPath *) best_path)->indexinfo->indextlist);
-                       /* Transfer any sortgroupref data to the replacement tlist */
-                       apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
+
+                       /*
+                        * Transfer sortgroupref data to the replacement tlist, if
+                        * requested (use_physical_tlist checked that this will work).
+                        */
+                       if (flags & CP_LABEL_TLIST)
+                               apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
                }
                else
                {
@@ -557,8 +606,9 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
                        }
                        else
                        {
-                               /* Transfer any sortgroupref data to the replacement tlist */
-                               apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
+                               /* As above, transfer sortgroupref data to replacement tlist */
+                               if (flags & CP_LABEL_TLIST)
+                                       apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
                        }
                }
        }
@@ -601,7 +651,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;
@@ -615,7 +665,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;
@@ -627,6 +677,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
                                                                                                         scan_clauses);
                        break;
 
+               case T_TableFuncScan:
+                       plan = (Plan *) create_tablefuncscan_plan(root,
+                                                                                                         best_path,
+                                                                                                         tlist,
+                                                                                                         scan_clauses);
+                       break;
+
                case T_ValuesScan:
                        plan = (Plan *) create_valuesscan_plan(root,
                                                                                                   best_path,
@@ -641,6 +698,20 @@ 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_Result:
+                       plan = (Plan *) create_resultscan_plan(root,
+                                                                                                  best_path,
+                                                                                                  tlist,
+                                                                                                  scan_clauses);
+                       break;
+
                case T_WorkTableScan:
                        plan = (Plan *) create_worktablescan_plan(root,
                                                                                                          best_path,
@@ -741,11 +812,12 @@ use_physical_tlist(PlannerInfo *root, Path *path, int flags)
 
        /*
         * We can do this for real relation scans, subquery scans, function scans,
-        * values scans, and CTE scans (but not for, eg, joins).
+        * tablefunc scans, values scans, and CTE scans (but not for, eg, joins).
         */
        if (rel->rtekind != RTE_RELATION &&
                rel->rtekind != RTE_SUBQUERY &&
                rel->rtekind != RTE_FUNCTION &&
+               rel->rtekind != RTE_TABLEFUNC &&
                rel->rtekind != RTE_VALUES &&
                rel->rtekind != RTE_CTE)
                return false;
@@ -758,6 +830,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
@@ -853,9 +943,26 @@ create_gating_plan(PlannerInfo *root, Path *path, Plan *plan,
                                   List *gating_quals)
 {
        Plan       *gplan;
+       Plan       *splan;
 
        Assert(gating_quals);
 
+       /*
+        * We might have a trivial Result plan already.  Stacking one Result atop
+        * another is silly, so if that applies, just discard the input plan.
+        * (We're assuming its targetlist is uninteresting; it should be either
+        * the same as the result of build_path_tlist, or a simplified version.)
+        */
+       splan = plan;
+       if (IsA(plan, Result))
+       {
+               Result     *rplan = (Result *) plan;
+
+               if (rplan->plan.lefttree == NULL &&
+                       rplan->resconstantqual == NULL)
+                       splan = NULL;
+       }
+
        /*
         * Since we need a Result node anyway, always return the path's requested
         * tlist; that's never a wrong choice, even if the parent node didn't ask
@@ -863,7 +970,7 @@ create_gating_plan(PlannerInfo *root, Path *path, Plan *plan,
         */
        gplan = (Plan *) make_result(build_path_tlist(root, path),
                                                                 (Node *) gating_quals,
-                                                                plan);
+                                                                splan);
 
        /*
         * Notice that we don't change cost or size estimates when doing gating.
@@ -879,6 +986,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;
 }
 
@@ -934,7 +1044,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;
@@ -948,12 +1058,22 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path)
  *       Returns a Plan node.
  */
 static Plan *
-create_append_plan(PlannerInfo *root, AppendPath *best_path)
+create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 {
        Append     *plan;
        List       *tlist = build_path_tlist(root, &best_path->path);
+       int                     orig_tlist_length = list_length(tlist);
+       bool            tlist_was_changed = false;
+       List       *pathkeys = best_path->path.pathkeys;
        List       *subplans = NIL;
        ListCell   *subpaths;
+       RelOptInfo *rel = best_path->path.parent;
+       PartitionPruneInfo *partpruneinfo = NULL;
+       int                     nodenumsortkeys = 0;
+       AttrNumber *nodeSortColIdx = NULL;
+       Oid                *nodeSortOperators = NULL;
+       Oid                *nodeCollations = NULL;
+       bool       *nodeNullsFirst = NULL;
 
        /*
         * The subpaths list could be empty, if every child was proven empty by
@@ -962,7 +1082,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
         *
         * Note that an AppendPath with no members is also generated in certain
         * cases where there was no appending construct at all, but we know the
-        * relation is empty (see set_dummy_rel_pathlist).
+        * relation is empty (see set_dummy_rel_pathlist and mark_dummy_rel).
         */
        if (best_path->subpaths == NIL)
        {
@@ -979,6 +1099,43 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
                return plan;
        }
 
+       /*
+        * Otherwise build an Append plan.  Note that if there's just one child,
+        * the Append is pretty useless; but we wait till setrefs.c to get rid of
+        * it.  Doing so here doesn't work because the varno of the child scan
+        * plan won't match the parent-rel Vars it'll be asked to emit.
+        *
+        * We don't have the actual creation of the Append node split out into a
+        * separate make_xxx function.  This is because we want to run
+        * prepare_sort_from_pathkeys on it before we do so on the individual
+        * child plans, to make cross-checking the sort info easier.
+        */
+       plan = makeNode(Append);
+       plan->plan.targetlist = tlist;
+       plan->plan.qual = NIL;
+       plan->plan.lefttree = NULL;
+       plan->plan.righttree = NULL;
+
+       if (pathkeys != NIL)
+       {
+               /*
+                * Compute sort column info, and adjust the Append's tlist as needed.
+                * Because we pass adjust_tlist_in_place = true, we may ignore the
+                * function result; it must be the same plan node.  However, we then
+                * need to detect whether any tlist entries were added.
+                */
+               (void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
+                                                                                 best_path->path.parent->relids,
+                                                                                 NULL,
+                                                                                 true,
+                                                                                 &nodenumsortkeys,
+                                                                                 &nodeSortColIdx,
+                                                                                 &nodeSortOperators,
+                                                                                 &nodeCollations,
+                                                                                 &nodeNullsFirst);
+               tlist_was_changed = (orig_tlist_length != list_length(plan->plan.targetlist));
+       }
+
        /* Build the plan for each child */
        foreach(subpaths, best_path->subpaths)
        {
@@ -988,21 +1145,118 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
                /* Must insist that all children return the same tlist */
                subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
 
+               /*
+                * For ordered Appends, we must insert a Sort node if subplan isn't
+                * sufficiently ordered.
+                */
+               if (pathkeys != NIL)
+               {
+                       int                     numsortkeys;
+                       AttrNumber *sortColIdx;
+                       Oid                *sortOperators;
+                       Oid                *collations;
+                       bool       *nullsFirst;
+
+                       /*
+                        * Compute sort column info, and adjust subplan's tlist as needed.
+                        * We must apply prepare_sort_from_pathkeys even to subplans that
+                        * don't need an explicit sort, to make sure they are returning
+                        * the same sort key columns the Append expects.
+                        */
+                       subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+                                                                                                subpath->parent->relids,
+                                                                                                nodeSortColIdx,
+                                                                                                false,
+                                                                                                &numsortkeys,
+                                                                                                &sortColIdx,
+                                                                                                &sortOperators,
+                                                                                                &collations,
+                                                                                                &nullsFirst);
+
+                       /*
+                        * Check that we got the same sort key information.  We just
+                        * Assert that the sortops match, since those depend only on the
+                        * pathkeys; but it seems like a good idea to check the sort
+                        * column numbers explicitly, to ensure the tlists match up.
+                        */
+                       Assert(numsortkeys == nodenumsortkeys);
+                       if (memcmp(sortColIdx, nodeSortColIdx,
+                                          numsortkeys * sizeof(AttrNumber)) != 0)
+                               elog(ERROR, "Append child's targetlist doesn't match Append");
+                       Assert(memcmp(sortOperators, nodeSortOperators,
+                                                 numsortkeys * sizeof(Oid)) == 0);
+                       Assert(memcmp(collations, nodeCollations,
+                                                 numsortkeys * sizeof(Oid)) == 0);
+                       Assert(memcmp(nullsFirst, nodeNullsFirst,
+                                                 numsortkeys * sizeof(bool)) == 0);
+
+                       /* Now, insert a Sort node if subplan isn't sufficiently ordered */
+                       if (!pathkeys_contained_in(pathkeys, subpath->pathkeys))
+                       {
+                               Sort       *sort = make_sort(subplan, numsortkeys,
+                                                                                        sortColIdx, sortOperators,
+                                                                                        collations, nullsFirst);
+
+                               label_sort_with_costsize(root, sort, best_path->limit_tuples);
+                               subplan = (Plan *) sort;
+                       }
+               }
+
                subplans = lappend(subplans, subplan);
        }
 
        /*
-        * 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
-        * not work because the varno of the child scan plan won't match the
-        * parent-rel Vars it'll be asked to emit.
+        * 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);
+       }
 
-       plan = make_append(subplans, tlist);
+       plan->appendplans = subplans;
+       plan->first_partial_plan = best_path->first_partial_path;
+       plan->part_prune_info = partpruneinfo;
 
        copy_generic_path_info(&plan->plan, (Path *) best_path);
 
-       return (Plan *) plan;
+       /*
+        * If prepare_sort_from_pathkeys added sort columns, but we were told to
+        * produce either the exact tlist or a narrow tlist, we should get rid of
+        * the sort columns again.  We must inject a projection node to do so.
+        */
+       if (tlist_was_changed && (flags & (CP_EXACT_TLIST | CP_SMALL_TLIST)))
+       {
+               tlist = list_truncate(list_copy(plan->plan.targetlist),
+                                                         orig_tlist_length);
+               return inject_projection_plan((Plan *) plan, tlist,
+                                                                         plan->plan.parallel_safe);
+       }
+       else
+               return (Plan *) plan;
 }
 
 /*
@@ -1013,14 +1267,19 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
  *       Returns a Plan node.
  */
 static Plan *
-create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
+create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
+                                                int flags)
 {
        MergeAppend *node = makeNode(MergeAppend);
        Plan       *plan = &node->plan;
        List       *tlist = build_path_tlist(root, &best_path->path);
+       int                     orig_tlist_length = list_length(tlist);
+       bool            tlist_was_changed;
        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
@@ -1034,7 +1293,12 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
        plan->lefttree = NULL;
        plan->righttree = NULL;
 
-       /* Compute sort column info, and adjust MergeAppend's tlist as needed */
+       /*
+        * Compute sort column info, and adjust MergeAppend's tlist as needed.
+        * Because we pass adjust_tlist_in_place = true, we may ignore the
+        * function result; it must be the same plan node.  However, we then need
+        * to detect whether any tlist entries were added.
+        */
        (void) prepare_sort_from_pathkeys(plan, pathkeys,
                                                                          best_path->path.parent->relids,
                                                                          NULL,
@@ -1044,6 +1308,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
                                                                          &node->sortOperators,
                                                                          &node->collations,
                                                                          &node->nullsFirst);
+       tlist_was_changed = (orig_tlist_length != list_length(plan->targetlist));
 
        /*
         * Now prepare the child plans.  We must apply prepare_sort_from_pathkeys
@@ -1106,21 +1371,63 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *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);
+       }
+
        node->mergeplans = subplans;
+       node->part_prune_info = partpruneinfo;
 
-       return (Plan *) node;
+       /*
+        * If prepare_sort_from_pathkeys added sort columns, but we were told to
+        * produce either the exact tlist or a narrow tlist, we should get rid of
+        * the sort columns again.  We must inject a projection node to do so.
+        */
+       if (tlist_was_changed && (flags & (CP_EXACT_TLIST | CP_SMALL_TLIST)))
+       {
+               tlist = list_truncate(list_copy(plan->targetlist), orig_tlist_length);
+               return inject_projection_plan(plan, tlist, plan->parallel_safe);
+       }
+       else
+               return plan;
 }
 
 /*
- * create_result_plan
+ * create_group_result_plan
  *       Create a Result plan for 'best_path'.
- *       This is only used for degenerate cases, such as a query with an empty
- *       jointree.
+ *       This is only used for degenerate grouping cases.
  *
  *       Returns a Plan node.
  */
 static Result *
-create_result_plan(PlannerInfo *root, ResultPath *best_path)
+create_group_result_plan(PlannerInfo *root, GroupResultPath *best_path)
 {
        Result     *plan;
        List       *tlist;
@@ -1138,6 +1445,31 @@ create_result_plan(PlannerInfo *root, ResultPath *best_path)
        return plan;
 }
 
+/*
+ * create_project_set_plan
+ *       Create a ProjectSet plan for 'best_path'.
+ *
+ *       Returns a Plan node.
+ */
+static ProjectSet *
+create_project_set_plan(PlannerInfo *root, ProjectSetPath *best_path)
+{
+       ProjectSet *plan;
+       Plan       *subplan;
+       List       *tlist;
+
+       /* Since we intend to project, we don't need to constrain child tlist */
+       subplan = create_plan_recurse(root, best_path->subpath, 0);
+
+       tlist = build_path_tlist(root, &best_path->path);
+
+       plan = make_project_set(tlist, subplan);
+
+       copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+       return plan;
+}
+
 /*
  * create_material_plan
  *       Create a Material plan for 'best_path' and (recursively) plans
@@ -1185,6 +1517,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
        bool            newitems;
        int                     numGroupCols;
        AttrNumber *groupColIdx;
+       Oid                *groupCollations;
        int                     groupColPos;
        ListCell   *l;
 
@@ -1221,7 +1554,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
 
        foreach(l, uniq_exprs)
        {
-               Node       *uniqexpr = lfirst(l);
+               Expr       *uniqexpr = lfirst(l);
                TargetEntry *tle;
 
                tle = tlist_member(uniqexpr, newtlist);
@@ -1237,19 +1570,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
@@ -1260,17 +1584,20 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
        newtlist = subplan->targetlist;
        numGroupCols = list_length(uniq_exprs);
        groupColIdx = (AttrNumber *) palloc(numGroupCols * sizeof(AttrNumber));
+       groupCollations = (Oid *) palloc(numGroupCols * sizeof(Oid));
 
        groupColPos = 0;
        foreach(l, uniq_exprs)
        {
-               Node       *uniqexpr = lfirst(l);
+               Expr       *uniqexpr = lfirst(l);
                TargetEntry *tle;
 
                tle = tlist_member(uniqexpr, newtlist);
                if (!tle)                               /* shouldn't happen */
                        elog(ERROR, "failed to find unique expression in subplan tlist");
-               groupColIdx[groupColPos++] = tle->resno;
+               groupColIdx[groupColPos] = tle->resno;
+               groupCollations[groupColPos] = exprCollation((Node *) tle->expr);
+               groupColPos++;
        }
 
        if (best_path->umethod == UNIQUE_PATH_HASH)
@@ -1304,12 +1631,11 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
                plan = (Plan *) make_agg(build_path_tlist(root, &best_path->path),
                                                                 NIL,
                                                                 AGG_HASHED,
-                                                                false,
-                                                                true,
-                                                                false,
+                                                                AGGSPLIT_SIMPLE,
                                                                 numGroupCols,
                                                                 groupColIdx,
                                                                 groupOperators,
+                                                                groupCollations,
                                                                 NIL,
                                                                 NIL,
                                                                 best_path->path.rows,
@@ -1342,7 +1668,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);
 
@@ -1394,7 +1720,8 @@ create_gather_plan(PlannerInfo *root, GatherPath *best_path)
 
        gather_plan = make_gather(tlist,
                                                          NIL,
-                                                         best_path->path.parallel_degree,
+                                                         best_path->num_workers,
+                                                         assign_special_exec_param(root),
                                                          best_path->single_copy,
                                                          subplan);
 
@@ -1406,51 +1733,153 @@ create_gather_plan(PlannerInfo *root, GatherPath *best_path)
        return gather_plan;
 }
 
+/*
+ * create_gather_merge_plan
+ *
+ *       Create a Gather Merge plan for 'best_path' and (recursively)
+ *       plans for its subpaths.
+ */
+static GatherMerge *
+create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
+{
+       GatherMerge *gm_plan;
+       Plan       *subplan;
+       List       *pathkeys = best_path->path.pathkeys;
+       List       *tlist = build_path_tlist(root, &best_path->path);
+
+       /* As with Gather, it's best to project away columns in the workers. */
+       subplan = create_plan_recurse(root, best_path->subpath, CP_EXACT_TLIST);
+
+       /* Create a shell for a GatherMerge plan. */
+       gm_plan = makeNode(GatherMerge);
+       gm_plan->plan.targetlist = tlist;
+       gm_plan->num_workers = best_path->num_workers;
+       copy_generic_path_info(&gm_plan->plan, &best_path->path);
+
+       /* Assign the rescan Param. */
+       gm_plan->rescan_param = assign_special_exec_param(root);
+
+       /* Gather Merge is pointless with no pathkeys; use Gather instead. */
+       Assert(pathkeys != NIL);
+
+       /* Compute sort column info, and adjust subplan's tlist as needed */
+       subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
+                                                                                best_path->subpath->parent->relids,
+                                                                                gm_plan->sortColIdx,
+                                                                                false,
+                                                                                &gm_plan->numCols,
+                                                                                &gm_plan->sortColIdx,
+                                                                                &gm_plan->sortOperators,
+                                                                                &gm_plan->collations,
+                                                                                &gm_plan->nullsFirst);
+
+
+       /* Now, insert a Sort node if subplan isn't sufficiently ordered */
+       if (!pathkeys_contained_in(pathkeys, best_path->subpath->pathkeys))
+               subplan = (Plan *) make_sort(subplan, gm_plan->numCols,
+                                                                        gm_plan->sortColIdx,
+                                                                        gm_plan->sortOperators,
+                                                                        gm_plan->collations,
+                                                                        gm_plan->nullsFirst);
+
+       /* Now insert the subplan under GatherMerge. */
+       gm_plan->plan.lefttree = subplan;
+
+       /* use parallel mode for parallel plans. */
+       root->glob->parallelModeNeeded = true;
+
+       return gm_plan;
+}
+
 /*
  * create_projection_plan
  *
- *       Create a Result node to do a projection step and (recursively) plans
- *       for its subpaths.
+ *       Create a plan tree to do a projection step and (recursively) plans
+ *       for its subpaths.  We may need a Result node for the projection,
+ *       but sometimes we can just let the subplan do the work.
  */
 static Plan *
-create_projection_plan(PlannerInfo *root, ProjectionPath *best_path)
+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.  There are several ways
-        * that this can happen.  For example, MergeAppend doesn't project, so we
-        * would have thought that we needed a projection to attach resjunk sort
-        * columns to its output ... but create_merge_append_plan might have
-        * added those same resjunk sort columns to both MergeAppend and its
-        * children.  Alternatively, apply_projection_to_path might have created
-        * a projection path as the subpath of a Gather node even though the
-        * subpath was projection-capable.  So, if the subpath is capable of
-        * projection or the desired tlist is the same expression-wise as the
-        * subplan's, just jam it in there.  We'll have charged for a Result that
-        * doesn't actually appear in the plan, but that's better than having a
-        * Result we don't need.
+        * 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;
                plan->targetlist = tlist;
 
-               /* Adjust cost to match what we thought during planning */
+               /* Label plan with the estimated costs we actually used */
                plan->startup_cost = best_path->path.startup_cost;
                plan->total_cost = best_path->path.total_cost;
-               /* ... but be careful not to munge subplan's parallel-aware flag */
+               plan->plan_rows = best_path->path.rows;
+               plan->plan_width = best_path->path.pathtarget->width;
+               plan->parallel_safe = best_path->path.parallel_safe;
+               /* ... but don't change subplan's parallel_aware flag */
        }
        else
        {
+               /* We need a Result node */
                plan = (Plan *) make_result(tlist, NULL, subplan);
 
                copy_generic_path_info(plan, (Path *) best_path);
@@ -1466,9 +1895,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;
 
@@ -1482,10 +1914,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
  *
@@ -1506,7 +1973,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);
 
@@ -1543,6 +2018,8 @@ create_group_plan(PlannerInfo *root, GroupPath *best_path)
                                          extract_grouping_cols(best_path->groupClause,
                                                                                        subplan->targetlist),
                                          extract_grouping_ops(best_path->groupClause),
+                                         extract_grouping_collations(best_path->groupClause,
+                                                                                                 subplan->targetlist),
                                          subplan);
 
        copy_generic_path_info(&plan->plan, (Path *) best_path);
@@ -1604,13 +2081,13 @@ create_agg_plan(PlannerInfo *root, AggPath *best_path)
 
        plan = make_agg(tlist, quals,
                                        best_path->aggstrategy,
-                                       best_path->combineStates,
-                                       best_path->finalizeAggs,
-                                       best_path->serialStates,
+                                       best_path->aggsplit,
                                        list_length(best_path->groupClause),
                                        extract_grouping_cols(best_path->groupClause,
                                                                                  subplan->targetlist),
                                        extract_grouping_ops(best_path->groupClause),
+                                       extract_grouping_collations(best_path->groupClause,
+                                                                                               subplan->targetlist),
                                        NIL,
                                        NIL,
                                        best_path->numGroups,
@@ -1672,18 +2149,15 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path)
 {
        Agg                *plan;
        Plan       *subplan;
-       List       *rollup_groupclauses = best_path->rollup_groupclauses;
-       List       *rollup_lists = best_path->rollup_lists;
+       List       *rollups = best_path->rollups;
        AttrNumber *grouping_map;
        int                     maxref;
        List       *chain;
-       ListCell   *lc,
-                          *lc2;
+       ListCell   *lc;
 
        /* Shouldn't get here without grouping sets */
        Assert(root->parse->groupingSets);
-       Assert(rollup_lists != NIL);
-       Assert(list_length(rollup_lists) == list_length(rollup_groupclauses));
+       Assert(rollups != NIL);
 
        /*
         * Agg can project, so no need to be terribly picky about child tlist, but
@@ -1724,7 +2198,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;
 
@@ -1735,76 +2209,88 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path)
         * costs will be shown by EXPLAIN.
         */
        chain = NIL;
-       if (list_length(rollup_groupclauses) > 1)
+       if (list_length(rollups) > 1)
        {
-               forboth(lc, rollup_groupclauses, lc2, rollup_lists)
+               ListCell   *lc2 = lnext(list_head(rollups));
+               bool            is_first_sort = ((RollupData *) linitial(rollups))->is_hashed;
+
+               for_each_cell(lc, lc2)
                {
-                       List       *groupClause = (List *) lfirst(lc);
-                       List       *gsets = (List *) lfirst(lc2);
+                       RollupData *rollup = lfirst(lc);
                        AttrNumber *new_grpColIdx;
-                       Plan       *sort_plan;
+                       Plan       *sort_plan = NULL;
                        Plan       *agg_plan;
+                       AggStrategy strat;
 
-                       /* We want to iterate over all but the last rollup list elements */
-                       if (lnext(lc) == NULL)
-                               break;
+                       new_grpColIdx = remap_groupColIdx(root, rollup->groupClause);
 
-                       new_grpColIdx = remap_groupColIdx(root, groupClause);
+                       if (!rollup->is_hashed && !is_first_sort)
+                       {
+                               sort_plan = (Plan *)
+                                       make_sort_from_groupcols(rollup->groupClause,
+                                                                                        new_grpColIdx,
+                                                                                        subplan);
+                       }
 
-                       sort_plan = (Plan *)
-                               make_sort_from_groupcols(groupClause,
-                                                                                new_grpColIdx,
-                                                                                subplan);
+                       if (!rollup->is_hashed)
+                               is_first_sort = false;
+
+                       if (rollup->is_hashed)
+                               strat = AGG_HASHED;
+                       else if (list_length(linitial(rollup->gsets)) == 0)
+                               strat = AGG_PLAIN;
+                       else
+                               strat = AGG_SORTED;
 
                        agg_plan = (Plan *) make_agg(NIL,
                                                                                 NIL,
-                                                                                AGG_SORTED,
-                                                                                false,
-                                                                                true,
-                                                                                false,
-                                                                          list_length((List *) linitial(gsets)),
+                                                                                strat,
+                                                                                AGGSPLIT_SIMPLE,
+                                                                                list_length((List *) linitial(rollup->gsets)),
                                                                                 new_grpColIdx,
-                                                                                extract_grouping_ops(groupClause),
-                                                                                gsets,
+                                                                                extract_grouping_ops(rollup->groupClause),
+                                                                                extract_grouping_collations(rollup->groupClause, subplan->targetlist),
+                                                                                rollup->gsets,
                                                                                 NIL,
-                                                                                0,             /* numGroups not needed */
+                                                                                rollup->numGroups,
                                                                                 sort_plan);
 
                        /*
-                        * Nuke stuff we don't need to avoid bloating debug output.
+                        * Remove stuff we don't need to avoid bloating debug output.
                         */
-                       sort_plan->targetlist = NIL;
-                       sort_plan->lefttree = NULL;
+                       if (sort_plan)
+                       {
+                               sort_plan->targetlist = NIL;
+                               sort_plan->lefttree = NULL;
+                       }
 
                        chain = lappend(chain, agg_plan);
                }
        }
 
        /*
-        * Now make the final Agg node
+        * Now make the real Agg node
         */
        {
-               List       *groupClause = (List *) llast(rollup_groupclauses);
-               List       *gsets = (List *) llast(rollup_lists);
+               RollupData *rollup = linitial(rollups);
                AttrNumber *top_grpColIdx;
                int                     numGroupCols;
 
-               top_grpColIdx = remap_groupColIdx(root, groupClause);
+               top_grpColIdx = remap_groupColIdx(root, rollup->groupClause);
 
-               numGroupCols = list_length((List *) linitial(gsets));
+               numGroupCols = list_length((List *) linitial(rollup->gsets));
 
                plan = make_agg(build_path_tlist(root, &best_path->path),
                                                best_path->qual,
-                                               (numGroupCols > 0) ? AGG_SORTED : AGG_PLAIN,
-                                               false,
-                                               true,
-                                               false,
+                                               best_path->aggstrategy,
+                                               AGGSPLIT_SIMPLE,
                                                numGroupCols,
                                                top_grpColIdx,
-                                               extract_grouping_ops(groupClause),
-                                               gsets,
+                                               extract_grouping_ops(rollup->groupClause),
+                                               extract_grouping_collations(rollup->groupClause, subplan->targetlist),
+                                               rollup->gsets,
                                                chain,
-                                               0,              /* numGroups not needed */
+                                               rollup->numGroups,
                                                subplan);
 
                /* Copy cost data from Path to Plan */
@@ -1853,6 +2339,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);
@@ -1875,7 +2362,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;
 
@@ -1893,19 +2380,19 @@ 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;
+       Oid                *partCollations;
        int                     ordNumCols;
        AttrNumber *ordColIdx;
        Oid                *ordOperators;
+       Oid                *ordCollations;
+       ListCell   *lc;
 
        /*
         * WindowAgg can project, so no need to be terribly picky about child
@@ -1916,32 +2403,47 @@ 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);
+       partCollations = (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;
+               partCollations[partNumCols] = exprCollation((Node *) tle->expr);
+               partNumCols++;
+       }
+
+       ordColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numOrder);
+       ordOperators = (Oid *) palloc(sizeof(Oid) * numOrder);
+       ordCollations = (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;
+               ordCollations[ordNumCols] = exprCollation((Node *) tle->expr);
+               ordNumCols++;
+       }
 
        /* And finally we can make the WindowAgg node */
        plan = make_windowagg(tlist,
@@ -1949,12 +2451,19 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path)
                                                  partNumCols,
                                                  partColIdx,
                                                  partOperators,
+                                                 partCollations,
                                                  ordNumCols,
                                                  ordColIdx,
                                                  ordOperators,
+                                                 ordCollations,
                                                  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);
@@ -1962,112 +2471,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
  *
@@ -2208,8 +2611,11 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path)
                                                        best_path->operation,
                                                        best_path->canSetTag,
                                                        best_path->nominalRelation,
+                                                       best_path->rootRelation,
+                                                       best_path->partColsUpdated,
                                                        best_path->resultRelations,
                                                        subplans,
+                                                       best_path->subroots,
                                                        best_path->withCheckOptionLists,
                                                        best_path->returningLists,
                                                        best_path->rowMarks,
@@ -2355,7 +2761,7 @@ create_indexscan_plan(PlannerInfo *root,
                                          bool indexonly)
 {
        Scan       *scan_plan;
-       List       *indexquals = best_path->indexquals;
+       List       *indexclauses = best_path->indexclauses;
        List       *indexorderbys = best_path->indexorderbys;
        Index           baserelid = best_path->path.parent->relid;
        Oid                     indexoid = best_path->indexinfo->indexoid;
@@ -2371,16 +2777,14 @@ create_indexscan_plan(PlannerInfo *root,
        Assert(best_path->path.parent->rtekind == RTE_RELATION);
 
        /*
-        * Build "stripped" indexquals structure (no RestrictInfos) to pass to
-        * executor as indexqualorig
-        */
-       stripped_indexquals = get_actual_clauses(indexquals);
-
-       /*
-        * The executor needs a copy with the indexkey on the left of each clause
-        * and with index Vars substituted for table ones.
+        * Extract the index qual expressions (stripped of RestrictInfos) from the
+        * IndexClauses list, and prepare a copy with index Vars substituted for
+        * table Vars.  (This step also does replace_nestloop_params on the
+        * fixed_indexquals.)
         */
-       fixed_indexquals = fix_indexqual_references(root, best_path);
+       fix_indexqual_references(root, best_path,
+                                                        &stripped_indexquals,
+                                                        &fixed_indexquals);
 
        /*
         * Likewise fix up index attr references in the ORDER BY expressions.
@@ -2396,14 +2800,14 @@ create_indexscan_plan(PlannerInfo *root,
         * included in qpqual.  The upshot is that qpqual must contain
         * scan_clauses minus whatever appears in indexquals.
         *
-        * 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 indexqual, and is therefore redundant
-        * with it, though not equal.  (This happens when indxpath.c prefers a
+        * is_redundant_with_indexclauses() detects cases where a scan clause is
+        * present in the indexclauses list or is generated from the same
+        * EquivalenceClass as some indexclause, and is therefore redundant with
+        * it, though not equal.  (The latter happens when indxpath.c prefers a
         * different derived equality than what generate_join_implied_equalities
-        * picked for a parameterized scan's ppi_clauses.)
+        * picked for a parameterized scan's ppi_clauses.)  Note that it will not
+        * match to lossy index clauses, which is critical because we have to
+        * include the original clause in qpqual in that case.
         *
         * In some situations (particularly with OR'd index conditions) we may
         * have scan_clauses that are not equal to, but are logically implied by,
@@ -2418,17 +2822,15 @@ create_indexscan_plan(PlannerInfo *root,
        qpqual = NIL;
        foreach(l, scan_clauses)
        {
-               RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
+               RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
 
-               Assert(IsA(rinfo, RestrictInfo));
                if (rinfo->pseudoconstant)
                        continue;                       /* we may drop pseudoconstants here */
-               if (list_member_ptr(indexquals, rinfo))
-                       continue;                       /* simple duplicate */
-               if (is_redundant_derived_clause(rinfo, indexquals))
-                       continue;                       /* derived from same EquivalenceClass */
+               if (is_redundant_with_indexclauses(rinfo, indexclauses))
+                       continue;                       /* dup or 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), stripped_indexquals,
+                                                                false))
                        continue;                       /* provably implied by indexquals */
                qpqual = lappend(qpqual, rinfo);
        }
@@ -2486,7 +2888,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);
                }
        }
@@ -2499,7 +2902,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,
@@ -2547,6 +2950,9 @@ create_bitmap_scan_plan(PlannerInfo *root,
                                                                                   &bitmapqualorig, &indexquals,
                                                                                   &indexECs);
 
+       if (best_path->path.parallel_aware)
+               bitmap_subplan_mark_shared(bitmapqualplan);
+
        /*
         * The qpqual list must contain all restrictions not automatically handled
         * by the index, other than pseudoconstant clauses which will be handled
@@ -2576,10 +2982,9 @@ create_bitmap_scan_plan(PlannerInfo *root,
        qpqual = NIL;
        foreach(l, scan_clauses)
        {
-               RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
+               RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
                Node       *clause = (Node *) rinfo->clause;
 
-               Assert(IsA(rinfo, RestrictInfo));
                if (rinfo->pseudoconstant)
                        continue;                       /* we may drop pseudoconstants here */
                if (list_member(indexquals, clause))
@@ -2587,7 +2992,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);
        }
@@ -2696,6 +3101,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;
@@ -2757,8 +3163,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;
                }
 
                /*
@@ -2784,13 +3191,15 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
        {
                IndexPath  *ipath = (IndexPath *) bitmapqual;
                IndexScan  *iscan;
+               List       *subquals;
+               List       *subindexquals;
                List       *subindexECs;
                ListCell   *l;
 
                /* Use the regular indexscan plan build machinery... */
-               iscan = (IndexScan *) create_indexscan_plan(root, ipath,
-                                                                                                       NIL, NIL, false);
-               Assert(IsA(iscan, IndexScan));
+               iscan = castNode(IndexScan,
+                                                create_indexscan_plan(root, ipath,
+                                                                                          NIL, NIL, false));
                /* then convert to a bitmap indexscan */
                plan = (Plan *) make_bitmap_indexscan(iscan->scan.scanrelid,
                                                                                          iscan->indexid,
@@ -2803,8 +3212,24 @@ 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;
-               *qual = get_actual_clauses(ipath->indexclauses);
-               *indexqual = get_actual_clauses(ipath->indexquals);
+               plan->parallel_safe = ipath->path.parallel_safe;
+               /* Extract original index clauses, actual index quals, relevant ECs */
+               subquals = NIL;
+               subindexquals = NIL;
+               subindexECs = NIL;
+               foreach(l, ipath->indexclauses)
+               {
+                       IndexClause *iclause = (IndexClause *) lfirst(l);
+                       RestrictInfo *rinfo = iclause->rinfo;
+
+                       Assert(!rinfo->pseudoconstant);
+                       subquals = lappend(subquals, rinfo->clause);
+                       subindexquals = list_concat(subindexquals,
+                                                                               get_actual_clauses(iclause->indexquals));
+                       if (rinfo->parent_ec)
+                               subindexECs = lappend(subindexECs, rinfo->parent_ec);
+               }
+               /* We can add any index predicate conditions, too */
                foreach(l, ipath->indexinfo->indpred)
                {
                        Expr       *pred = (Expr *) lfirst(l);
@@ -2815,20 +3240,14 @@ 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), subquals, false))
                        {
-                               *qual = lappend(*qual, pred);
-                               *indexqual = lappend(*indexqual, pred);
+                               subquals = lappend(subquals, pred);
+                               subindexquals = lappend(subindexquals, pred);
                        }
                }
-               subindexECs = NIL;
-               foreach(l, ipath->indexquals)
-               {
-                       RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
-
-                       if (rinfo->parent_ec)
-                               subindexECs = lappend(subindexECs, rinfo->parent_ec);
-               }
+               *qual = subquals;
+               *indexqual = subindexquals;
                *indexECs = subindexECs;
        }
        else
@@ -2852,18 +3271,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)
        {
@@ -2873,15 +3346,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,
@@ -2985,6 +3449,49 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path,
        return scan_plan;
 }
 
+/*
+ * create_tablefuncscan_plan
+ *      Returns a tablefuncscan plan for the base relation scanned by 'best_path'
+ *      with restriction clauses 'scan_clauses' and targetlist 'tlist'.
+ */
+static TableFuncScan *
+create_tablefuncscan_plan(PlannerInfo *root, Path *best_path,
+                                                 List *tlist, List *scan_clauses)
+{
+       TableFuncScan *scan_plan;
+       Index           scan_relid = best_path->parent->relid;
+       RangeTblEntry *rte;
+       TableFunc  *tablefunc;
+
+       /* it should be a function base rel... */
+       Assert(scan_relid > 0);
+       rte = planner_rt_fetch(scan_relid, root);
+       Assert(rte->rtekind == RTE_TABLEFUNC);
+       tablefunc = rte->tablefunc;
+
+       /* Sort clauses into best execution order */
+       scan_clauses = order_qual_clauses(root, scan_clauses);
+
+       /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
+       scan_clauses = extract_actual_clauses(scan_clauses, false);
+
+       /* Replace any outer-relation variables with nestloop params */
+       if (best_path->param_info)
+       {
+               scan_clauses = (List *)
+                       replace_nestloop_params(root, (Node *) scan_clauses);
+               /* The function expressions could contain nestloop params, too */
+               tablefunc = (TableFunc *) replace_nestloop_params(root, (Node *) tablefunc);
+       }
+
+       scan_plan = make_tablefuncscan(tlist, scan_clauses, scan_relid,
+                                                                  tablefunc);
+
+       copy_generic_path_info(&scan_plan->scan.plan, best_path);
+
+       return scan_plan;
+}
+
 /*
  * create_valuesscan_plan
  *      Returns a valuesscan plan for the base relation scanned by 'best_path'
@@ -3117,7 +3624,84 @@ create_ctescan_plan(PlannerInfo *root, Path *best_path,
        scan_plan = make_ctescan(tlist, scan_clauses, scan_relid,
                                                         plan_id, cte_param_id);
 
-       copy_generic_path_info(&scan_plan->scan.plan, best_path);
+       copy_generic_path_info(&scan_plan->scan.plan, 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_resultscan_plan
+ *      Returns a Result plan for the RTE_RESULT base relation scanned by
+ *     'best_path' with restriction clauses 'scan_clauses' and targetlist
+ *     'tlist'.
+ */
+static Result *
+create_resultscan_plan(PlannerInfo *root, Path *best_path,
+                                          List *tlist, List *scan_clauses)
+{
+       Result     *scan_plan;
+       Index           scan_relid = best_path->parent->relid;
+       RangeTblEntry *rte PG_USED_FOR_ASSERTS_ONLY;
+
+       Assert(scan_relid > 0);
+       rte = planner_rt_fetch(scan_relid, root);
+       Assert(rte->rtekind == RTE_RESULT);
+
+       /* 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_result(tlist, (Node *) scan_clauses, NULL);
+
+       copy_generic_path_info(&scan_plan->plan, best_path);
 
        return scan_plan;
 }
@@ -3158,7 +3742,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 */
@@ -3242,23 +3826,29 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
        /* Copy foreign server OID; likewise, no need to make FDW do this */
        scan_plan->fs_server = rel->serverid;
 
-       /* Likewise, copy the relids that are represented by this foreign scan */
-       scan_plan->fs_relids = best_path->path.parent->relids;
+       /*
+        * Likewise, copy the relids that are represented by this foreign scan. An
+        * upper rel doesn't have relids set, but it covers all the base relations
+        * participating in the underlying scan, so use root's all_baserels.
+        */
+       if (rel->reloptkind == RELOPT_UPPER_REL)
+               scan_plan->fs_relids = root->all_baserels;
+       else
+               scan_plan->fs_relids = best_path->path.parent->relids;
 
        /*
-        * If a join between foreign relations was pushed down, remember it. The
-        * push-down safety of the join depends upon the server and user mapping
-        * being same. That can change between planning and execution time, in which
-        * case the plan should be invalidated.
+        * If this is a foreign join, and to make it valid to push down we had to
+        * assume that the current user is the same as some user explicitly named
+        * in the query, mark the finished plan as depending on the current user.
         */
-       if (scan_relid == 0)
-               root->glob->hasForeignJoin = true;
+       if (rel->useridiscurrent)
+               root->glob->dependsOnRole = true;
 
        /*
         * Replace any outer-relation variables with nestloop params in the qual,
         * fdw_exprs and fdw_recheck_quals expressions.  We do this last so that
-        * the FDW doesn't have to be involved.  (Note that parts of fdw_exprs
-        * or fdw_recheck_quals could have come from join clauses, so doing this
+        * the FDW doesn't have to be involved.  (Note that parts of fdw_exprs or
+        * fdw_recheck_quals could have come from join clauses, so doing this
         * beforehand on the scan_clauses wouldn't work.)  We assume
         * fdw_scan_tlist contains no such variables.
         */
@@ -3279,8 +3869,8 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
         * 0, but there can be no Var with relid 0 in the rel's targetlist or the
         * restriction clauses, so we skip this in that case.  Note that any such
         * columns in base relations that were joined are assumed to be contained
-        * in fdw_scan_tlist.)  This is a bit of a kluge and might go away someday,
-        * so we intentionally leave it out of the API presented to FDWs.
+        * in fdw_scan_tlist.)  This is a bit of a kluge and might go away
+        * someday, so we intentionally leave it out of the API presented to FDWs.
         */
        scan_plan->fsSystemCol = false;
        if (scan_relid > 0)
@@ -3353,13 +3943,13 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path,
         * Invoke custom plan provider to create the Plan node represented by the
         * CustomPath.
         */
-       cplan = (CustomScan *) best_path->methods->PlanCustomPath(root,
-                                                                                                                         rel,
-                                                                                                                         best_path,
-                                                                                                                         tlist,
-                                                                                                                         scan_clauses,
-                                                                                                                         custom_plans);
-       Assert(IsA(cplan, CustomScan));
+       cplan = castNode(CustomScan,
+                                        best_path->methods->PlanCustomPath(root,
+                                                                                                               rel,
+                                                                                                               best_path,
+                                                                                                               tlist,
+                                                                                                               scan_clauses,
+                                                                                                               custom_plans));
 
        /*
         * Copy cost data from Path to Plan; no need to make custom-plan providers
@@ -3410,9 +4000,6 @@ create_nestloop_plan(PlannerInfo *root,
        Relids          outerrelids;
        List       *nestParams;
        Relids          saveOuterRels = root->curOuterRels;
-       ListCell   *cell;
-       ListCell   *prev;
-       ListCell   *next;
 
        /* NestLoop can project, so no need to be picky about child tlists */
        outer_plan = create_plan_recurse(root, best_path->outerjoinpath, 0);
@@ -3435,6 +4022,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
@@ -3455,38 +4043,10 @@ create_nestloop_plan(PlannerInfo *root,
 
        /*
         * Identify any nestloop parameters that should be supplied by this join
-        * node, and move them from root->curOuterParams to the nestParams list.
+        * node, and remove them from root->curOuterParams.
         */
        outerrelids = best_path->outerjoinpath->parent->relids;
-       nestParams = NIL;
-       prev = NULL;
-       for (cell = list_head(root->curOuterParams); cell; cell = next)
-       {
-               NestLoopParam *nlp = (NestLoopParam *) lfirst(cell);
-
-               next = lnext(cell);
-               if (IsA(nlp->paramval, Var) &&
-                       bms_is_member(nlp->paramval->varno, outerrelids))
-               {
-                       root->curOuterParams = list_delete_cell(root->curOuterParams,
-                                                                                                       cell, prev);
-                       nestParams = lappend(nestParams, nlp);
-               }
-               else if (IsA(nlp->paramval, PlaceHolderVar) &&
-                                bms_overlap(((PlaceHolderVar *) nlp->paramval)->phrels,
-                                                        outerrelids) &&
-                                bms_is_subset(find_placeholder_info(root,
-                                                                                       (PlaceHolderVar *) nlp->paramval,
-                                                                                                        false)->ph_eval_at,
-                                                          outerrelids))
-               {
-                       root->curOuterParams = list_delete_cell(root->curOuterParams,
-                                                                                                       cell, prev);
-                       nestParams = lappend(nestParams, nlp);
-               }
-               else
-                       prev = cell;
-       }
+       nestParams = identify_current_nestloop_params(root, outerrelids);
 
        join_plan = make_nestloop(tlist,
                                                          joinclauses,
@@ -3494,7 +4054,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);
 
@@ -3519,10 +4080,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
@@ -3531,10 +4096,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 */
@@ -3545,6 +4110,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
@@ -3579,15 +4145,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;
@@ -3598,8 +4166,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;
@@ -3631,7 +4201,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));
@@ -3640,22 +4211,21 @@ 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 = (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 */
-               Assert(IsA(rinfo, RestrictInfo));
                if (rinfo->outer_is_left)
                {
                        oeclass = rinfo->left_ec;
@@ -3670,104 +4240,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 */
@@ -3797,7 +4359,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);
@@ -3820,8 +4384,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
@@ -3831,7 +4393,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);
@@ -3845,6 +4407,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
@@ -3878,7 +4441,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
@@ -3908,8 +4471,6 @@ create_hashjoin_plan(PlannerInfo *root,
                                skewTable = rte->relid;
                                skewColumn = var->varattno;
                                skewInherit = rte->inh;
-                               skewColType = var->vartype;
-                               skewColTypmod = var->vartypmod;
                        }
                }
        }
@@ -3920,9 +4481,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
@@ -3931,13 +4490,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);
 
@@ -3975,42 +4546,18 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root)
        if (IsA(node, Var))
        {
                Var                *var = (Var *) node;
-               Param      *param;
-               NestLoopParam *nlp;
-               ListCell   *lc;
 
                /* Upper-level Vars should be long gone at this point */
                Assert(var->varlevelsup == 0);
                /* If not to be replaced, we can just return the Var unmodified */
                if (!bms_is_member(var->varno, root->curOuterRels))
                        return node;
-               /* Create a Param representing the Var */
-               param = assign_nestloop_param_var(root, var);
-               /* Is this param already listed in root->curOuterParams? */
-               foreach(lc, root->curOuterParams)
-               {
-                       nlp = (NestLoopParam *) lfirst(lc);
-                       if (nlp->paramno == param->paramid)
-                       {
-                               Assert(equal(var, nlp->paramval));
-                               /* Present, so we can just return the Param */
-                               return (Node *) param;
-                       }
-               }
-               /* No, so add it */
-               nlp = makeNode(NestLoopParam);
-               nlp->paramno = param->paramid;
-               nlp->paramval = var;
-               root->curOuterParams = lappend(root->curOuterParams, nlp);
-               /* And return the replacement Param */
-               return (Node *) param;
+               /* Replace the Var with a nestloop Param */
+               return (Node *) replace_nestloop_param_var(root, var);
        }
        if (IsA(node, PlaceHolderVar))
        {
                PlaceHolderVar *phv = (PlaceHolderVar *) node;
-               Param      *param;
-               NestLoopParam *nlp;
-               ListCell   *lc;
 
                /* Upper-level PlaceHolderVars should be long gone at this point */
                Assert(phv->phlevelsup == 0);
@@ -4021,8 +4568,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
@@ -4047,257 +4594,65 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root)
                                                                                                root);
                        return (Node *) newphv;
                }
-               /* Create a Param representing the PlaceHolderVar */
-               param = assign_nestloop_param_placeholdervar(root, phv);
-               /* Is this param already listed in root->curOuterParams? */
-               foreach(lc, root->curOuterParams)
-               {
-                       nlp = (NestLoopParam *) lfirst(lc);
-                       if (nlp->paramno == param->paramid)
-                       {
-                               Assert(equal(phv, nlp->paramval));
-                               /* Present, so we can just return the Param */
-                               return (Node *) param;
-                       }
-               }
-               /* No, so add it */
-               nlp = makeNode(NestLoopParam);
-               nlp->paramno = param->paramid;
-               nlp->paramval = (Var *) phv;
-               root->curOuterParams = lappend(root->curOuterParams, nlp);
-               /* And return the replacement Param */
-               return (Node *) param;
+               /* Replace the PlaceHolderVar with a nestloop Param */
+               return (Node *) replace_nestloop_param_placeholdervar(root, phv);
        }
        return expression_tree_mutator(node,
                                                                   replace_nestloop_params_mutator,
                                                                   (void *) root);
 }
 
-/*
- * process_subquery_nestloop_params
- *       Handle params of a parameterized subquery that need to be fed
- *       from an outer nestloop.
- *
- * Currently, that would be *all* params that a subquery in FROM has demanded
- * from the current query level, since they must be LATERAL references.
- *
- * The subplan's references to the outer variables are already represented
- * as PARAM_EXEC Params, so we need not modify the subplan here.  What we
- * do need to do is add entries to root->curOuterParams to signal the parent
- * nestloop plan node that it must provide these values.
- */
-static void
-process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
-{
-       ListCell   *ppl;
-
-       foreach(ppl, subplan_params)
-       {
-               PlannerParamItem *pitem = (PlannerParamItem *) lfirst(ppl);
-
-               if (IsA(pitem->item, Var))
-               {
-                       Var                *var = (Var *) pitem->item;
-                       NestLoopParam *nlp;
-                       ListCell   *lc;
-
-                       /* If not from a nestloop outer rel, complain */
-                       if (!bms_is_member(var->varno, root->curOuterRels))
-                               elog(ERROR, "non-LATERAL parameter required by subquery");
-                       /* Is this param already listed in root->curOuterParams? */
-                       foreach(lc, root->curOuterParams)
-                       {
-                               nlp = (NestLoopParam *) lfirst(lc);
-                               if (nlp->paramno == pitem->paramId)
-                               {
-                                       Assert(equal(var, nlp->paramval));
-                                       /* Present, so nothing to do */
-                                       break;
-                               }
-                       }
-                       if (lc == NULL)
-                       {
-                               /* No, so add it */
-                               nlp = makeNode(NestLoopParam);
-                               nlp->paramno = pitem->paramId;
-                               nlp->paramval = copyObject(var);
-                               root->curOuterParams = lappend(root->curOuterParams, nlp);
-                       }
-               }
-               else if (IsA(pitem->item, PlaceHolderVar))
-               {
-                       PlaceHolderVar *phv = (PlaceHolderVar *) pitem->item;
-                       NestLoopParam *nlp;
-                       ListCell   *lc;
-
-                       /* If not from a nestloop outer rel, complain */
-                       if (!bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at,
-                                                          root->curOuterRels))
-                               elog(ERROR, "non-LATERAL parameter required by subquery");
-                       /* Is this param already listed in root->curOuterParams? */
-                       foreach(lc, root->curOuterParams)
-                       {
-                               nlp = (NestLoopParam *) lfirst(lc);
-                               if (nlp->paramno == pitem->paramId)
-                               {
-                                       Assert(equal(phv, nlp->paramval));
-                                       /* Present, so nothing to do */
-                                       break;
-                               }
-                       }
-                       if (lc == NULL)
-                       {
-                               /* No, so add it */
-                               nlp = makeNode(NestLoopParam);
-                               nlp->paramno = pitem->paramId;
-                               nlp->paramval = copyObject(phv);
-                               root->curOuterParams = lappend(root->curOuterParams, nlp);
-                       }
-               }
-               else
-                       elog(ERROR, "unexpected type of subquery parameter");
-       }
-}
-
 /*
  * fix_indexqual_references
  *       Adjust indexqual clauses to the form the executor's indexqual
  *       machinery needs.
  *
- * We have four tasks here:
- *     * Remove RestrictInfo nodes from the input clauses.
+ * We have three tasks here:
+ *     * Select the actual qual clauses out of the input IndexClause list,
+ *       and remove RestrictInfo nodes from the qual clauses.
  *     * Replace any outer-relation Var or PHV nodes with nestloop Params.
  *       (XXX eventually, that responsibility should go elsewhere?)
  *     * Index keys must be represented by Var nodes with varattno set to the
  *       index's attribute number, not the attribute number in the original rel.
- *     * If the index key is on the right, commute the clause to put it on the
- *       left.
  *
- * The result is a modified copy of the path's indexquals list --- the
- * original is not changed.  Note also that the copy shares no substructure
- * with the original; this is needed in case there is a subplan in it (we need
- * two separate copies of the subplan tree, or things will go awry).
+ * *stripped_indexquals_p receives a list of the actual qual clauses.
+ *
+ * *fixed_indexquals_p receives a list of the adjusted quals.  This is a copy
+ * that shares no substructure with the original; this is needed in case there
+ * are subplans in it (we need two separate copies of the subplan tree, or
+ * things will go awry).
  */
-static List *
-fix_indexqual_references(PlannerInfo *root, IndexPath *index_path)
+static void
+fix_indexqual_references(PlannerInfo *root, IndexPath *index_path,
+                                                List **stripped_indexquals_p, List **fixed_indexquals_p)
 {
        IndexOptInfo *index = index_path->indexinfo;
+       List       *stripped_indexquals;
        List       *fixed_indexquals;
-       ListCell   *lcc,
-                          *lci;
+       ListCell   *lc;
 
-       fixed_indexquals = NIL;
+       stripped_indexquals = fixed_indexquals = NIL;
 
-       forboth(lcc, index_path->indexquals, lci, index_path->indexqualcols)
+       foreach(lc, index_path->indexclauses)
        {
-               RestrictInfo *rinfo = (RestrictInfo *) lfirst(lcc);
-               int                     indexcol = lfirst_int(lci);
-               Node       *clause;
-
-               Assert(IsA(rinfo, RestrictInfo));
-
-               /*
-                * Replace any outer-relation variables with nestloop params.
-                *
-                * This also makes a copy of the clause, so it's safe to modify it
-                * in-place below.
-                */
-               clause = replace_nestloop_params(root, (Node *) rinfo->clause);
-
-               if (IsA(clause, OpExpr))
-               {
-                       OpExpr     *op = (OpExpr *) clause;
-
-                       if (list_length(op->args) != 2)
-                               elog(ERROR, "indexqual clause is not binary opclause");
-
-                       /*
-                        * Check to see if the indexkey is on the right; if so, commute
-                        * the clause.  The indexkey should be the side that refers to
-                        * (only) the base relation.
-                        */
-                       if (!bms_equal(rinfo->left_relids, index->rel->relids))
-                               CommuteOpExpr(op);
-
-                       /*
-                        * Now replace the indexkey expression with an index Var.
-                        */
-                       linitial(op->args) = fix_indexqual_operand(linitial(op->args),
-                                                                                                          index,
-                                                                                                          indexcol);
-               }
-               else if (IsA(clause, RowCompareExpr))
-               {
-                       RowCompareExpr *rc = (RowCompareExpr *) clause;
-                       Expr       *newrc;
-                       List       *indexcolnos;
-                       bool            var_on_left;
-                       ListCell   *lca,
-                                          *lcai;
-
-                       /*
-                        * Re-discover which index columns are used in the rowcompare.
-                        */
-                       newrc = adjust_rowcompare_for_index(rc,
-                                                                                               index,
-                                                                                               indexcol,
-                                                                                               &indexcolnos,
-                                                                                               &var_on_left);
-
-                       /*
-                        * Trouble if adjust_rowcompare_for_index thought the
-                        * RowCompareExpr didn't match the index as-is; the clause should
-                        * have gone through that routine already.
-                        */
-                       if (newrc != (Expr *) rc)
-                               elog(ERROR, "inconsistent results from adjust_rowcompare_for_index");
-
-                       /*
-                        * Check to see if the indexkey is on the right; if so, commute
-                        * the clause.
-                        */
-                       if (!var_on_left)
-                               CommuteRowCompareExpr(rc);
-
-                       /*
-                        * Now replace the indexkey expressions with index Vars.
-                        */
-                       Assert(list_length(rc->largs) == list_length(indexcolnos));
-                       forboth(lca, rc->largs, lcai, indexcolnos)
-                       {
-                               lfirst(lca) = fix_indexqual_operand(lfirst(lca),
-                                                                                                       index,
-                                                                                                       lfirst_int(lcai));
-                       }
-               }
-               else if (IsA(clause, ScalarArrayOpExpr))
-               {
-                       ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
+               IndexClause *iclause = lfirst_node(IndexClause, lc);
+               int                     indexcol = iclause->indexcol;
+               ListCell   *lc2;
 
-                       /* Never need to commute... */
-
-                       /* Replace the indexkey expression with an index Var. */
-                       linitial(saop->args) = fix_indexqual_operand(linitial(saop->args),
-                                                                                                                index,
-                                                                                                                indexcol);
-               }
-               else if (IsA(clause, NullTest))
+               foreach(lc2, iclause->indexquals)
                {
-                       NullTest   *nt = (NullTest *) clause;
+                       RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
+                       Node       *clause = (Node *) rinfo->clause;
 
-                       /* Replace the indexkey expression with an index Var. */
-                       nt->arg = (Expr *) fix_indexqual_operand((Node *) nt->arg,
-                                                                                                        index,
-                                                                                                        indexcol);
+                       stripped_indexquals = lappend(stripped_indexquals, clause);
+                       clause = fix_indexqual_clause(root, index, indexcol,
+                                                                                 clause, iclause->indexcols);
+                       fixed_indexquals = lappend(fixed_indexquals, clause);
                }
-               else
-                       elog(ERROR, "unsupported indexqual type: %d",
-                                (int) nodeTag(clause));
-
-               fixed_indexquals = lappend(fixed_indexquals, clause);
        }
 
-       return fixed_indexquals;
+       *stripped_indexquals_p = stripped_indexquals;
+       *fixed_indexquals_p = fixed_indexquals;
 }
 
 /*
@@ -4305,11 +4660,8 @@ fix_indexqual_references(PlannerInfo *root, IndexPath *index_path)
  *       Adjust indexorderby clauses to the form the executor's index
  *       machinery needs.
  *
- * This is a simplified version of fix_indexqual_references.  The input does
- * not have RestrictInfo nodes, and we assume that indxpath.c already
- * commuted the clauses to put the index keys on the left.  Also, we don't
- * bother to support any cases except simple OpExprs, since nothing else
- * is allowed for ordering operators.
+ * This is a simplified version of fix_indexqual_references.  The input is
+ * bare clauses and a separate indexcol list, instead of IndexClauses.
  */
 static List *
 fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path)
@@ -4326,36 +4678,79 @@ fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path)
                Node       *clause = (Node *) lfirst(lcc);
                int                     indexcol = lfirst_int(lci);
 
-               /*
-                * Replace any outer-relation variables with nestloop params.
-                *
-                * This also makes a copy of the clause, so it's safe to modify it
-                * in-place below.
-                */
-               clause = replace_nestloop_params(root, clause);
+               clause = fix_indexqual_clause(root, index, indexcol, clause, NIL);
+               fixed_indexorderbys = lappend(fixed_indexorderbys, clause);
+       }
 
-               if (IsA(clause, OpExpr))
-               {
-                       OpExpr     *op = (OpExpr *) clause;
+       return fixed_indexorderbys;
+}
+
+/*
+ * fix_indexqual_clause
+ *       Convert a single indexqual clause to the form needed by the executor.
+ *
+ * We replace nestloop params here, and replace the index key variables
+ * or expressions by index Var nodes.
+ */
+static Node *
+fix_indexqual_clause(PlannerInfo *root, IndexOptInfo *index, int indexcol,
+                                        Node *clause, List *indexcolnos)
+{
+       /*
+        * Replace any outer-relation variables with nestloop params.
+        *
+        * This also makes a copy of the clause, so it's safe to modify it
+        * in-place below.
+        */
+       clause = replace_nestloop_params(root, clause);
 
-                       if (list_length(op->args) != 2)
-                               elog(ERROR, "indexorderby clause is not binary opclause");
+       if (IsA(clause, OpExpr))
+       {
+               OpExpr     *op = (OpExpr *) clause;
 
-                       /*
-                        * Now replace the indexkey expression with an index Var.
-                        */
-                       linitial(op->args) = fix_indexqual_operand(linitial(op->args),
-                                                                                                          index,
-                                                                                                          indexcol);
+               /* Replace the indexkey expression with an index Var. */
+               linitial(op->args) = fix_indexqual_operand(linitial(op->args),
+                                                                                                  index,
+                                                                                                  indexcol);
+       }
+       else if (IsA(clause, RowCompareExpr))
+       {
+               RowCompareExpr *rc = (RowCompareExpr *) clause;
+               ListCell   *lca,
+                                  *lcai;
+
+               /* Replace the indexkey expressions with index Vars. */
+               Assert(list_length(rc->largs) == list_length(indexcolnos));
+               forboth(lca, rc->largs, lcai, indexcolnos)
+               {
+                       lfirst(lca) = fix_indexqual_operand(lfirst(lca),
+                                                                                               index,
+                                                                                               lfirst_int(lcai));
                }
-               else
-                       elog(ERROR, "unsupported indexorderby type: %d",
-                                (int) nodeTag(clause));
+       }
+       else if (IsA(clause, ScalarArrayOpExpr))
+       {
+               ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
 
-               fixed_indexorderbys = lappend(fixed_indexorderbys, clause);
+               /* Replace the indexkey expression with an index Var. */
+               linitial(saop->args) = fix_indexqual_operand(linitial(saop->args),
+                                                                                                        index,
+                                                                                                        indexcol);
+       }
+       else if (IsA(clause, NullTest))
+       {
+               NullTest   *nt = (NullTest *) clause;
+
+               /* Replace the indexkey expression with an index Var. */
+               nt->arg = (Expr *) fix_indexqual_operand((Node *) nt->arg,
+                                                                                                index,
+                                                                                                indexcol);
        }
+       else
+               elog(ERROR, "unsupported indexqual type: %d",
+                        (int) nodeTag(clause));
 
-       return fixed_indexorderbys;
+       return clause;
 }
 
 /*
@@ -4429,7 +4824,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol)
                }
        }
 
-       /* Ooops... */
+       /* Oops... */
        elog(ERROR, "index key does not match expected index column");
        return NULL;                            /* keep compiler quiet */
 }
@@ -4493,21 +4888,32 @@ get_switched_clauses(List *clauses, Relids outerrelids)
  *             plan node, sort the list into the order we want to check the quals
  *             in at runtime.
  *
+ * When security barrier quals are used in the query, we may have quals with
+ * different security levels in the list.  Quals of lower security_level
+ * must go before quals of higher security_level, except that we can grant
+ * exceptions to move up quals that are leakproof.  When security level
+ * doesn't force the decision, we prefer to order clauses by estimated
+ * execution cost, cheapest first.
+ *
  * Ideally the order should be driven by a combination of execution cost and
  * selectivity, but it's not immediately clear how to account for both,
  * and given the uncertainty of the estimates the reliability of the decisions
- * would be doubtful anyway.  So we just order by estimated per-tuple cost,
- * being careful not to change the order when (as is often the case) the
- * estimates are identical.
+ * would be doubtful anyway.  So we just order by security level then
+ * estimated per-tuple cost, being careful not to change the order when
+ * (as is often the case) the estimates are identical.
  *
  * Although this will work on either bare clauses or RestrictInfos, it's
  * much faster to apply it to RestrictInfos, since it can re-use cost
- * information that is cached in RestrictInfos.
+ * information that is cached in RestrictInfos.  XXX in the bare-clause
+ * case, we are also not able to apply security considerations.  That is
+ * all right for the moment, because the bare-clause case doesn't occur
+ * anywhere that barrier quals could be present, but it would be better to
+ * get rid of it.
  *
  * Note: some callers pass lists that contain entries that will later be
  * removed; this is the easiest way to let this routine see RestrictInfos
- * instead of bare clauses.  It's OK because we only sort by cost, but
- * a cost/selectivity combination would likely do the wrong thing.
+ * instead of bare clauses.  This is another reason why trying to consider
+ * selectivity in the ordering would likely do the wrong thing.
  */
 static List *
 order_qual_clauses(PlannerInfo *root, List *clauses)
@@ -4516,6 +4922,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
        {
                Node       *clause;
                Cost            cost;
+               Index           security_level;
        } QualItem;
        int                     nitems = list_length(clauses);
        QualItem   *items;
@@ -4541,6 +4948,27 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
                cost_qual_eval_node(&qcost, clause, root);
                items[i].clause = clause;
                items[i].cost = qcost.per_tuple;
+               if (IsA(clause, RestrictInfo))
+               {
+                       RestrictInfo *rinfo = (RestrictInfo *) clause;
+
+                       /*
+                        * If a clause is leakproof, it doesn't have to be constrained by
+                        * its nominal security level.  If it's also reasonably cheap
+                        * (here defined as 10X cpu_operator_cost), pretend it has
+                        * security_level 0, which will allow it to go in front of
+                        * more-expensive quals of lower security levels.  Of course, that
+                        * will also force it to go in front of cheaper quals of its own
+                        * security level, which is not so great, but we can alleviate
+                        * that risk by applying the cost limit cutoff.
+                        */
+                       if (rinfo->leakproof && items[i].cost < 10 * cpu_operator_cost)
+                               items[i].security_level = 0;
+                       else
+                               items[i].security_level = rinfo->security_level;
+               }
+               else
+                       items[i].security_level = 0;
                i++;
        }
 
@@ -4557,9 +4985,13 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
                /* insert newitem into the already-sorted subarray */
                for (j = i; j > 0; j--)
                {
-                       if (newitem.cost >= items[j - 1].cost)
+                       QualItem   *olditem = &items[j - 1];
+
+                       if (newitem.security_level > olditem->security_level ||
+                               (newitem.security_level == olditem->security_level &&
+                                newitem.cost >= olditem->cost))
                                break;
-                       items[j] = items[j - 1];
+                       items[j] = *olditem;
                }
                items[j] = newitem;
        }
@@ -4575,7 +5007,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)
@@ -4585,6 +5017,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;
 }
 
 /*
@@ -4600,6 +5033,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;
 }
 
 /*
@@ -4629,8 +5064,31 @@ 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
+ *      shared memory.
+ */
+static void
+bitmap_subplan_mark_shared(Plan *plan)
+{
+       if (IsA(plan, BitmapAnd))
+               bitmap_subplan_mark_shared(
+                                                                  linitial(((BitmapAnd *) plan)->bitmapplans));
+       else if (IsA(plan, BitmapOr))
+       {
+               ((BitmapOr *) plan)->isshared = true;
+               bitmap_subplan_mark_shared(
+                                                                  linitial(((BitmapOr *) plan)->bitmapplans));
+       }
+       else if (IsA(plan, BitmapIndexScan))
+               ((BitmapIndexScan *) plan)->isshared = true;
+       else
+               elog(ERROR, "unrecognized node type: %d", nodeTag(plan));
+}
 
 /*****************************************************************************
  *
@@ -4840,6 +5298,25 @@ make_functionscan(List *qptlist,
        return node;
 }
 
+static TableFuncScan *
+make_tablefuncscan(List *qptlist,
+                                  List *qpqual,
+                                  Index scanrelid,
+                                  TableFunc *tablefunc)
+{
+       TableFuncScan *node = makeNode(TableFuncScan);
+       Plan       *plan = &node->scan.plan;
+
+       plan->targetlist = qptlist;
+       plan->qual = qpqual;
+       plan->lefttree = NULL;
+       plan->righttree = NULL;
+       node->scan.scanrelid = scanrelid;
+       node->tablefunc = tablefunc;
+
+       return node;
+}
+
 static ValuesScan *
 make_valuesscan(List *qptlist,
                                List *qpqual,
@@ -4880,6 +5357,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,
@@ -4933,21 +5430,6 @@ make_foreignscan(List *qptlist,
        return node;
 }
 
-static Append *
-make_append(List *appendplans, List *tlist)
-{
-       Append     *node = makeNode(Append);
-       Plan       *plan = &node->plan;
-
-       plan->targetlist = tlist;
-       plan->qual = NIL;
-       plan->lefttree = NULL;
-       plan->righttree = NULL;
-       node->appendplans = appendplans;
-
-       return node;
-}
-
 static RecursiveUnion *
 make_recursive_union(List *tlist,
                                         Plan *lefttree,
@@ -4976,10 +5458,12 @@ make_recursive_union(List *tlist,
                int                     keyno = 0;
                AttrNumber *dupColIdx;
                Oid                *dupOperators;
+               Oid                *dupCollations;
                ListCell   *slitem;
 
                dupColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
                dupOperators = (Oid *) palloc(sizeof(Oid) * numCols);
+               dupCollations = (Oid *) palloc(sizeof(Oid) * numCols);
 
                foreach(slitem, distinctList)
                {
@@ -4989,11 +5473,13 @@ make_recursive_union(List *tlist,
 
                        dupColIdx[keyno] = tle->resno;
                        dupOperators[keyno] = sortcl->eqop;
+                       dupCollations[keyno] = exprCollation((Node *) tle->expr);
                        Assert(OidIsValid(dupOperators[keyno]));
                        keyno++;
                }
                node->dupColIdx = dupColIdx;
                node->dupOperators = dupOperators;
+               node->dupCollations = dupCollations;
        }
        node->numGroups = numGroups;
 
@@ -5037,7 +5523,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;
@@ -5047,6 +5534,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;
 
@@ -5060,7 +5548,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;
@@ -5071,6 +5560,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;
@@ -5080,9 +5570,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;
@@ -5095,8 +5583,6 @@ make_hash(Plan *lefttree,
        node->skewTable = skewTable;
        node->skewColumn = skewColumn;
        node->skewInherit = skewInherit;
-       node->skewColType = skewColType;
-       node->skewColTypmod = skewColTypmod;
 
        return node;
 }
@@ -5112,7 +5598,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;
@@ -5121,12 +5609,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;
@@ -5163,16 +5653,16 @@ make_sort(Plan *lefttree, int numCols,
  * prepare_sort_from_pathkeys
  *       Prepare to sort according to given pathkeys
  *
- * This is used to set up for both Sort and MergeAppend nodes.  It calculates
- * the executor's representation of the sort key information, and adjusts the
- * plan targetlist if needed to add resjunk sort columns.
+ * This is used to set up for Sort, MergeAppend, and Gather Merge nodes.  It
+ * calculates the executor's representation of the sort key information, and
+ * adjusts the plan targetlist if needed to add resjunk sort columns.
  *
  * Input parameters:
  *       'lefttree' is the plan node which yields input tuples
  *       '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,
@@ -5180,8 +5670,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;
@@ -5189,10 +5680,10 @@ make_sort(Plan *lefttree, int numCols,
  *
  * If the pathkeys include expressions that aren't simple Vars, we will
  * usually need to add resjunk items to the input plan's targetlist to
- * compute these expressions, since the Sort/MergeAppend node itself won't
+ * compute these expressions, since a Sort or MergeAppend node itself won't
  * do any such calculations.  If the input plan type isn't one that can do
  * projections, this means adding a Result node just to do the projection.
- * However, the caller can pass adjust_tlist_in_place = TRUE to force the
+ * 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.
  *
@@ -5336,11 +5827,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;
@@ -5371,7 +5862,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 */
@@ -5385,7 +5877,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 */
                }
 
                /*
@@ -5397,7 +5889,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);
 
@@ -5423,7 +5915,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,
@@ -5451,10 +5943,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) */
@@ -5475,9 +5967,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;
@@ -5487,7 +5980,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,
@@ -5629,6 +6122,16 @@ materialize_finished_plan(Plan *subplan)
 
        matplan = (Plan *) make_material(subplan);
 
+       /*
+        * XXX horrid kluge: if there are any initPlans attached to the subplan,
+        * move them up to the Material node, which is now effectively the top
+        * plan node in its query level.  This prevents failure in
+        * SS_finalize_plan(), which see for comments.  We don't bother adjusting
+        * the subplan's cost estimate for this.
+        */
+       matplan->initPlan = subplan->initPlan;
+       subplan->initPlan = NIL;
+
        /* Set cost data */
        cost_material(&matpath,
                                  subplan->startup_cost,
@@ -5640,15 +6143,15 @@ 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;
 }
 
 Agg *
 make_agg(List *tlist, List *qual,
-                AggStrategy aggstrategy,
-                bool combineStates, bool finalizeAggs, bool serialStates,
-                int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators,
+                AggStrategy aggstrategy, AggSplit aggsplit,
+                int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
                 List *groupingSets, List *chain,
                 double dNumGroups, Plan *lefttree)
 {
@@ -5660,13 +6163,13 @@ make_agg(List *tlist, List *qual,
        numGroups = (long) Min(dNumGroups, (double) LONG_MAX);
 
        node->aggstrategy = aggstrategy;
-       node->combineStates = combineStates;
-       node->finalizeAggs = finalizeAggs;
-       node->serialStates = serialStates;
+       node->aggsplit = aggsplit;
        node->numCols = numGroupCols;
        node->grpColIdx = grpColIdx;
        node->grpOperators = grpOperators;
+       node->grpCollations = grpCollations;
        node->numGroups = numGroups;
+       node->aggParams = NULL;         /* SS_finalize_plan() will fill this */
        node->groupingSets = groupingSets;
        node->chain = chain;
 
@@ -5680,9 +6183,11 @@ make_agg(List *tlist, List *qual,
 
 static WindowAgg *
 make_windowagg(List *tlist, Index winref,
-                          int partNumCols, AttrNumber *partColIdx, Oid *partOperators,
-                          int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators,
+                          int partNumCols, AttrNumber *partColIdx, Oid *partOperators, Oid *partCollations,
+                          int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators, Oid *ordCollations,
                           int frameOptions, Node *startOffset, Node *endOffset,
+                          Oid startInRangeFunc, Oid endInRangeFunc,
+                          Oid inRangeColl, bool inRangeAsc, bool inRangeNullsFirst,
                           Plan *lefttree)
 {
        WindowAgg  *node = makeNode(WindowAgg);
@@ -5692,12 +6197,19 @@ make_windowagg(List *tlist, Index winref,
        node->partNumCols = partNumCols;
        node->partColIdx = partColIdx;
        node->partOperators = partOperators;
+       node->partCollations = partCollations;
        node->ordNumCols = ordNumCols;
        node->ordColIdx = ordColIdx;
        node->ordOperators = ordOperators;
+       node->ordCollations = ordCollations;
        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;
@@ -5714,6 +6226,7 @@ make_group(List *tlist,
                   int numGroupCols,
                   AttrNumber *grpColIdx,
                   Oid *grpOperators,
+                  Oid *grpCollations,
                   Plan *lefttree)
 {
        Group      *node = makeNode(Group);
@@ -5722,6 +6235,7 @@ make_group(List *tlist,
        node->numCols = numGroupCols;
        node->grpColIdx = grpColIdx;
        node->grpOperators = grpOperators;
+       node->grpCollations = grpCollations;
 
        plan->qual = qual;
        plan->targetlist = tlist;
@@ -5745,6 +6259,7 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
        int                     keyno = 0;
        AttrNumber *uniqColIdx;
        Oid                *uniqOperators;
+       Oid                *uniqCollations;
        ListCell   *slitem;
 
        plan->targetlist = lefttree->targetlist;
@@ -5759,6 +6274,7 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
        Assert(numCols > 0);
        uniqColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
        uniqOperators = (Oid *) palloc(sizeof(Oid) * numCols);
+       uniqCollations = (Oid *) palloc(sizeof(Oid) * numCols);
 
        foreach(slitem, distinctList)
        {
@@ -5767,6 +6283,7 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
 
                uniqColIdx[keyno] = tle->resno;
                uniqOperators[keyno] = sortcl->eqop;
+               uniqCollations[keyno] = exprCollation((Node *) tle->expr);
                Assert(OidIsValid(uniqOperators[keyno]));
                keyno++;
        }
@@ -5774,6 +6291,7 @@ make_unique_from_sortclauses(Plan *lefttree, List *distinctList)
        node->numCols = numCols;
        node->uniqColIdx = uniqColIdx;
        node->uniqOperators = uniqOperators;
+       node->uniqCollations = uniqCollations;
 
        return node;
 }
@@ -5789,6 +6307,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
        int                     keyno = 0;
        AttrNumber *uniqColIdx;
        Oid                *uniqOperators;
+       Oid                *uniqCollations;
        ListCell   *lc;
 
        plan->targetlist = lefttree->targetlist;
@@ -5804,6 +6323,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
        Assert(numCols >= 0 && numCols <= list_length(pathkeys));
        uniqColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
        uniqOperators = (Oid *) palloc(sizeof(Oid) * numCols);
+       uniqCollations = (Oid *) palloc(sizeof(Oid) * numCols);
 
        foreach(lc, pathkeys)
        {
@@ -5866,12 +6386,13 @@ 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);
 
                uniqColIdx[keyno] = tle->resno;
                uniqOperators[keyno] = eqop;
+               uniqCollations[keyno] = ec->ec_collation;
 
                keyno++;
        }
@@ -5879,6 +6400,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
        node->numCols = numCols;
        node->uniqColIdx = uniqColIdx;
        node->uniqOperators = uniqOperators;
+       node->uniqCollations = uniqCollations;
 
        return node;
 }
@@ -5887,6 +6409,7 @@ static Gather *
 make_gather(List *qptlist,
                        List *qpqual,
                        int nworkers,
+                       int rescan_param,
                        bool single_copy,
                        Plan *subplan)
 {
@@ -5898,8 +6421,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->invisible = false;
+       node->initParam = NULL;
 
        return node;
 }
@@ -5920,6 +6445,7 @@ make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
        int                     keyno = 0;
        AttrNumber *dupColIdx;
        Oid                *dupOperators;
+       Oid                *dupCollations;
        ListCell   *slitem;
 
        plan->targetlist = lefttree->targetlist;
@@ -5931,9 +6457,9 @@ 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);
+       dupCollations = (Oid *) palloc(sizeof(Oid) * numCols);
 
        foreach(slitem, distinctList)
        {
@@ -5942,6 +6468,7 @@ make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
 
                dupColIdx[keyno] = tle->resno;
                dupOperators[keyno] = sortcl->eqop;
+               dupCollations[keyno] = exprCollation((Node *) tle->expr);
                Assert(OidIsValid(dupOperators[keyno]));
                keyno++;
        }
@@ -5951,6 +6478,7 @@ make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
        node->numCols = numCols;
        node->dupColIdx = dupColIdx;
        node->dupOperators = dupOperators;
+       node->dupCollations = dupCollations;
        node->flagColIdx = flagColIdx;
        node->firstFlag = firstFlag;
        node->numGroups = numGroups;
@@ -6021,6 +6549,25 @@ make_result(List *tlist,
        return node;
 }
 
+/*
+ * make_project_set
+ *       Build a ProjectSet plan node
+ */
+static ProjectSet *
+make_project_set(List *tlist,
+                                Plan *subplan)
+{
+       ProjectSet *node = makeNode(ProjectSet);
+       Plan       *plan = &node->plan;
+
+       plan->targetlist = tlist;
+       plan->qual = NIL;
+       plan->lefttree = subplan;
+       plan->righttree = NULL;
+
+       return node;
+}
+
 /*
  * make_modifytable
  *       Build a ModifyTable plan node
@@ -6028,8 +6575,9 @@ make_result(List *tlist,
 static ModifyTable *
 make_modifytable(PlannerInfo *root,
                                 CmdType operation, bool canSetTag,
-                                Index nominalRelation,
-                                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)
 {
@@ -6037,9 +6585,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 ||
@@ -6054,8 +6604,11 @@ make_modifytable(PlannerInfo *root,
        node->operation = operation;
        node->canSetTag = canSetTag;
        node->nominalRelation = nominalRelation;
+       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)
        {
@@ -6095,9 +6648,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;
@@ -6109,16 +6663,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)
@@ -6128,8 +6682,11 @@ 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 and (2) there are no local
+                * structures that need to be run for each modified row: row-level
+                * triggers on the foreign table, stored generated columns, WITH CHECK
+                * OPTIONs from parent views.
                 */
                direct_modify = false;
                if (fdwroutine != NULL &&
@@ -6137,15 +6694,17 @@ 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) &&
+                       !has_stored_generated_columns(subroot, rti))
+                       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);
@@ -6181,12 +6740,20 @@ is_projection_capable_path(Path *path)
                case T_Append:
 
                        /*
-                        * Append can't project, but if it's being used to represent a
-                        * dummy path, claim that it can project.  This prevents us from
-                        * converting a rel from dummy to non-dummy status by applying a
-                        * projection to its dummy path.
+                        * Append can't project, but if an AppendPath is being used to
+                        * represent a dummy path, what will actually be generated is a
+                        * Result which can project.
                         */
-                       return IS_DUMMY_PATH(path);
+                       return IS_DUMMY_APPEND(path);
+               case T_ProjectSet:
+
+                       /*
+                        * Although ProjectSet certainly projects, say "no" because we
+                        * don't want the planner to randomly replace its tlist with
+                        * something else; the SRFs have to stay at top level.  This might
+                        * get relaxed later.
+                        */
+                       return false;
                default:
                        break;
        }
@@ -6215,6 +6782,15 @@ is_projection_capable_plan(Plan *plan)
                case T_MergeAppend:
                case T_RecursiveUnion:
                        return false;
+               case T_ProjectSet:
+
+                       /*
+                        * Although ProjectSet certainly projects, say "no" because we
+                        * don't want the planner to randomly replace its tlist with
+                        * something else; the SRFs have to stay at top level.  This might
+                        * get relaxed later.
+                        */
+                       return false;
                default:
                        break;
        }