create_foreignscan_path(root, baserel,
                                                                         NULL,  /* default pathtarget */
                                                                         baserel->rows,
+                                                                        0,
                                                                         startup_cost,
                                                                         total_cost,
                                                                         NIL,   /* no pathkeys */
 
                                                                        List *pathkeys,
                                                                        PgFdwPathExtraData *fpextra,
                                                                        double *p_rows, int *p_width,
+                                                                       int *p_disabled_nodes,
                                                                        Cost *p_startup_cost, Cost *p_total_cost);
 static void get_remote_estimate(const char *sql,
                                                                PGconn *conn,
                                                                                          double retrieved_rows,
                                                                                          double width,
                                                                                          double limit_tuples,
+                                                                                         int *disabled_nodes,
                                                                                          Cost *p_startup_cost,
                                                                                          Cost *p_run_cost);
 static bool ec_member_matches_foreign(PlannerInfo *root, RelOptInfo *rel,
                 */
                estimate_path_cost_size(root, baserel, NIL, NIL, NULL,
                                                                &fpinfo->rows, &fpinfo->width,
+                                                               &fpinfo->disabled_nodes,
                                                                &fpinfo->startup_cost, &fpinfo->total_cost);
 
                /* Report estimated baserel size to planner. */
                /* Fill in basically-bogus cost estimates for use later. */
                estimate_path_cost_size(root, baserel, NIL, NIL, NULL,
                                                                &fpinfo->rows, &fpinfo->width,
+                                                               &fpinfo->disabled_nodes,
                                                                &fpinfo->startup_cost, &fpinfo->total_cost);
        }
 
        path = create_foreignscan_path(root, baserel,
                                                                   NULL,        /* default pathtarget */
                                                                   fpinfo->rows,
+                                                                  fpinfo->disabled_nodes,
                                                                   fpinfo->startup_cost,
                                                                   fpinfo->total_cost,
                                                                   NIL, /* no pathkeys */
                ParamPathInfo *param_info = (ParamPathInfo *) lfirst(lc);
                double          rows;
                int                     width;
+               int                     disabled_nodes;
                Cost            startup_cost;
                Cost            total_cost;
 
                /* Get a cost estimate from the remote */
                estimate_path_cost_size(root, baserel,
                                                                param_info->ppi_clauses, NIL, NULL,
-                                                               &rows, &width,
+                                                               &rows, &width, &disabled_nodes,
                                                                &startup_cost, &total_cost);
 
                /*
                path = create_foreignscan_path(root, baserel,
                                                                           NULL,        /* default pathtarget */
                                                                           rows,
+                                                                          disabled_nodes,
                                                                           startup_cost,
                                                                           total_cost,
                                                                           NIL, /* no pathkeys */
  * final sort and the LIMIT restriction.
  *
  * The function returns the cost and size estimates in p_rows, p_width,
- * p_startup_cost and p_total_cost variables.
+ * p_disabled_nodes, p_startup_cost and p_total_cost variables.
  */
 static void
 estimate_path_cost_size(PlannerInfo *root,
                                                List *pathkeys,
                                                PgFdwPathExtraData *fpextra,
                                                double *p_rows, int *p_width,
+                                               int *p_disabled_nodes,
                                                Cost *p_startup_cost, Cost *p_total_cost)
 {
        PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
        double          rows;
        double          retrieved_rows;
        int                     width;
+       int                     disabled_nodes = 0;
        Cost            startup_cost;
        Cost            total_cost;
 
                                adjust_foreign_grouping_path_cost(root, pathkeys,
                                                                                                  retrieved_rows, width,
                                                                                                  fpextra->limit_tuples,
+                                                                                                 &disabled_nodes,
                                                                                                  &startup_cost, &run_cost);
                        }
                        else
        /* Return results. */
        *p_rows = rows;
        *p_width = width;
+       *p_disabled_nodes = disabled_nodes;
        *p_startup_cost = startup_cost;
        *p_total_cost = total_cost;
 }
                                                                  double retrieved_rows,
                                                                  double width,
                                                                  double limit_tuples,
+                                                                 int *p_disabled_nodes,
                                                                  Cost *p_startup_cost,
                                                                  Cost *p_run_cost)
 {
                cost_sort(&sort_path,
                                  root,
                                  pathkeys,
+                                 0,
                                  *p_startup_cost + *p_run_cost,
                                  retrieved_rows,
                                  width,
        {
                double          rows;
                int                     width;
+               int                     disabled_nodes;
                Cost            startup_cost;
                Cost            total_cost;
                List       *useful_pathkeys = lfirst(lc);
                Path       *sorted_epq_path;
 
                estimate_path_cost_size(root, rel, NIL, useful_pathkeys, NULL,
-                                                               &rows, &width, &startup_cost, &total_cost);
+                                                               &rows, &width, &disabled_nodes,
+                                                               &startup_cost, &total_cost);
 
                /*
                 * The EPQ path must be at least as well sorted as the path itself, in
                                         create_foreignscan_path(root, rel,
                                                                                         NULL,
                                                                                         rows,
+                                                                                        disabled_nodes,
                                                                                         startup_cost,
                                                                                         total_cost,
                                                                                         useful_pathkeys,
                                         create_foreign_join_path(root, rel,
                                                                                          NULL,
                                                                                          rows,
+                                                                                         disabled_nodes,
                                                                                          startup_cost,
                                                                                          total_cost,
                                                                                          useful_pathkeys,
        ForeignPath *joinpath;
        double          rows;
        int                     width;
+       int                     disabled_nodes;
        Cost            startup_cost;
        Cost            total_cost;
        Path       *epq_path;           /* Path to create plan to be executed when
 
        /* Estimate costs for bare join relation */
        estimate_path_cost_size(root, joinrel, NIL, NIL, NULL,
-                                                       &rows, &width, &startup_cost, &total_cost);
+                                                       &rows, &width, &disabled_nodes,
+                                                       &startup_cost, &total_cost);
        /* Now update this information in the joinrel */
        joinrel->rows = rows;
        joinrel->reltarget->width = width;
        fpinfo->rows = rows;
        fpinfo->width = width;
+       fpinfo->disabled_nodes = disabled_nodes;
        fpinfo->startup_cost = startup_cost;
        fpinfo->total_cost = total_cost;
 
                                                                                joinrel,
                                                                                NULL,   /* default pathtarget */
                                                                                rows,
+                                                                               disabled_nodes,
                                                                                startup_cost,
                                                                                total_cost,
                                                                                NIL,    /* no pathkeys */
        ForeignPath *grouppath;
        double          rows;
        int                     width;
+       int                     disabled_nodes;
        Cost            startup_cost;
        Cost            total_cost;
 
 
        /* Estimate the cost of push down */
        estimate_path_cost_size(root, grouped_rel, NIL, NIL, NULL,
-                                                       &rows, &width, &startup_cost, &total_cost);
+                                                       &rows, &width, &disabled_nodes,
+                                                       &startup_cost, &total_cost);
 
        /* Now update this information in the fpinfo */
        fpinfo->rows = rows;
        fpinfo->width = width;
+       fpinfo->disabled_nodes = disabled_nodes;
        fpinfo->startup_cost = startup_cost;
        fpinfo->total_cost = total_cost;
 
                                                                                  grouped_rel,
                                                                                  grouped_rel->reltarget,
                                                                                  rows,
+                                                                                 disabled_nodes,
                                                                                  startup_cost,
                                                                                  total_cost,
                                                                                  NIL,  /* no pathkeys */
        PgFdwPathExtraData *fpextra;
        double          rows;
        int                     width;
+       int                     disabled_nodes;
        Cost            startup_cost;
        Cost            total_cost;
        List       *fdw_private;
 
        /* Estimate the costs of performing the final sort remotely */
        estimate_path_cost_size(root, input_rel, NIL, root->sort_pathkeys, fpextra,
-                                                       &rows, &width, &startup_cost, &total_cost);
+                                                       &rows, &width, &disabled_nodes,
+                                                       &startup_cost, &total_cost);
 
        /*
         * Build the fdw_private list that will be used by postgresGetForeignPlan.
                                                                                         input_rel,
                                                                                         root->upper_targets[UPPERREL_ORDERED],
                                                                                         rows,
+                                                                                        disabled_nodes,
                                                                                         startup_cost,
                                                                                         total_cost,
                                                                                         root->sort_pathkeys,
        bool            save_use_remote_estimate = false;
        double          rows;
        int                     width;
+       int                     disabled_nodes;
        Cost            startup_cost;
        Cost            total_cost;
        List       *fdw_private;
                                                                                                           path->parent,
                                                                                                           path->pathtarget,
                                                                                                           path->rows,
+                                                                                                          path->disabled_nodes,
                                                                                                           path->startup_cost,
                                                                                                           path->total_cost,
                                                                                                           path->pathkeys,
                ifpinfo->use_remote_estimate = false;
        }
        estimate_path_cost_size(root, input_rel, NIL, pathkeys, fpextra,
-                                                       &rows, &width, &startup_cost, &total_cost);
+                                                       &rows, &width, &disabled_nodes,
+                                                       &startup_cost, &total_cost);
        if (!fpextra->has_final_sort)
                ifpinfo->use_remote_estimate = save_use_remote_estimate;
 
                                                                                   input_rel,
                                                                                   root->upper_targets[UPPERREL_FINAL],
                                                                                   rows,
+                                                                                  disabled_nodes,
                                                                                   startup_cost,
                                                                                   total_cost,
                                                                                   pathkeys,
 
        /* Estimated size and cost for a scan, join, or grouping/aggregation. */
        double          rows;
        int                     width;
+       int                     disabled_nodes;
        Cost            startup_cost;
        Cost            total_cost;
 
 
  * so beware of division-by-zero.)     The LIMIT is applied as a top-level
  * plan node.
  *
+ * Each path stores the total number of disabled nodes that exist at or
+ * below that point in the plan tree. This is regarded as a component of
+ * the cost, and paths with fewer disabled nodes should be regarded as
+ * cheaper than those with more. Disabled nodes occur when the user sets
+ * a GUC like enable_seqscan=false. We can't necessarily respect such a
+ * setting in every part of the plan tree, but we want to respect in as many
+ * parts of the plan tree as possible. Simpler schemes like storing a Boolean
+ * here rather than a count fail to do that. We used to disable nodes by
+ * adding a large constant to the startup cost, but that distorted planning
+ * in other ways.
+ *
  * For largely historical reasons, most of the routines in this module use
  * the passed result Path only to store their results (rows, startup_cost and
  * total_cost) into.  All the input data they need is passed as separate
        else
                path->rows = baserel->rows;
 
-       if (!enable_seqscan)
-               startup_cost += disable_cost;
-
        /* fetch estimated page cost for tablespace containing table */
        get_tablespace_page_costs(baserel->reltablespace,
                                                          NULL,
                path->rows = clamp_row_est(path->rows / parallel_divisor);
        }
 
+       path->disabled_nodes = enable_seqscan ? 0 : 1;
        path->startup_cost = startup_cost;
        path->total_cost = startup_cost + cpu_run_cost + disk_run_cost;
 }
        startup_cost += path->pathtarget->cost.startup;
        run_cost += path->pathtarget->cost.per_tuple * path->rows;
 
+       path->disabled_nodes = 0;
        path->startup_cost = startup_cost;
        path->total_cost = startup_cost + run_cost;
 }
        startup_cost += parallel_setup_cost;
        run_cost += parallel_tuple_cost * path->path.rows;
 
+       path->path.disabled_nodes = path->subpath->disabled_nodes;
        path->path.startup_cost = startup_cost;
        path->path.total_cost = (startup_cost + run_cost);
 }
 void
 cost_gather_merge(GatherMergePath *path, PlannerInfo *root,
                                  RelOptInfo *rel, ParamPathInfo *param_info,
+                                 int input_disabled_nodes,
                                  Cost input_startup_cost, Cost input_total_cost,
                                  double *rows)
 {
        else
                path->path.rows = rel->rows;
 
-       if (!enable_gathermerge)
-               startup_cost += disable_cost;
-
        /*
         * Add one to the number of workers to account for the leader.  This might
         * be overgenerous since the leader will do less work than other workers
        startup_cost += parallel_setup_cost;
        run_cost += parallel_tuple_cost * path->path.rows * 1.05;
 
+       path->path.disabled_nodes = input_disabled_nodes
+               + (enable_gathermerge ? 0 : 1);
        path->path.startup_cost = startup_cost + input_startup_cost;
        path->path.total_cost = (startup_cost + run_cost + input_total_cost);
 }
                                                                                          path->indexclauses);
        }
 
-       if (!enable_indexscan)
-               startup_cost += disable_cost;
        /* we don't need to check enable_indexonlyscan; indxpath.c does that */
+       path->path.disabled_nodes = enable_indexscan ? 0 : 1;
 
        /*
         * Call index-access-method-specific code to estimate the processing cost
        else
                path->rows = baserel->rows;
 
-       if (!enable_bitmapscan)
-               startup_cost += disable_cost;
-
        pages_fetched = compute_bitmap_pages(root, baserel, bitmapqual,
                                                                                 loop_count, &indexTotalCost,
                                                                                 &tuples_fetched);
        startup_cost += path->pathtarget->cost.startup;
        run_cost += path->pathtarget->cost.per_tuple * path->rows;
 
+       path->disabled_nodes = enable_bitmapscan ? 0 : 1;
        path->startup_cost = startup_cost;
        path->total_cost = startup_cost + run_cost;
 }
        }
        path->bitmapselectivity = selec;
        path->path.rows = 0;            /* per above, not used */
+       path->path.disabled_nodes = 0;
        path->path.startup_cost = totalCost;
        path->path.total_cost = totalCost;
 }
        /* Should only be applied to base relations */
        Assert(baserel->relid > 0);
        Assert(baserel->rtekind == RTE_RELATION);
+       Assert(tidquals != NIL);
 
        /* Mark the path with the correct row estimate */
        if (param_info)
                RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
                Expr       *qual = rinfo->clause;
 
+               /*
+                * We must use a TID scan for CurrentOfExpr; in any other case, we
+                * should be generating a TID scan only if enable_tidscan=true. Also,
+                * if CurrentOfExpr is the qual, there should be only one.
+                */
+               Assert(enable_tidscan || IsA(qual, CurrentOfExpr));
+               Assert(list_length(tidquals) == 1 || !IsA(qual, CurrentOfExpr));
+
                if (IsA(qual, ScalarArrayOpExpr))
                {
                        /* Each element of the array yields 1 tuple */
        startup_cost += path->pathtarget->cost.startup;
        run_cost += path->pathtarget->cost.per_tuple * path->rows;
 
+       /*
+        * There are assertions above verifying that we only reach this function
+        * either when enable_tidscan=true or when the TID scan is the only legal
+        * path, so it's safe to set disabled_nodes to zero here.
+        */
+       path->disabled_nodes = 0;
        path->startup_cost = startup_cost;
        path->total_cost = startup_cost + run_cost;
 }
        startup_cost += path->pathtarget->cost.startup;
        run_cost += path->pathtarget->cost.per_tuple * path->rows;
 
+       /* we should not generate this path type when enable_tidscan=false */
+       Assert(enable_tidscan);
+       path->disabled_nodes = 0;
        path->startup_cost = startup_cost;
        path->total_cost = startup_cost + run_cost;
 }
         * SubqueryScan node, plus cpu_tuple_cost to account for selection and
         * projection overhead.
         */
+       path->path.disabled_nodes = path->subpath->disabled_nodes;
        path->path.startup_cost = path->subpath->startup_cost;
        path->path.total_cost = path->subpath->total_cost;
 
        startup_cost += path->pathtarget->cost.startup;
        run_cost += path->pathtarget->cost.per_tuple * path->rows;
 
+       path->disabled_nodes = 0;
        path->startup_cost = startup_cost;
        path->total_cost = startup_cost + run_cost;
 }
        startup_cost += path->pathtarget->cost.startup;
        run_cost += path->pathtarget->cost.per_tuple * path->rows;
 
+       path->disabled_nodes = 0;
        path->startup_cost = startup_cost;
        path->total_cost = startup_cost + run_cost;
 }
        startup_cost += path->pathtarget->cost.startup;
        run_cost += path->pathtarget->cost.per_tuple * path->rows;
 
+       path->disabled_nodes = 0;
        path->startup_cost = startup_cost;
        path->total_cost = startup_cost + run_cost;
 }
        startup_cost += path->pathtarget->cost.startup;
        run_cost += path->pathtarget->cost.per_tuple * path->rows;
 
+       path->disabled_nodes = 0;
        path->startup_cost = startup_cost;
        path->total_cost = startup_cost + run_cost;
 }
        cpu_per_tuple += cpu_tuple_cost + qpqual_cost.per_tuple;
        run_cost += cpu_per_tuple * baserel->tuples;
 
+       path->disabled_nodes = 0;
        path->startup_cost = startup_cost;
        path->total_cost = startup_cost + run_cost;
 }
        cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
        run_cost += cpu_per_tuple * baserel->tuples;
 
+       path->disabled_nodes = 0;
        path->startup_cost = startup_cost;
        path->total_cost = startup_cost + run_cost;
 }
         */
        total_cost += cpu_tuple_cost * total_rows;
 
+       runion->disabled_nodes = nrterm->disabled_nodes + rterm->disabled_nodes;
        runion->startup_cost = startup_cost;
        runion->total_cost = total_cost;
        runion->rows = total_rows;
 void
 cost_incremental_sort(Path *path,
                                          PlannerInfo *root, List *pathkeys, int presorted_keys,
+                                         int input_disabled_nodes,
                                          Cost input_startup_cost, Cost input_total_cost,
                                          double input_tuples, int width, Cost comparison_cost, int sort_mem,
                                          double limit_tuples)
        run_cost += 2.0 * cpu_tuple_cost * input_groups;
 
        path->rows = input_tuples;
+
+       /* should not generate these paths when enable_incremental_sort=false */
+       Assert(enable_incremental_sort);
+       path->disabled_nodes = input_disabled_nodes;
+
        path->startup_cost = startup_cost;
        path->total_cost = startup_cost + run_cost;
 }
  */
 void
 cost_sort(Path *path, PlannerInfo *root,
-                 List *pathkeys, Cost input_cost, double tuples, int width,
+                 List *pathkeys, int input_disabled_nodes,
+                 Cost input_cost, double tuples, int width,
                  Cost comparison_cost, int sort_mem,
                  double limit_tuples)
 
                                   comparison_cost, sort_mem,
                                   limit_tuples);
 
-       if (!enable_sort)
-               startup_cost += disable_cost;
-
        startup_cost += input_cost;
 
        path->rows = tuples;
+       path->disabled_nodes = input_disabled_nodes + (enable_sort ? 0 : 1);
        path->startup_cost = startup_cost;
        path->total_cost = startup_cost + run_cost;
 }
 {
        ListCell   *l;
 
+       apath->path.disabled_nodes = 0;
        apath->path.startup_cost = 0;
        apath->path.total_cost = 0;
        apath->path.rows = 0;
                         */
                        apath->path.startup_cost = firstsubpath->startup_cost;
 
-                       /* Compute rows and costs as sums of subplan rows and costs. */
+                       /*
+                        * Compute rows, number of disabled nodes, and total cost as sums
+                        * of underlying subplan values.
+                        */
                        foreach(l, apath->subpaths)
                        {
                                Path       *subpath = (Path *) lfirst(l);
 
                                apath->path.rows += subpath->rows;
+                               apath->path.disabled_nodes += subpath->disabled_nodes;
                                apath->path.total_cost += subpath->total_cost;
                        }
                }
                                        cost_sort(&sort_path,
                                                          NULL, /* doesn't currently need root */
                                                          pathkeys,
+                                                         subpath->disabled_nodes,
                                                          subpath->total_cost,
                                                          subpath->rows,
                                                          subpath->pathtarget->width,
                                }
 
                                apath->path.rows += subpath->rows;
+                               apath->path.disabled_nodes += subpath->disabled_nodes;
                                apath->path.startup_cost += subpath->startup_cost;
                                apath->path.total_cost += subpath->total_cost;
                        }
                                apath->path.total_cost += subpath->total_cost;
                        }
 
+                       apath->path.disabled_nodes += subpath->disabled_nodes;
                        apath->path.rows = clamp_row_est(apath->path.rows);
 
                        i++;
  *
  * 'pathkeys' is a list of sort keys
  * 'n_streams' is the number of input streams
+ * 'input_disabled_nodes' is the sum of the input streams' disabled node counts
  * 'input_startup_cost' is the sum of the input streams' startup costs
  * 'input_total_cost' is the sum of the input streams' total costs
  * 'tuples' is the number of tuples in all the streams
 void
 cost_merge_append(Path *path, PlannerInfo *root,
                                  List *pathkeys, int n_streams,
+                                 int input_disabled_nodes,
                                  Cost input_startup_cost, Cost input_total_cost,
                                  double tuples)
 {
         */
        run_cost += cpu_tuple_cost * APPEND_CPU_COST_MULTIPLIER * tuples;
 
+       path->disabled_nodes = input_disabled_nodes;
        path->startup_cost = startup_cost + input_startup_cost;
        path->total_cost = startup_cost + run_cost + input_total_cost;
 }
  */
 void
 cost_material(Path *path,
+                         int input_disabled_nodes,
                          Cost input_startup_cost, Cost input_total_cost,
                          double tuples, int width)
 {
                run_cost += seq_page_cost * npages;
        }
 
+       path->disabled_nodes = input_disabled_nodes + (enable_material ? 0 : 1);
        path->startup_cost = startup_cost;
        path->total_cost = startup_cost + run_cost;
 }
                 AggStrategy aggstrategy, const AggClauseCosts *aggcosts,
                 int numGroupCols, double numGroups,
                 List *quals,
+                int disabled_nodes,
                 Cost input_startup_cost, Cost input_total_cost,
                 double input_tuples, double input_width)
 {
                startup_cost = input_startup_cost;
                total_cost = input_total_cost;
                if (aggstrategy == AGG_MIXED && !enable_hashagg)
-               {
-                       startup_cost += disable_cost;
-                       total_cost += disable_cost;
-               }
+                       ++disabled_nodes;
                /* calcs phrased this way to match HASHED case, see note above */
                total_cost += aggcosts->transCost.startup;
                total_cost += aggcosts->transCost.per_tuple * input_tuples;
                /* must be AGG_HASHED */
                startup_cost = input_total_cost;
                if (!enable_hashagg)
-                       startup_cost += disable_cost;
+                       ++disabled_nodes;
                startup_cost += aggcosts->transCost.startup;
                startup_cost += aggcosts->transCost.per_tuple * input_tuples;
                /* cost of computing hash value */
        }
 
        path->rows = output_tuples;
+       path->disabled_nodes = disabled_nodes;
        path->startup_cost = startup_cost;
        path->total_cost = total_cost;
 }
 void
 cost_windowagg(Path *path, PlannerInfo *root,
                           List *windowFuncs, WindowClause *winclause,
+                          int input_disabled_nodes,
                           Cost input_startup_cost, Cost input_total_cost,
                           double input_tuples)
 {
        total_cost += cpu_tuple_cost * input_tuples;
 
        path->rows = input_tuples;
+       path->disabled_nodes = input_disabled_nodes;
        path->startup_cost = startup_cost;
        path->total_cost = total_cost;
 
 cost_group(Path *path, PlannerInfo *root,
                   int numGroupCols, double numGroups,
                   List *quals,
+                  int input_disabled_nodes,
                   Cost input_startup_cost, Cost input_total_cost,
                   double input_tuples)
 {
        }
 
        path->rows = output_tuples;
+       path->disabled_nodes = input_disabled_nodes;
        path->startup_cost = startup_cost;
        path->total_cost = total_cost;
 }
                                          Path *outer_path, Path *inner_path,
                                          JoinPathExtraData *extra)
 {
+       int                     disabled_nodes;
        Cost            startup_cost = 0;
        Cost            run_cost = 0;
        double          outer_path_rows = outer_path->rows;
        Cost            inner_run_cost;
        Cost            inner_rescan_run_cost;
 
+       /* Count up disabled nodes. */
+       disabled_nodes = enable_nestloop ? 0 : 1;
+       disabled_nodes += inner_path->disabled_nodes;
+       disabled_nodes += outer_path->disabled_nodes;
+
        /* estimate costs to rescan the inner relation */
        cost_rescan(root, inner_path,
                                &inner_rescan_start_cost,
        /* CPU costs left for later */
 
        /* Public result fields */
+       workspace->disabled_nodes = disabled_nodes;
        workspace->startup_cost = startup_cost;
        workspace->total_cost = startup_cost + run_cost;
        /* Save private data for final_cost_nestloop */
        QualCost        restrict_qual_cost;
        double          ntuples;
 
+       /* Set the number of disabled nodes. */
+       path->jpath.path.disabled_nodes = workspace->disabled_nodes;
+
        /* Protect some assumptions below that rowcounts aren't zero */
        if (outer_path_rows <= 0)
                outer_path_rows = 1;
                        clamp_row_est(path->jpath.path.rows / parallel_divisor);
        }
 
-       /*
-        * We could include disable_cost in the preliminary estimate, but that
-        * would amount to optimizing for the case where the join method is
-        * disabled, which doesn't seem like the way to bet.
-        */
-       if (!enable_nestloop)
-               startup_cost += disable_cost;
-
        /* cost of inner-relation source data (we already dealt with outer rel) */
 
        if (path->jpath.jointype == JOIN_SEMI || path->jpath.jointype == JOIN_ANTI ||
                                           List *outersortkeys, List *innersortkeys,
                                           JoinPathExtraData *extra)
 {
+       int                     disabled_nodes;
        Cost            startup_cost = 0;
        Cost            run_cost = 0;
        double          outer_path_rows = outer_path->rows;
        Assert(outerstartsel <= outerendsel);
        Assert(innerstartsel <= innerendsel);
 
+       disabled_nodes = enable_mergejoin ? 0 : 1;
+
        /* cost of source data */
 
        if (outersortkeys)                      /* do we need to sort outer? */
                cost_sort(&sort_path,
                                  root,
                                  outersortkeys,
+                                 outer_path->disabled_nodes,
                                  outer_path->total_cost,
                                  outer_path_rows,
                                  outer_path->pathtarget->width,
                                  0.0,
                                  work_mem,
                                  -1.0);
+               disabled_nodes += sort_path.disabled_nodes;
                startup_cost += sort_path.startup_cost;
                startup_cost += (sort_path.total_cost - sort_path.startup_cost)
                        * outerstartsel;
        }
        else
        {
+               disabled_nodes += outer_path->disabled_nodes;
                startup_cost += outer_path->startup_cost;
                startup_cost += (outer_path->total_cost - outer_path->startup_cost)
                        * outerstartsel;
                cost_sort(&sort_path,
                                  root,
                                  innersortkeys,
+                                 inner_path->disabled_nodes,
                                  inner_path->total_cost,
                                  inner_path_rows,
                                  inner_path->pathtarget->width,
                                  0.0,
                                  work_mem,
                                  -1.0);
+               disabled_nodes += sort_path.disabled_nodes;
                startup_cost += sort_path.startup_cost;
                startup_cost += (sort_path.total_cost - sort_path.startup_cost)
                        * innerstartsel;
        }
        else
        {
+               disabled_nodes += inner_path->disabled_nodes;
                startup_cost += inner_path->startup_cost;
                startup_cost += (inner_path->total_cost - inner_path->startup_cost)
                        * innerstartsel;
        /* CPU costs left for later */
 
        /* Public result fields */
+       workspace->disabled_nodes = disabled_nodes;
        workspace->startup_cost = startup_cost;
        workspace->total_cost = startup_cost + run_cost + inner_run_cost;
        /* Save private data for final_cost_mergejoin */
                                rescannedtuples;
        double          rescanratio;
 
+       /* Set the number of disabled nodes. */
+       path->jpath.path.disabled_nodes = workspace->disabled_nodes;
+
        /* Protect some assumptions below that rowcounts aren't zero */
        if (inner_path_rows <= 0)
                inner_path_rows = 1;
                        clamp_row_est(path->jpath.path.rows / parallel_divisor);
        }
 
-       /*
-        * We could include disable_cost in the preliminary estimate, but that
-        * would amount to optimizing for the case where the join method is
-        * disabled, which doesn't seem like the way to bet.
-        */
-       if (!enable_mergejoin)
-               startup_cost += disable_cost;
-
        /*
         * Compute cost of the mergequals and qpquals (other restriction clauses)
         * separately.
                                          JoinPathExtraData *extra,
                                          bool parallel_hash)
 {
+       int                     disabled_nodes;
        Cost            startup_cost = 0;
        Cost            run_cost = 0;
        double          outer_path_rows = outer_path->rows;
        int                     num_skew_mcvs;
        size_t          space_allowed;  /* unused */
 
+       /* Count up disabled nodes. */
+       disabled_nodes = enable_hashjoin ? 0 : 1;
+       disabled_nodes += inner_path->disabled_nodes;
+       disabled_nodes += outer_path->disabled_nodes;
+
        /* cost of source data */
        startup_cost += outer_path->startup_cost;
        run_cost += outer_path->total_cost - outer_path->startup_cost;
        /* CPU costs left for later */
 
        /* Public result fields */
+       workspace->disabled_nodes = disabled_nodes;
        workspace->startup_cost = startup_cost;
        workspace->total_cost = startup_cost + run_cost;
        /* Save private data for final_cost_hashjoin */
        Selectivity innermcvfreq;
        ListCell   *hcl;
 
+       /* Set the number of disabled nodes. */
+       path->jpath.path.disabled_nodes = workspace->disabled_nodes;
+
        /* Mark the path with the correct row estimate */
        if (path->jpath.path.param_info)
                path->jpath.path.rows = path->jpath.path.param_info->ppi_rows;
                        clamp_row_est(path->jpath.path.rows / parallel_divisor);
        }
 
-       /*
-        * We could include disable_cost in the preliminary estimate, but that
-        * would amount to optimizing for the case where the join method is
-        * disabled, which doesn't seem like the way to bet.
-        */
-       if (!enable_hashjoin)
-               startup_cost += disable_cost;
-
        /* mark the path with estimated # of batches */
        path->num_batches = numbatches;
 
 
        initial_cost_nestloop(root, &workspace, jointype,
                                                  outer_path, inner_path, extra);
 
-       if (add_path_precheck(joinrel,
+       if (add_path_precheck(joinrel, workspace.disabled_nodes,
                                                  workspace.startup_cost, workspace.total_cost,
                                                  pathkeys, required_outer))
        {
         */
        initial_cost_nestloop(root, &workspace, jointype,
                                                  outer_path, inner_path, extra);
-       if (!add_partial_path_precheck(joinrel, workspace.total_cost, pathkeys))
+       if (!add_partial_path_precheck(joinrel, workspace.disabled_nodes,
+                                                                  workspace.total_cost, pathkeys))
                return;
 
        /* Might be good enough to be worth trying, so let's try it. */
                                                   outersortkeys, innersortkeys,
                                                   extra);
 
-       if (add_path_precheck(joinrel,
+       if (add_path_precheck(joinrel, workspace.disabled_nodes,
                                                  workspace.startup_cost, workspace.total_cost,
                                                  pathkeys, required_outer))
        {
                                                   outersortkeys, innersortkeys,
                                                   extra);
 
-       if (!add_partial_path_precheck(joinrel, workspace.total_cost, pathkeys))
+       if (!add_partial_path_precheck(joinrel, workspace.disabled_nodes,
+                                                                  workspace.total_cost, pathkeys))
                return;
 
        /* Might be good enough to be worth trying, so let's try it. */
        initial_cost_hashjoin(root, &workspace, jointype, hashclauses,
                                                  outer_path, inner_path, extra, false);
 
-       if (add_path_precheck(joinrel,
+       if (add_path_precheck(joinrel, workspace.disabled_nodes,
                                                  workspace.startup_cost, workspace.total_cost,
                                                  NIL, required_outer))
        {
         */
        initial_cost_hashjoin(root, &workspace, jointype, hashclauses,
                                                  outer_path, inner_path, extra, parallel_hash);
-       if (!add_partial_path_precheck(joinrel, workspace.total_cost, NIL))
+       if (!add_partial_path_precheck(joinrel, workspace.disabled_nodes,
+                                                                  workspace.total_cost, NIL))
                return;
 
        /* Might be good enough to be worth trying, so let's try it. */
 
 
        cost_sort(&sort_path, root, NIL,
                          lefttree->total_cost,
+                         0,                            /* a Plan contains no count of disabled nodes */
                          lefttree->plan_rows,
                          lefttree->plan_width,
                          0.0,
 
        /* Set cost data */
        cost_material(&matpath,
+                                 0,                    /* a Plan contains no count of disabled nodes */
                                  subplan->startup_cost,
                                  subplan->total_cost,
                                  subplan->plan_rows,
 
        /* Estimate the cost of seq scan + sort */
        seqScanPath = create_seqscan_path(root, rel, NULL, 0);
        cost_sort(&seqScanAndSortPath, root, NIL,
+                         seqScanPath->disabled_nodes,
                          seqScanPath->total_cost, rel->tuples, rel->reltarget->width,
                          comparisonCost, maintenance_work_mem, -1.0);
 
 
        cost_agg(&hashed_p, root, AGG_HASHED, NULL,
                         numGroupCols, dNumGroups,
                         NIL,
+                        input_path->disabled_nodes,
                         input_path->startup_cost, input_path->total_cost,
                         input_path->rows, input_path->pathtarget->width);
 
         * Now for the sorted case.  Note that the input is *always* unsorted,
         * since it was made by appending unrelated sub-relations together.
         */
+       sorted_p.disabled_nodes = input_path->disabled_nodes;
        sorted_p.startup_cost = input_path->startup_cost;
        sorted_p.total_cost = input_path->total_cost;
        /* XXX cost_sort doesn't actually look at pathkeys, so just pass NIL */
-       cost_sort(&sorted_p, root, NIL, sorted_p.total_cost,
+       cost_sort(&sorted_p, root, NIL, sorted_p.disabled_nodes,
+                         sorted_p.total_cost,
                          input_path->rows, input_path->pathtarget->width,
                          0.0, work_mem, -1.0);
        cost_group(&sorted_p, root, numGroupCols, dNumGroups,
                           NIL,
+                          sorted_p.disabled_nodes,
                           sorted_p.startup_cost, sorted_p.total_cost,
                           input_path->rows);
 
 
 int
 compare_path_costs(Path *path1, Path *path2, CostSelector criterion)
 {
+       /* Number of disabled nodes, if different, trumps all else. */
+       if (unlikely(path1->disabled_nodes != path2->disabled_nodes))
+       {
+               if (path1->disabled_nodes < path2->disabled_nodes)
+                       return -1;
+               else
+                       return +1;
+       }
+
        if (criterion == STARTUP_COST)
        {
                if (path1->startup_cost < path2->startup_cost)
        Cost            cost1,
                                cost2;
 
+       /* Number of disabled nodes, if different, trumps all else. */
+       if (unlikely(path1->disabled_nodes != path2->disabled_nodes))
+       {
+               if (path1->disabled_nodes < path2->disabled_nodes)
+                       return -1;
+               else
+                       return +1;
+       }
+
        if (fraction <= 0.0 || fraction >= 1.0)
                return compare_path_costs(path1, path2, TOTAL_COST);
        cost1 = path1->startup_cost +
 #define CONSIDER_PATH_STARTUP_COST(p)  \
        ((p)->param_info == NULL ? (p)->parent->consider_startup : (p)->parent->consider_param_startup)
 
+       /* Number of disabled nodes, if different, trumps all else. */
+       if (unlikely(path1->disabled_nodes != path2->disabled_nodes))
+       {
+               if (path1->disabled_nodes < path2->disabled_nodes)
+                       return COSTS_BETTER1;
+               else
+                       return COSTS_BETTER2;
+       }
+
        /*
         * Check total cost first since it's more likely to be different; many
         * paths have zero startup cost.
  * add_path
  *       Consider a potential implementation path for the specified parent rel,
  *       and add it to the rel's pathlist if it is worthy of consideration.
+ *
  *       A path is worthy if it has a better sort order (better pathkeys) or
- *       cheaper cost (on either dimension), or generates fewer rows, than any
- *       existing path that has the same or superset parameterization rels.
- *       We also consider parallel-safe paths more worthy than others.
+ *       cheaper cost (as defined below), or generates fewer rows, than any
+ *    existing path that has the same or superset parameterization rels.  We
+ *    also consider parallel-safe paths more worthy than others.
+ *
+ *    Cheaper cost can mean either a cheaper total cost or a cheaper startup
+ *    cost; if one path is cheaper in one of these aspects and another is
+ *    cheaper in the other, we keep both. However, when some path type is
+ *    disabled (e.g. due to enable_seqscan=false), the number of times that
+ *    a disabled path type is used is considered to be a higher-order
+ *    component of the cost. Hence, if path A uses no disabled path type,
+ *    and path B uses 1 or more disabled path types, A is cheaper, no matter
+ *    what we estimate for the startup and total costs. The startup and total
+ *    cost essentially act as a tiebreak when comparing paths that use equal
+ *    numbers of disabled path nodes; but in practice this tiebreak is almost
+ *    always used, since normally no path types are disabled.
  *
- *       We also remove from the rel's pathlist any old paths that are dominated
- *       by new_path --- that is, new_path is cheaper, at least as well ordered,
- *       generates no more rows, requires no outer rels not required by the old
- *       path, and is no less parallel-safe.
+ *       In addition to possibly adding new_path, we also remove from the rel's
+ *    pathlist any old paths that are dominated by new_path --- that is,
+ *    new_path is cheaper, at least as well ordered, generates no more rows,
+ *    requires no outer rels not required by the old path, and is no less
+ *    parallel-safe.
  *
  *       In most cases, a path with a superset parameterization will generate
  *       fewer rows (since it has more join clauses to apply), so that those two
  *       parent_rel->consider_param_startup is true for a parameterized one.
  *       Again, this allows discarding useless paths sooner.
  *
- *       The pathlist is kept sorted by total_cost, with cheaper paths
- *       at the front.  Within this routine, that's simply a speed hack:
- *       doing it that way makes it more likely that we will reject an inferior
- *       path after a few comparisons, rather than many comparisons.
+ *       The pathlist is kept sorted by disabled_nodes and then by total_cost,
+ *    with cheaper paths at the front.  Within this routine, that's simply a
+ *    speed hack: doing it that way makes it more likely that we will reject
+ *    an inferior path after a few comparisons, rather than many comparisons.
  *       However, add_path_precheck relies on this ordering to exit early
  *       when possible.
  *
                }
                else
                {
-                       /* new belongs after this old path if it has cost >= old's */
-                       if (new_path->total_cost >= old_path->total_cost)
+                       /*
+                        * new belongs after this old path if it has more disabled nodes
+                        * or if it has the same number of nodes but a greater total cost
+                        */
+                       if (new_path->disabled_nodes > old_path->disabled_nodes ||
+                               (new_path->disabled_nodes == old_path->disabled_nodes &&
+                                new_path->total_cost >= old_path->total_cost))
                                insert_at = foreach_current_index(p1) + 1;
                }
 
  * so the required information has to be passed piecemeal.
  */
 bool
-add_path_precheck(RelOptInfo *parent_rel,
+add_path_precheck(RelOptInfo *parent_rel, int disabled_nodes,
                                  Cost startup_cost, Cost total_cost,
                                  List *pathkeys, Relids required_outer)
 {
                Path       *old_path = (Path *) lfirst(p1);
                PathKeysComparison keyscmp;
 
+               /*
+                * Since the pathlist is sorted by disabled_nodes and then by
+                * total_cost, we can stop looking once we reach a path with more
+                * disabled nodes, or the same number of disabled nodes plus a
+                * total_cost larger than the new path's.
+                */
+               if (unlikely(old_path->disabled_nodes != disabled_nodes))
+               {
+                       if (disabled_nodes < old_path->disabled_nodes)
+                               break;
+               }
+               else if (total_cost <= old_path->total_cost * STD_FUZZ_FACTOR)
+                       break;
+
                /*
                 * We are looking for an old_path with the same parameterization (and
                 * by assumption the same rowcount) that dominates the new path on
                 *
                 * Cost comparisons here should match compare_path_costs_fuzzily.
                 */
-               if (total_cost > old_path->total_cost * STD_FUZZ_FACTOR)
+               /* new path can win on startup cost only if consider_startup */
+               if (startup_cost > old_path->startup_cost * STD_FUZZ_FACTOR ||
+                       !consider_startup)
                {
-                       /* new path can win on startup cost only if consider_startup */
-                       if (startup_cost > old_path->startup_cost * STD_FUZZ_FACTOR ||
-                               !consider_startup)
+                       /* new path loses on cost, so check pathkeys... */
+                       List       *old_path_pathkeys;
+
+                       old_path_pathkeys = old_path->param_info ? NIL : old_path->pathkeys;
+                       keyscmp = compare_pathkeys(new_path_pathkeys,
+                                                                          old_path_pathkeys);
+                       if (keyscmp == PATHKEYS_EQUAL ||
+                               keyscmp == PATHKEYS_BETTER2)
                        {
-                               /* new path loses on cost, so check pathkeys... */
-                               List       *old_path_pathkeys;
-
-                               old_path_pathkeys = old_path->param_info ? NIL : old_path->pathkeys;
-                               keyscmp = compare_pathkeys(new_path_pathkeys,
-                                                                                  old_path_pathkeys);
-                               if (keyscmp == PATHKEYS_EQUAL ||
-                                       keyscmp == PATHKEYS_BETTER2)
+                               /* new path does not win on pathkeys... */
+                               if (bms_equal(required_outer, PATH_REQ_OUTER(old_path)))
                                {
-                                       /* new path does not win on pathkeys... */
-                                       if (bms_equal(required_outer, PATH_REQ_OUTER(old_path)))
-                                       {
-                                               /* Found an old path that dominates the new one */
-                                               return false;
-                                       }
+                                       /* Found an old path that dominates the new one */
+                                       return false;
                                }
                        }
                }
-               else
-               {
-                       /*
-                        * Since the pathlist is sorted by total_cost, we can stop looking
-                        * once we reach a path with a total_cost larger than the new
-                        * path's.
-                        */
-                       break;
-               }
        }
 
        return true;
  *       produce the same number of rows.  Neither do we need to consider startup
  *       costs: parallelism is only used for plans that will be run to completion.
  *       Therefore, this routine is much simpler than add_path: it needs to
- *       consider only pathkeys and total cost.
+ *       consider only disabled nodes, pathkeys and total cost.
  *
  *       As with add_path, we pfree paths that are found to be dominated by
  *       another partial path; this requires that there be no other references to
                /* Unless pathkeys are incompatible, keep just one of the two paths. */
                if (keyscmp != PATHKEYS_DIFFERENT)
                {
-                       if (new_path->total_cost > old_path->total_cost * STD_FUZZ_FACTOR)
+                       if (unlikely(new_path->disabled_nodes != old_path->disabled_nodes))
+                       {
+                               if (new_path->disabled_nodes > old_path->disabled_nodes)
+                                       accept_new = false;
+                               else
+                                       remove_old = true;
+                       }
+                       else if (new_path->total_cost > old_path->total_cost
+                                        * STD_FUZZ_FACTOR)
                        {
                                /* New path costs more; keep it only if pathkeys are better. */
                                if (keyscmp != PATHKEYS_BETTER1)
  * is surely a loser.
  */
 bool
-add_partial_path_precheck(RelOptInfo *parent_rel, Cost total_cost,
-                                                 List *pathkeys)
+add_partial_path_precheck(RelOptInfo *parent_rel, int disabled_nodes,
+                                                 Cost total_cost, List *pathkeys)
 {
        ListCell   *p1;
 
         * partial path; the resulting plans, if run in parallel, will be run to
         * completion.
         */
-       if (!add_path_precheck(parent_rel, total_cost, total_cost, pathkeys,
-                                                  NULL))
+       if (!add_path_precheck(parent_rel, disabled_nodes, total_cost, total_cost,
+                                                  pathkeys, NULL))
                return false;
 
        return true;
                                                 Relids required_outer)
 {
        MergeAppendPath *pathnode = makeNode(MergeAppendPath);
+       int                     input_disabled_nodes;
        Cost            input_startup_cost;
        Cost            input_total_cost;
        ListCell   *l;
         * Add up the sizes and costs of the input paths.
         */
        pathnode->path.rows = 0;
+       input_disabled_nodes = 0;
        input_startup_cost = 0;
        input_total_cost = 0;
        foreach(l, subpaths)
                if (pathkeys_contained_in(pathkeys, subpath->pathkeys))
                {
                        /* Subpath is adequately ordered, we won't need to sort it */
+                       input_disabled_nodes += subpath->disabled_nodes;
                        input_startup_cost += subpath->startup_cost;
                        input_total_cost += subpath->total_cost;
                }
                        cost_sort(&sort_path,
                                          root,
                                          pathkeys,
+                                         subpath->disabled_nodes,
                                          subpath->total_cost,
                                          subpath->rows,
                                          subpath->pathtarget->width,
                                          0.0,
                                          work_mem,
                                          pathnode->limit_tuples);
+                       input_disabled_nodes += sort_path.disabled_nodes;
                        input_startup_cost += sort_path.startup_cost;
                        input_total_cost += sort_path.total_cost;
                }
                ((Path *) linitial(subpaths))->parallel_aware ==
                pathnode->path.parallel_aware)
        {
+               pathnode->path.disabled_nodes = input_disabled_nodes;
                pathnode->path.startup_cost = input_startup_cost;
                pathnode->path.total_cost = input_total_cost;
        }
        else
                cost_merge_append(&pathnode->path, root,
                                                  pathkeys, list_length(subpaths),
+                                                 input_disabled_nodes,
                                                  input_startup_cost, input_total_cost,
                                                  pathnode->path.rows);
 
        pathnode->subpath = subpath;
 
        cost_material(&pathnode->path,
+                                 subpath->disabled_nodes,
                                  subpath->startup_cost,
                                  subpath->total_cost,
                                  subpath->rows,
         */
        pathnode->est_entries = 0;
 
+       /* we should not generate this path type when enable_memoize=false */
+       Assert(enable_memoize);
+       pathnode->path.disabled_nodes = subpath->disabled_nodes;
+
        /*
         * Add a small additional charge for caching the first entry.  All the
         * harder calculations for rescans are performed in cost_memoize_rescan().
        {
                pathnode->umethod = UNIQUE_PATH_NOOP;
                pathnode->path.rows = rel->rows;
+               pathnode->path.disabled_nodes = subpath->disabled_nodes;
                pathnode->path.startup_cost = subpath->startup_cost;
                pathnode->path.total_cost = subpath->total_cost;
                pathnode->path.pathkeys = subpath->pathkeys;
                        {
                                pathnode->umethod = UNIQUE_PATH_NOOP;
                                pathnode->path.rows = rel->rows;
+                               pathnode->path.disabled_nodes = subpath->disabled_nodes;
                                pathnode->path.startup_cost = subpath->startup_cost;
                                pathnode->path.total_cost = subpath->total_cost;
                                pathnode->path.pathkeys = subpath->pathkeys;
                 * Estimate cost for sort+unique implementation
                 */
                cost_sort(&sort_path, root, NIL,
+                                 subpath->disabled_nodes,
                                  subpath->total_cost,
                                  rel->rows,
                                  subpath->pathtarget->width,
                                         AGG_HASHED, NULL,
                                         numCols, pathnode->path.rows,
                                         NIL,
+                                        subpath->disabled_nodes,
                                         subpath->startup_cost,
                                         subpath->total_cost,
                                         rel->rows,
 
        if (sjinfo->semi_can_btree && sjinfo->semi_can_hash)
        {
-               if (agg_path.total_cost < sort_path.total_cost)
+               if (agg_path.disabled_nodes < sort_path.disabled_nodes ||
+                       (agg_path.disabled_nodes == sort_path.disabled_nodes &&
+                        agg_path.total_cost < sort_path.total_cost))
                        pathnode->umethod = UNIQUE_PATH_HASH;
                else
                        pathnode->umethod = UNIQUE_PATH_SORT;
 
        if (pathnode->umethod == UNIQUE_PATH_HASH)
        {
+               pathnode->path.disabled_nodes = agg_path.disabled_nodes;
                pathnode->path.startup_cost = agg_path.startup_cost;
                pathnode->path.total_cost = agg_path.total_cost;
        }
        else
        {
+               pathnode->path.disabled_nodes = sort_path.disabled_nodes;
                pathnode->path.startup_cost = sort_path.startup_cost;
                pathnode->path.total_cost = sort_path.total_cost;
        }
                                                 Relids required_outer, double *rows)
 {
        GatherMergePath *pathnode = makeNode(GatherMergePath);
+       int                     input_disabled_nodes = 0;
        Cost            input_startup_cost = 0;
        Cost            input_total_cost = 0;
 
        pathnode->path.pathkeys = pathkeys;
        pathnode->path.pathtarget = target ? target : rel->reltarget;
 
+       input_disabled_nodes += subpath->disabled_nodes;
        input_startup_cost += subpath->startup_cost;
        input_total_cost += subpath->total_cost;
 
        cost_gather_merge(pathnode, root, rel, pathnode->path.param_info,
-                                         input_startup_cost, input_total_cost, rows);
+                                         input_disabled_nodes, input_startup_cost,
+                                         input_total_cost, rows);
 
        return pathnode;
 }
 ForeignPath *
 create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
                                                PathTarget *target,
-                                               double rows, Cost startup_cost, Cost total_cost,
+                                               double rows, int disabled_nodes,
+                                               Cost startup_cost, Cost total_cost,
                                                List *pathkeys,
                                                Relids required_outer,
                                                Path *fdw_outerpath,
        pathnode->path.parallel_safe = rel->consider_parallel;
        pathnode->path.parallel_workers = 0;
        pathnode->path.rows = rows;
+       pathnode->path.disabled_nodes = disabled_nodes;
        pathnode->path.startup_cost = startup_cost;
        pathnode->path.total_cost = total_cost;
        pathnode->path.pathkeys = pathkeys;
 ForeignPath *
 create_foreign_join_path(PlannerInfo *root, RelOptInfo *rel,
                                                 PathTarget *target,
-                                                double rows, Cost startup_cost, Cost total_cost,
+                                                double rows, int disabled_nodes,
+                                                Cost startup_cost, Cost total_cost,
                                                 List *pathkeys,
                                                 Relids required_outer,
                                                 Path *fdw_outerpath,
        pathnode->path.parallel_safe = rel->consider_parallel;
        pathnode->path.parallel_workers = 0;
        pathnode->path.rows = rows;
+       pathnode->path.disabled_nodes = disabled_nodes;
        pathnode->path.startup_cost = startup_cost;
        pathnode->path.total_cost = total_cost;
        pathnode->path.pathkeys = pathkeys;
 ForeignPath *
 create_foreign_upper_path(PlannerInfo *root, RelOptInfo *rel,
                                                  PathTarget *target,
-                                                 double rows, Cost startup_cost, Cost total_cost,
+                                                 double rows, int disabled_nodes,
+                                                 Cost startup_cost, Cost total_cost,
                                                  List *pathkeys,
                                                  Path *fdw_outerpath,
                                                  List *fdw_restrictinfo,
        pathnode->path.parallel_safe = rel->consider_parallel;
        pathnode->path.parallel_workers = 0;
        pathnode->path.rows = rows;
+       pathnode->path.disabled_nodes = disabled_nodes;
        pathnode->path.startup_cost = startup_cost;
        pathnode->path.total_cost = total_cost;
        pathnode->path.pathkeys = pathkeys;
                 * Set cost of plan as subpath's cost, adjusted for tlist replacement.
                 */
                pathnode->path.rows = subpath->rows;
+               pathnode->path.disabled_nodes = subpath->disabled_nodes;
                pathnode->path.startup_cost = subpath->startup_cost +
                        (target->cost.startup - oldtarget->cost.startup);
                pathnode->path.total_cost = subpath->total_cost +
                 * evaluating the tlist.  There is no qual to worry about.
                 */
                pathnode->path.rows = subpath->rows;
+               pathnode->path.disabled_nodes = subpath->disabled_nodes;
                pathnode->path.startup_cost = subpath->startup_cost +
                        target->cost.startup;
                pathnode->path.total_cost = subpath->total_cost +
         * This is slightly bizarre maybe, but it's what 9.6 did; we may revisit
         * this estimate later.
         */
+       pathnode->path.disabled_nodes = subpath->disabled_nodes;
        pathnode->path.rows = subpath->rows * tlist_rows;
        pathnode->path.startup_cost = subpath->startup_cost +
                target->cost.startup;
 
        cost_incremental_sort(&pathnode->path,
                                                  root, pathkeys, presorted_keys,
+                                                 subpath->disabled_nodes,
                                                  subpath->startup_cost,
                                                  subpath->total_cost,
                                                  subpath->rows,
        pathnode->subpath = subpath;
 
        cost_sort(&pathnode->path, root, pathkeys,
+                         subpath->disabled_nodes,
                          subpath->total_cost,
                          subpath->rows,
                          subpath->pathtarget->width,
                           list_length(groupClause),
                           numGroups,
                           qual,
+                          subpath->disabled_nodes,
                           subpath->startup_cost, subpath->total_cost,
                           subpath->rows);
 
         * all columns get compared at most of the tuples.  (XXX probably this is
         * an overestimate.)
         */
+       pathnode->path.disabled_nodes = subpath->disabled_nodes;
        pathnode->path.startup_cost = subpath->startup_cost;
        pathnode->path.total_cost = subpath->total_cost +
                cpu_operator_cost * subpath->rows * numCols;
                         aggstrategy, aggcosts,
                         list_length(groupClause), numGroups,
                         qual,
+                        subpath->disabled_nodes,
                         subpath->startup_cost, subpath->total_cost,
                         subpath->rows, subpath->pathtarget->width);
 
                                         numGroupCols,
                                         rollup->numGroups,
                                         having_qual,
+                                        subpath->disabled_nodes,
                                         subpath->startup_cost,
                                         subpath->total_cost,
                                         subpath->rows,
                                                 numGroupCols,
                                                 rollup->numGroups,
                                                 having_qual,
-                                                0.0, 0.0,
+                                                0, 0.0, 0.0,
                                                 subpath->rows,
                                                 subpath->pathtarget->width);
                                if (!rollup->is_hashed)
                        else
                        {
                                /* Account for cost of sort, but don't charge input cost again */
-                               cost_sort(&sort_path, root, NIL,
+                               cost_sort(&sort_path, root, NIL, 0,
                                                  0.0,
                                                  subpath->rows,
                                                  subpath->pathtarget->width,
                                                 numGroupCols,
                                                 rollup->numGroups,
                                                 having_qual,
+                                                sort_path.disabled_nodes,
                                                 sort_path.startup_cost,
                                                 sort_path.total_cost,
                                                 sort_path.rows,
                                                 subpath->pathtarget->width);
                        }
 
+                       pathnode->path.disabled_nodes += agg_path.disabled_nodes;
                        pathnode->path.total_cost += agg_path.total_cost;
                        pathnode->path.rows += agg_path.rows;
                }
 {
        MinMaxAggPath *pathnode = makeNode(MinMaxAggPath);
        Cost            initplan_cost;
+       int                     initplan_disabled_nodes = 0;
        ListCell   *lc;
 
        /* The topmost generated Plan node will be a Result */
        {
                MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
 
+               initplan_disabled_nodes += mminfo->path->disabled_nodes;
                initplan_cost += mminfo->pathcost;
                if (!mminfo->path->parallel_safe)
                        pathnode->path.parallel_safe = false;
        }
 
        /* add tlist eval cost for each output row, plus cpu_tuple_cost */
+       pathnode->path.disabled_nodes = initplan_disabled_nodes;
        pathnode->path.startup_cost = initplan_cost + target->cost.startup;
        pathnode->path.total_cost = initplan_cost + target->cost.startup +
                target->cost.per_tuple + cpu_tuple_cost;
        cost_windowagg(&pathnode->path, root,
                                   windowFuncs,
                                   winclause,
+                                  subpath->disabled_nodes,
                                   subpath->startup_cost,
                                   subpath->total_cost,
                                   subpath->rows);
         * Charge one cpu_operator_cost per comparison per input tuple. We assume
         * all columns get compared at most of the tuples.
         */
+       pathnode->path.disabled_nodes = subpath->disabled_nodes;
        pathnode->path.startup_cost = subpath->startup_cost;
        pathnode->path.total_cost = subpath->total_cost +
                cpu_operator_cost * subpath->rows * list_length(distinctList);
         * possible refetches, but it's hard to say how much.  For now, use
         * cpu_tuple_cost per row.
         */
+       pathnode->path.disabled_nodes = subpath->disabled_nodes;
        pathnode->path.startup_cost = subpath->startup_cost;
        pathnode->path.total_cost = subpath->total_cost +
                cpu_tuple_cost * subpath->rows;
         * costs to change any higher-level planning choices.  But we might want
         * to make it look better sometime.
         */
+       pathnode->path.disabled_nodes = subpath->disabled_nodes;
        pathnode->path.startup_cost = subpath->startup_cost;
        pathnode->path.total_cost = subpath->total_cost;
        if (returningLists != NIL)
                subpath->parallel_safe;
        pathnode->path.parallel_workers = subpath->parallel_workers;
        pathnode->path.rows = subpath->rows;
+       pathnode->path.disabled_nodes = subpath->disabled_nodes;
        pathnode->path.startup_cost = subpath->startup_cost;
        pathnode->path.total_cost = subpath->total_cost;
        pathnode->path.pathkeys = subpath->pathkeys;
 
 
        /* estimated size/costs for path (see costsize.c for more info) */
        Cardinality rows;                       /* estimated number of result tuples */
+       int                     disabled_nodes; /* count of disabled nodes */
        Cost            startup_cost;   /* cost expended before fetching any tuples */
        Cost            total_cost;             /* total cost (assuming all tuples fetched) */
 
 typedef struct JoinCostWorkspace
 {
        /* Preliminary cost estimates --- must not be larger than final ones! */
+       int                     disabled_nodes;
        Cost            startup_cost;   /* cost expended before fetching any tuples */
        Cost            total_cost;             /* total cost (assuming all tuples fetched) */
 
 
                                                        RelOptInfo *baserel, ParamPathInfo *param_info);
 extern void cost_recursive_union(Path *runion, Path *nrterm, Path *rterm);
 extern void cost_sort(Path *path, PlannerInfo *root,
-                                         List *pathkeys, Cost input_cost, double tuples, int width,
+                                         List *pathkeys, int disabled_nodes,
+                                         Cost input_cost, double tuples, int width,
                                          Cost comparison_cost, int sort_mem,
                                          double limit_tuples);
 extern void cost_incremental_sort(Path *path,
                                                                  PlannerInfo *root, List *pathkeys, int presorted_keys,
+                                                                 int input_disabled_nodes,
                                                                  Cost input_startup_cost, Cost input_total_cost,
                                                                  double input_tuples, int width, Cost comparison_cost, int sort_mem,
                                                                  double limit_tuples);
 extern void cost_append(AppendPath *apath);
 extern void cost_merge_append(Path *path, PlannerInfo *root,
                                                          List *pathkeys, int n_streams,
+                                                         int input_disabled_nodes,
                                                          Cost input_startup_cost, Cost input_total_cost,
                                                          double tuples);
 extern void cost_material(Path *path,
+                                                 int input_disabled_nodes,
                                                  Cost input_startup_cost, Cost input_total_cost,
                                                  double tuples, int width);
 extern void cost_agg(Path *path, PlannerInfo *root,
                                         AggStrategy aggstrategy, const AggClauseCosts *aggcosts,
                                         int numGroupCols, double numGroups,
                                         List *quals,
+                                        int input_disabled_nodes,
                                         Cost input_startup_cost, Cost input_total_cost,
                                         double input_tuples, double input_width);
 extern void cost_windowagg(Path *path, PlannerInfo *root,
                                                   List *windowFuncs, WindowClause *winclause,
+                                                  int input_disabled_nodes,
                                                   Cost input_startup_cost, Cost input_total_cost,
                                                   double input_tuples);
 extern void cost_group(Path *path, PlannerInfo *root,
                                           int numGroupCols, double numGroups,
                                           List *quals,
+                                          int input_disabled_nodes,
                                           Cost input_startup_cost, Cost input_total_cost,
                                           double input_tuples);
 extern void initial_cost_nestloop(PlannerInfo *root,
                                                RelOptInfo *rel, ParamPathInfo *param_info, double *rows);
 extern void cost_gather_merge(GatherMergePath *path, PlannerInfo *root,
                                                          RelOptInfo *rel, ParamPathInfo *param_info,
+                                                         int input_disabled_nodes,
                                                          Cost input_startup_cost, Cost input_total_cost,
                                                          double *rows);
 extern void cost_subplan(PlannerInfo *root, SubPlan *subplan, Plan *plan);
 
                                                                                  double fraction);
 extern void set_cheapest(RelOptInfo *parent_rel);
 extern void add_path(RelOptInfo *parent_rel, Path *new_path);
-extern bool add_path_precheck(RelOptInfo *parent_rel,
+extern bool add_path_precheck(RelOptInfo *parent_rel, int disabled_nodes,
                                                          Cost startup_cost, Cost total_cost,
                                                          List *pathkeys, Relids required_outer);
 extern void add_partial_path(RelOptInfo *parent_rel, Path *new_path);
 extern bool add_partial_path_precheck(RelOptInfo *parent_rel,
+                                                                         int disabled_nodes,
                                                                          Cost total_cost, List *pathkeys);
 
 extern Path *create_seqscan_path(PlannerInfo *root, RelOptInfo *rel,
                                                                           Relids required_outer);
 extern ForeignPath *create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
                                                                                        PathTarget *target,
-                                                                                       double rows, Cost startup_cost, Cost total_cost,
+                                                                                       double rows, int disabled_nodes,
+                                                                                       Cost startup_cost, Cost total_cost,
                                                                                        List *pathkeys,
                                                                                        Relids required_outer,
                                                                                        Path *fdw_outerpath,
                                                                                        List *fdw_private);
 extern ForeignPath *create_foreign_join_path(PlannerInfo *root, RelOptInfo *rel,
                                                                                         PathTarget *target,
-                                                                                        double rows, Cost startup_cost, Cost total_cost,
+                                                                                        double rows, int disabled_nodes,
+                                                                                        Cost startup_cost, Cost total_cost,
                                                                                         List *pathkeys,
                                                                                         Relids required_outer,
                                                                                         Path *fdw_outerpath,
                                                                                         List *fdw_private);
 extern ForeignPath *create_foreign_upper_path(PlannerInfo *root, RelOptInfo *rel,
                                                                                          PathTarget *target,
-                                                                                         double rows, Cost startup_cost, Cost total_cost,
+                                                                                         double rows, int disabled_nodes,
+                                                                                         Cost startup_cost, Cost total_cost,
                                                                                          List *pathkeys,
                                                                                          Path *fdw_outerpath,
                                                                                          List *fdw_restrictinfo,
 
 setup
 {
     SET enable_seqscan = false;
-    SET enable_indexscan = false;
     SET enable_bitmapscan = false;
 }
 
 
 
 explain (costs off)
 select proname from pg_proc where proname ilike 'ri%foo' order by 1;
-                           QUERY PLAN                            
------------------------------------------------------------------
- Index Only Scan using pg_proc_proname_args_nsp_index on pg_proc
-   Filter: (proname ~~* 'ri%foo'::text)
-(2 rows)
+                  QUERY PLAN                  
+----------------------------------------------
+ Sort
+   Sort Key: proname
+   ->  Seq Scan on pg_proc
+         Filter: (proname ~~* 'ri%foo'::text)
+(4 rows)
 
 reset enable_seqscan;
 reset enable_indexscan;
 
 ------------------------------------------------------------
  Aggregate
    ->  Nested Loop
-         ->  Seq Scan on tenk2
-               Filter: (thousand = 0)
+         ->  Gather
+               Workers Planned: 4
+               ->  Parallel Seq Scan on tenk2
+                     Filter: (thousand = 0)
          ->  Gather
                Workers Planned: 4
                ->  Parallel Bitmap Heap Scan on tenk1
                      Recheck Cond: (hundred > 1)
                      ->  Bitmap Index Scan on tenk1_hundred
                            Index Cond: (hundred > 1)
-(10 rows)
+(12 rows)
 
 select count(*) from tenk1, tenk2 where tenk1.hundred > 1 and tenk2.thousand=0;
  count