]> 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 b6ad01be6bb158723e97ec1817912021f31fc53f..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-2018, 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"
@@ -65,7 +63,7 @@
  * 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 doens't matter a bit what target list gets generated.
+ * 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 */
@@ -83,9 +81,12 @@ 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);
@@ -107,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);
@@ -148,6 +140,8 @@ 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,
@@ -160,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);
@@ -210,8 +207,6 @@ 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, int first_partial_plan,
-                       List *tlist, List *partitioned_rels, List *partpruneinfos);
 static RecursiveUnion *make_recursive_union(List *tlist,
                                         Plan *lefttree,
                                         Plan *righttree,
@@ -265,14 +260,14 @@ static Sort *make_sort_from_groupcols(List *groupcls,
                                                 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,
@@ -287,9 +282,9 @@ static Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
 static ProjectSet *make_project_set(List *tlist, Plan *subplan);
 static ModifyTable *make_modifytable(PlannerInfo *root,
                                 CmdType operation, bool canSetTag,
-                                Index nominalRelation, List *partitioned_rels,
+                                Index nominalRelation, Index rootRelation,
                                 bool partColsUpdated,
-                                List *resultRelations, List *subplans,
+                                List *resultRelations, List *subplans, List *subroots,
                                 List *withCheckOptionLists, List *returningLists,
                                 List *rowMarks, OnConflictExpr *onconflict, int epqParam);
 static GatherMerge *create_gather_merge_plan(PlannerInfo *root,
@@ -319,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;
 
@@ -397,11 +392,13 @@ 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))
@@ -415,11 +412,16 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
                                plan = (Plan *) create_minmaxagg_plan(root,
                                                                                                          (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:
@@ -588,10 +590,10 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
                        tlist = copyObject(((IndexPath *) best_path)->indexinfo->indextlist);
 
                        /*
-                        * Transfer any sortgroupref data to the replacement tlist, unless
-                        * we don't care because the gating Result will handle it.
+                        * Transfer sortgroupref data to the replacement tlist, if
+                        * requested (use_physical_tlist checked that this will work).
                         */
-                       if (!gating_clauses)
+                       if (flags & CP_LABEL_TLIST)
                                apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
                }
                else
@@ -605,7 +607,7 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
                        else
                        {
                                /* As above, transfer sortgroupref data to replacement tlist */
-                               if (!gating_clauses)
+                               if (flags & CP_LABEL_TLIST)
                                        apply_pathtarget_labeling_to_tlist(tlist, best_path->pathtarget);
                        }
                }
@@ -703,6 +705,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
                                                                                                                        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,
@@ -934,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
@@ -944,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.
@@ -1032,14 +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;
-       List       *partpruneinfos = NIL;
+       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
@@ -1048,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)
        {
@@ -1065,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)
        {
@@ -1074,10 +1145,73 @@ 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);
        }
 
-       if (rel->reloptkind == RELOPT_BASEREL &&
+       /*
+        * 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;
@@ -1086,7 +1220,6 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
 
                if (best_path->path.param_info)
                {
-
                        List       *prmquals = best_path->path.param_info->ppi_clauses;
 
                        prmquals = extract_actual_clauses(prmquals, false);
@@ -1096,33 +1229,34 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
                        prunequal = list_concat(prunequal, prmquals);
                }
 
-               /*
-                * If any quals exist, they may be useful to perform further partition
-                * pruning during execution.  Generate a PartitionPruneInfo for each
-                * partitioned rel to store these quals and allow translation of
-                * partition indexes into subpath indexes.
-                */
                if (prunequal != NIL)
-                       partpruneinfos =
-                               make_partition_pruneinfo(root,
+                       partpruneinfo =
+                               make_partition_pruneinfo(root, rel,
+                                                                                best_path->subpaths,
                                                                                 best_path->partitioned_rels,
-                                                                                best_path->subpaths, prunequal);
+                                                                                prunequal);
        }
 
-       /*
-        * XXX ideally, if there's just one child, we'd not bother to generate an
-        * Append node but just return the single child.  At the moment this does
-        * not work because the varno of the child scan plan won't match the
-        * parent-rel Vars it'll be asked to emit.
-        */
-
-       plan = make_append(subplans, best_path->first_partial_path,
-                                          tlist, best_path->partitioned_rels,
-                                          partpruneinfos);
+       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;
 }
 
 /*
@@ -1133,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
@@ -1154,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,
@@ -1164,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
@@ -1226,22 +1371,63 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
                subplans = lappend(subplans, subplan);
        }
 
-       node->partitioned_rels = best_path->partitioned_rels;
+       /*
+        * If any quals exist, they may be useful to perform further partition
+        * pruning during execution.  Gather information needed by the executor to
+        * do partition pruning.
+        */
+       if (enable_partition_pruning &&
+               rel->reloptkind == RELOPT_BASEREL &&
+               best_path->partitioned_rels != NIL)
+       {
+               List       *prunequal;
+
+               prunequal = extract_actual_clauses(rel->baserestrictinfo, false);
+
+               if (best_path->path.param_info)
+               {
+                       List       *prmquals = best_path->path.param_info->ppi_clauses;
+
+                       prmquals = extract_actual_clauses(prmquals, false);
+                       prmquals = (List *) replace_nestloop_params(root,
+                                                                                                               (Node *) prmquals);
+
+                       prunequal = list_concat(prunequal, prmquals);
+               }
+
+               if (prunequal != NIL)
+                       partpruneinfo = make_partition_pruneinfo(root, rel,
+                                                                                                        best_path->subpaths,
+                                                                                                        best_path->partitioned_rels,
+                                                                                                        prunequal);
+       }
+
        node->mergeplans = subplans;
+       node->part_prune_info = partpruneinfo;
 
-       return (Plan *) node;
+       /*
+        * 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;
@@ -1331,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;
 
@@ -1383,20 +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,
-                                                                                        best_path->path.parallel_safe);
-               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
@@ -1407,6 +1584,7 @@ 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)
@@ -1417,7 +1595,9 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
                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)
@@ -1455,6 +1635,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
                                                                 numGroupCols,
                                                                 groupColIdx,
                                                                 groupOperators,
+                                                                groupCollations,
                                                                 NIL,
                                                                 NIL,
                                                                 best_path->path.rows,
@@ -1540,7 +1721,7 @@ create_gather_plan(PlannerInfo *root, GatherPath *best_path)
        gather_plan = make_gather(tlist,
                                                          NIL,
                                                          best_path->num_workers,
-                                                         SS_assign_special_param(root),
+                                                         assign_special_exec_param(root),
                                                          best_path->single_copy,
                                                          subplan);
 
@@ -1576,7 +1757,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
        copy_generic_path_info(&gm_plan->plan, &best_path->path);
 
        /* Assign the rescan Param. */
-       gm_plan->rescan_param = SS_assign_special_param(root);
+       gm_plan->rescan_param = assign_special_exec_param(root);
 
        /* Gather Merge is pointless with no pathkeys; use Gather instead. */
        Assert(pathkeys != NIL);
@@ -1647,7 +1828,7 @@ create_projection_plan(PlannerInfo *root, ProjectionPath *best_path, int flags)
                 */
                subplan = create_plan_recurse(root, best_path->subpath, 0);
                tlist = subplan->targetlist;
-               if ((flags & CP_LABEL_TLIST) != 0)
+               if (flags & CP_LABEL_TLIST)
                        apply_pathtarget_labeling_to_tlist(tlist,
                                                                                           best_path->path.pathtarget);
        }
@@ -1738,6 +1919,40 @@ inject_projection_plan(Plan *subplan, List *tlist, bool 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
  *
@@ -1803,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);
@@ -1869,6 +2086,8 @@ create_agg_plan(PlannerInfo *root, AggPath *best_path)
                                        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,
@@ -1979,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;
 
@@ -2030,6 +2249,7 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path)
                                                                                 list_length((List *) linitial(rollup->gsets)),
                                                                                 new_grpColIdx,
                                                                                 extract_grouping_ops(rollup->groupClause),
+                                                                                extract_grouping_collations(rollup->groupClause, subplan->targetlist),
                                                                                 rollup->gsets,
                                                                                 NIL,
                                                                                 rollup->numGroups,
@@ -2067,6 +2287,7 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path)
                                                numGroupCols,
                                                top_grpColIdx,
                                                extract_grouping_ops(rollup->groupClause),
+                                               extract_grouping_collations(rollup->groupClause, subplan->targetlist),
                                                rollup->gsets,
                                                chain,
                                                rollup->numGroups,
@@ -2141,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;
 
@@ -2159,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
@@ -2182,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,
@@ -2215,9 +2451,11 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path)
                                                  partNumCols,
                                                  partColIdx,
                                                  partOperators,
+                                                 partCollations,
                                                  ordNumCols,
                                                  ordColIdx,
                                                  ordOperators,
+                                                 ordCollations,
                                                  wc->frameOptions,
                                                  wc->startOffset,
                                                  wc->endOffset,
@@ -2233,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
  *
@@ -2479,10 +2611,11 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path)
                                                        best_path->operation,
                                                        best_path->canSetTag,
                                                        best_path->nominalRelation,
-                                                       best_path->partitioned_rels,
+                                                       best_path->rootRelation,
                                                        best_path->partColsUpdated,
                                                        best_path->resultRelations,
                                                        subplans,
+                                                       best_path->subroots,
                                                        best_path->withCheckOptionLists,
                                                        best_path->returningLists,
                                                        best_path->rowMarks,
@@ -2628,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;
@@ -2644,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
+        * 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.)
         */
-       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.
-        */
-       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.
@@ -2669,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,
@@ -2695,12 +2826,11 @@ create_indexscan_plan(PlannerInfo *root,
 
                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, false))
+                       predicate_implied_by(list_make1(rinfo->clause), stripped_indexquals,
+                                                                false))
                        continue;                       /* provably implied by indexquals */
                qpqual = lappend(qpqual, rinfo);
        }
@@ -3061,6 +3191,8 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
        {
                IndexPath  *ipath = (IndexPath *) bitmapqual;
                IndexScan  *iscan;
+               List       *subquals;
+               List       *subindexquals;
                List       *subindexECs;
                ListCell   *l;
 
@@ -3081,8 +3213,23 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
                plan->plan_width = 0;   /* meaningless */
                plan->parallel_aware = false;
                plan->parallel_safe = ipath->path.parallel_safe;
-               *qual = get_actual_clauses(ipath->indexclauses);
-               *indexqual = get_actual_clauses(ipath->indexquals);
+               /* 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);
@@ -3093,21 +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,
-                                                                         false))
+                       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
@@ -3131,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)
        {
@@ -3152,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,
@@ -3483,6 +3668,44 @@ create_namedtuplestorescan_plan(PlannerInfo *root, Path *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;
+}
+
 /*
  * create_worktablescan_plan
  *      Returns a worktablescan plan for the base relation scanned by 'best_path'
@@ -3777,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);
@@ -3802,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
@@ -3822,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,
@@ -3917,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
@@ -4213,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
@@ -4351,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);
@@ -4423,255 +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 = (Var *) 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 = lfirst_node(RestrictInfo, lcc);
-               int                     indexcol = lfirst_int(lci);
-               Node       *clause;
-
-               /*
-                * 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;
+               IndexClause *iclause = lfirst_node(IndexClause, lc);
+               int                     indexcol = iclause->indexcol;
+               ListCell   *lc2;
 
-                       /*
-                        * 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))
+               foreach(lc2, iclause->indexquals)
                {
-                       ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
+                       RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
+                       Node       *clause = (Node *) rinfo->clause;
 
-                       /* Never need to commute... */
-
-                       /* Replace the indexkey expression with an index Var. */
-                       linitial(saop->args) = fix_indexqual_operand(linitial(saop->args),
-                                                                                                                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 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));
-
-               fixed_indexquals = lappend(fixed_indexquals, clause);
        }
 
-       return fixed_indexquals;
+       *stripped_indexquals_p = stripped_indexquals;
+       *fixed_indexquals_p = fixed_indexquals;
 }
 
 /*
@@ -4679,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)
@@ -4700,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;
+}
 
-                       if (list_length(op->args) != 2)
-                               elog(ERROR, "indexorderby clause is not binary opclause");
+/*
+ * 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);
 
-                       /*
-                        * Now replace the indexkey expression with an index Var.
-                        */
-                       linitial(op->args) = fix_indexqual_operand(linitial(op->args),
-                                                                                                          index,
-                                                                                                          indexcol);
+       if (IsA(clause, OpExpr))
+       {
+               OpExpr     *op = (OpExpr *) clause;
+
+               /* 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;
 
-       return fixed_indexorderbys;
+               /* 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 clause;
 }
 
 /*
@@ -5409,25 +5430,6 @@ make_foreignscan(List *qptlist,
        return node;
 }
 
-static Append *
-make_append(List *appendplans, int first_partial_plan,
-                       List *tlist, List *partitioned_rels,
-                       List *partpruneinfos)
-{
-       Append     *node = makeNode(Append);
-       Plan       *plan = &node->plan;
-
-       plan->targetlist = tlist;
-       plan->qual = NIL;
-       plan->lefttree = NULL;
-       plan->righttree = NULL;
-       node->partitioned_rels = partitioned_rels;
-       node->appendplans = appendplans;
-       node->first_partial_plan = first_partial_plan;
-       node->part_prune_infos = partpruneinfos;
-       return node;
-}
-
 static RecursiveUnion *
 make_recursive_union(List *tlist,
                                         Plan *lefttree,
@@ -5456,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)
                {
@@ -5469,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;
 
@@ -6145,7 +6151,7 @@ materialize_finished_plan(Plan *subplan)
 Agg *
 make_agg(List *tlist, List *qual,
                 AggStrategy aggstrategy, AggSplit aggsplit,
-                int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators,
+                int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators, Oid *grpCollations,
                 List *groupingSets, List *chain,
                 double dNumGroups, Plan *lefttree)
 {
@@ -6161,6 +6167,7 @@ make_agg(List *tlist, List *qual,
        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;
@@ -6176,8 +6183,8 @@ 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,
@@ -6190,9 +6197,11 @@ 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;
@@ -6217,6 +6226,7 @@ make_group(List *tlist,
                   int numGroupCols,
                   AttrNumber *grpColIdx,
                   Oid *grpOperators,
+                  Oid *grpCollations,
                   Plan *lefttree)
 {
        Group      *node = makeNode(Group);
@@ -6225,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;
@@ -6248,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;
@@ -6262,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)
        {
@@ -6270,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++;
        }
@@ -6277,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;
 }
@@ -6292,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;
@@ -6307,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)
        {
@@ -6375,6 +6392,7 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 
                uniqColIdx[keyno] = tle->resno;
                uniqOperators[keyno] = eqop;
+               uniqCollations[keyno] = ec->ec_collation;
 
                keyno++;
        }
@@ -6382,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;
 }
@@ -6426,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;
@@ -6439,6 +6459,7 @@ make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
         */
        dupColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
        dupOperators = (Oid *) palloc(sizeof(Oid) * numCols);
+       dupCollations = (Oid *) palloc(sizeof(Oid) * numCols);
 
        foreach(slitem, distinctList)
        {
@@ -6447,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++;
        }
@@ -6456,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;
@@ -6552,9 +6575,9 @@ make_project_set(List *tlist,
 static ModifyTable *
 make_modifytable(PlannerInfo *root,
                                 CmdType operation, bool canSetTag,
-                                Index nominalRelation, List *partitioned_rels,
+                                Index nominalRelation, Index rootRelation,
                                 bool partColsUpdated,
-                                List *resultRelations, List *subplans,
+                                List *resultRelations, List *subplans, List *subroots,
                                 List *withCheckOptionLists, List *returningLists,
                                 List *rowMarks, OnConflictExpr *onconflict, int epqParam)
 {
@@ -6562,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 ||
@@ -6579,7 +6604,7 @@ make_modifytable(PlannerInfo *root,
        node->operation = operation;
        node->canSetTag = canSetTag;
        node->nominalRelation = nominalRelation;
-       node->partitioned_rels = partitioned_rels;
+       node->rootRelation = rootRelation;
        node->partColsUpdated = partColsUpdated;
        node->resultRelations = resultRelations;
        node->resultRelIndex = -1;      /* will be set correctly in setrefs.c */
@@ -6623,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;
@@ -6637,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)
@@ -6657,8 +6683,9 @@ make_modifytable(PlannerInfo *root,
 
                /*
                 * Try to modify the foreign table directly if (1) the FDW provides
-                * callback functions needed for that, (2) there are no row-level
-                * triggers on the foreign table, and (3) there are no WITH CHECK
+                * 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;
@@ -6668,15 +6695,16 @@ make_modifytable(PlannerInfo *root,
                        fdwroutine->IterateDirectModify != NULL &&
                        fdwroutine->EndDirectModify != NULL &&
                        withCheckOptionLists == NIL &&
-                       !has_row_triggers(root, rti, operation))
-                       direct_modify = fdwroutine->PlanDirectModify(root, node, rti, i);
+                       !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);
@@ -6712,12 +6740,11 @@ 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:
 
                        /*