From a93e84eed0bc792be7acdab434671bea25c0b7ee Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Fri, 6 Dec 2019 21:38:02 -0600
Subject: [PATCH] Make "Cost" a structure..

..to allow "Explain" to show its members.

As mentioned by Jeff here:
https://www.postgresql.org/message-id/CAMkU%3D1zBJNVo2DGYBgLJqpu8fyjCE_ys%2Bmsr6pOEoiwA7y5jrA%40mail.gmail.com
---
 src/backend/commands/explain.c            |  65 +-
 src/backend/executor/nodeHashjoin.c       |   3 +-
 src/backend/executor/nodeSubplan.c        |   9 +-
 src/backend/nodes/equalfuncs.c            |   7 +-
 src/backend/nodes/outfuncs.c              |  25 +-
 src/backend/nodes/readfuncs.c             |  15 +-
 src/backend/optimizer/geqo/geqo_eval.c    |   5 +-
 src/backend/optimizer/geqo/geqo_pool.c    |  17 +-
 src/backend/optimizer/path/allpaths.c     |   2 +-
 src/backend/optimizer/path/costsize.c     | 999 ++++++++++++++++++------------
 src/backend/optimizer/path/indxpath.c     |  12 +-
 src/backend/optimizer/plan/createplan.c   |  13 +-
 src/backend/optimizer/plan/planagg.c      |   7 +-
 src/backend/optimizer/plan/planner.c      |  26 +-
 src/backend/optimizer/plan/subselect.c    |   9 +-
 src/backend/optimizer/prep/prepunion.c    |   7 +-
 src/backend/optimizer/util/appendinfo.c   |   3 +-
 src/backend/optimizer/util/clauses.c      |  14 +-
 src/backend/optimizer/util/pathnode.c     | 268 ++++----
 src/backend/optimizer/util/placeholder.c  |   4 +-
 src/backend/optimizer/util/plancat.c      |  10 +-
 src/backend/optimizer/util/relnode.c      |  12 +-
 src/backend/optimizer/util/restrictinfo.c |   3 +-
 src/backend/utils/adt/selfuncs.c          | 120 ++--
 src/backend/utils/cache/plancache.c       |   3 +-
 src/include/nodes/nodes.h                 |  16 +-
 src/include/optimizer/cost.h              |  31 +-
 src/include/utils/selfuncs.h              |   5 +-
 src/test/regress/regress.c                |   5 +-
 29 files changed, 1028 insertions(+), 687 deletions(-)

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 62fb343..acc6ad4 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -24,6 +24,7 @@
 #include "nodes/extensible.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
+#include "optimizer/cost.h"
 #include "parser/parsetree.h"
 #include "rewrite/rewriteHandler.h"
 #include "storage/bufmgr.h"
@@ -61,6 +62,7 @@ static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
 							ExplainState *es);
 static double elapsed_time(instr_time *starttime);
 static bool ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used);
+static void explain_verbose_costs(Cost *cost, ExplainState *es);
 static void ExplainNode(PlanState *planstate, List *ancestors,
 						const char *relationship, const char *plan_name,
 						ExplainState *es);
@@ -1041,6 +1043,26 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
 	return planstate_tree_walker(planstate, ExplainPreScanNode, rels_used);
 }
 
+static void explain_verbose_costs(Cost *cost, ExplainState *es)
+{
+	ExplainPropertyFloat("cpu_index_tuple_cost", NULL,
+		cost->cpu_index_tuple_cost, 2, es);
+	ExplainPropertyFloat("cpu_operator_cost", NULL,
+		cost->cpu_operator_cost, 2, es);
+	ExplainPropertyFloat("cpu_tuple_cost", NULL,
+		cost->cpu_tuple_cost, 2, es);
+	ExplainPropertyFloat("parallel_setup_cost", NULL,
+		cost->parallel_setup_cost, 2, es);
+	ExplainPropertyFloat("parallel_tuple_cost", NULL,
+		cost->parallel_tuple_cost, 2, es);
+	ExplainPropertyFloat("random_page_cost", NULL,
+		cost->random_page_cost, 2, es);
+	ExplainPropertyFloat("seq_page_cost", NULL,
+		cost->seq_page_cost, 2, es);
+	ExplainPropertyFloat("disable_cost", NULL,
+		cost->disable_cost, 2, es);
+}
+
 /*
  * ExplainNode -
  *	  Appends a description of a plan tree to es->str
@@ -1473,18 +1495,47 @@ ExplainNode(PlanState *planstate, List *ancestors,
 
 	if (es->costs)
 	{
-		if (es->format == EXPLAIN_FORMAT_TEXT)
+		if (es->format == EXPLAIN_FORMAT_TEXT && !es->verbose)
 		{
 			appendStringInfo(es->str, "  (cost=%.2f..%.2f rows=%.0f width=%d)",
-							 plan->startup_cost, plan->total_cost,
+							 cost_asscalar(&plan->startup_cost),
+							 cost_asscalar(&plan->total_cost),
 							 plan->plan_rows, plan->plan_width);
-		}
-		else
-		{
-			ExplainPropertyFloat("Startup Cost", NULL, plan->startup_cost,
+		} else if (es->format == EXPLAIN_FORMAT_TEXT) { /* && es->verbose */
+			// XXX: other fields
+			appendStringInfo(es->str, "  (cost=Seq page %.2f Rand page %2f CPU tuple: %2f CPU oper %2f"\
+										"..Seq page %.2f Rand page %2f CPU tuple: %2f CPU oper %2f"\
+										" rows=%.0f width=%d)",
+							 plan->startup_cost.seq_page_cost,
+							 plan->startup_cost.random_page_cost,
+							 plan->startup_cost.cpu_tuple_cost,
+							 plan->startup_cost.cpu_operator_cost,
+							 plan->total_cost.seq_page_cost,
+							 plan->total_cost.random_page_cost,
+							 plan->total_cost.cpu_tuple_cost,
+							 plan->total_cost.cpu_operator_cost,
+							 plan->plan_rows, plan->plan_width);
+		} else if (!es->verbose) {
+			ExplainPropertyFloat("Startup Cost", NULL, cost_asscalar(&plan->startup_cost),
 								 2, es);
-			ExplainPropertyFloat("Total Cost", NULL, plan->total_cost,
+			ExplainPropertyFloat("Total Cost", NULL, cost_asscalar(&plan->total_cost),
 								 2, es);
+
+			ExplainPropertyFloat("Plan Rows", NULL, plan->plan_rows,
+								 0, es);
+			ExplainPropertyInteger("Plan Width", NULL, plan->plan_width,
+								   es);
+		}
+		else /* !EXPLAIN_FORMAT_TEXT text && es->verbose */
+		{
+			ExplainOpenGroup("Startup Cost", "Startup Cost", false, es);
+			explain_verbose_costs(&plan->startup_cost, es);
+			ExplainCloseGroup("Startup Cost", "Startup Cost", false, es);
+
+			ExplainOpenGroup("Total Cost", "Total Cost", true, es);
+			explain_verbose_costs(&plan->total_cost, es);
+			ExplainCloseGroup("Total Cost", "Total Cost", true, es);
+
 			ExplainPropertyFloat("Plan Rows", NULL, plan->plan_rows,
 								 0, es);
 			ExplainPropertyInteger("Plan Width", NULL, plan->plan_width,
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index ec37558..797ee20 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -112,6 +112,7 @@
 #include "executor/hashjoin.h"
 #include "executor/nodeHash.h"
 #include "executor/nodeHashjoin.h"
+#include "optimizer/cost.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "utils/memutils.h"
@@ -256,7 +257,7 @@ ExecHashJoinImpl(PlanState *pstate, bool parallel)
 					node->hj_FirstOuterTupleSlot = NULL;
 				}
 				else if (HJ_FILL_OUTER(node) ||
-						 (outerNode->plan->startup_cost < hashNode->ps.plan->total_cost &&
+						 (cost_islt(&outerNode->plan->startup_cost, &hashNode->ps.plan->total_cost) &&
 						  !node->hj_OuterNotEmpty))
 				{
 					node->hj_FirstOuterTupleSlot = ExecProcNode(outerNode);
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 2c364bd..6c41582 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -35,6 +35,7 @@
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
+#include "optimizer/cost.h"
 #include "utils/array.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
@@ -1353,10 +1354,12 @@ ExecInitAlternativeSubPlan(AlternativeSubPlan *asplan, PlanState *parent)
 	subplan1 = (SubPlan *) linitial(asplan->subplans);
 	subplan2 = (SubPlan *) lsecond(asplan->subplans);
 
-	cost1 = subplan1->startup_cost + num_calls * subplan1->per_call_cost;
-	cost2 = subplan2->startup_cost + num_calls * subplan2->per_call_cost;
+	cost1 = subplan1->startup_cost;
+	cost_add_mul(&cost1, &subplan1->per_call_cost, num_calls);
+	cost2 = subplan2->startup_cost;
+	cost_add_mul(&cost2, &subplan2->per_call_cost, num_calls);
 
-	if (cost1 < cost2)
+	if (cost_islt(&cost1, &cost2))
 		asstate->active = 0;
 	else
 		asstate->active = 1;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 2fcd4a3..d262c16 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -32,6 +32,7 @@
 #include "miscadmin.h"
 #include "nodes/extensible.h"
 #include "nodes/pathnodes.h"
+#include "optimizer/cost.h"
 #include "utils/datum.h"
 
 
@@ -449,8 +450,10 @@ _equalSubPlan(const SubPlan *a, const SubPlan *b)
 	COMPARE_NODE_FIELD(setParam);
 	COMPARE_NODE_FIELD(parParam);
 	COMPARE_NODE_FIELD(args);
-	COMPARE_SCALAR_FIELD(startup_cost);
-	COMPARE_SCALAR_FIELD(per_call_cost);
+	if (cost_asscalar(&a->startup_cost) != cost_asscalar(&b->startup_cost))
+		return false;
+	if (cost_asscalar(&a->per_call_cost) != cost_asscalar(&b->per_call_cost))
+		return false;
 
 	return true;
 }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index a80eccc..e605c10 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -34,6 +34,7 @@
 #include "nodes/extensible.h"
 #include "nodes/pathnodes.h"
 #include "nodes/plannodes.h"
+#include "optimizer/cost.h"
 #include "utils/datum.h"
 #include "utils/rel.h"
 
@@ -86,6 +87,10 @@ static void outChar(StringInfo str, char c);
 #define WRITE_FLOAT_FIELD(fldname,format) \
 	appendStringInfo(str, " :" CppAsString(fldname) " " format, node->fldname)
 
+/* Write a float field --- caller must give format to define precision */
+#define WRITE_COST_FIELD(fldname,format) \
+	appendStringInfo(str, " :" CppAsString(fldname) " " format, cost_asscalar(&node->fldname))
+
 /* Write a boolean field */
 #define WRITE_BOOL_FIELD(fldname) \
 	appendStringInfo(str, " :" CppAsString(fldname) " %s", \
@@ -327,8 +332,8 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
 static void
 _outPlanInfo(StringInfo str, const Plan *node)
 {
-	WRITE_FLOAT_FIELD(startup_cost, "%.2f");
-	WRITE_FLOAT_FIELD(total_cost, "%.2f");
+	WRITE_COST_FIELD(startup_cost, "%.2f");
+	WRITE_COST_FIELD(total_cost, "%.2f");
 	WRITE_FLOAT_FIELD(plan_rows, "%.0f");
 	WRITE_INT_FIELD(plan_width);
 	WRITE_BOOL_FIELD(parallel_aware);
@@ -1321,8 +1326,8 @@ _outSubPlan(StringInfo str, const SubPlan *node)
 	WRITE_NODE_FIELD(setParam);
 	WRITE_NODE_FIELD(parParam);
 	WRITE_NODE_FIELD(args);
-	WRITE_FLOAT_FIELD(startup_cost, "%.2f");
-	WRITE_FLOAT_FIELD(per_call_cost, "%.2f");
+	WRITE_COST_FIELD(startup_cost, "%.2f");
+	WRITE_COST_FIELD(per_call_cost, "%.2f");
 }
 
 static void
@@ -1717,8 +1722,8 @@ _outPathInfo(StringInfo str, const Path *node)
 	WRITE_BOOL_FIELD(parallel_safe);
 	WRITE_INT_FIELD(parallel_workers);
 	WRITE_FLOAT_FIELD(rows, "%.0f");
-	WRITE_FLOAT_FIELD(startup_cost, "%.2f");
-	WRITE_FLOAT_FIELD(total_cost, "%.2f");
+	WRITE_COST_FIELD(startup_cost, "%.2f");
+	WRITE_COST_FIELD(total_cost, "%.2f");
 	WRITE_NODE_FIELD(pathkeys);
 }
 
@@ -1757,7 +1762,7 @@ _outIndexPath(StringInfo str, const IndexPath *node)
 	WRITE_NODE_FIELD(indexorderbys);
 	WRITE_NODE_FIELD(indexorderbycols);
 	WRITE_ENUM_FIELD(indexscandir, ScanDirection);
-	WRITE_FLOAT_FIELD(indextotalcost, "%.2f");
+	WRITE_COST_FIELD(indextotalcost, "%.2f");
 	WRITE_FLOAT_FIELD(indexselectivity, "%.4f");
 }
 
@@ -2411,8 +2416,8 @@ _outPathTarget(StringInfo str, const PathTarget *node)
 		for (i = 0; i < list_length(node->exprs); i++)
 			appendStringInfo(str, " %u", node->sortgrouprefs[i]);
 	}
-	WRITE_FLOAT_FIELD(cost.startup, "%.2f");
-	WRITE_FLOAT_FIELD(cost.per_tuple, "%.2f");
+	WRITE_COST_FIELD(cost.startup, "%.2f");
+	WRITE_COST_FIELD(cost.per_tuple, "%.2f");
 	WRITE_INT_FIELD(width);
 }
 
@@ -2537,7 +2542,7 @@ _outMinMaxAggInfo(StringInfo str, const MinMaxAggInfo *node)
 	WRITE_NODE_FIELD(target);
 	/* We intentionally omit subroot --- too large, not interesting enough */
 	WRITE_NODE_FIELD(path);
-	WRITE_FLOAT_FIELD(pathcost, "%.2f");
+	WRITE_COST_FIELD(pathcost, "%.2f");
 	WRITE_NODE_FIELD(param);
 }
 
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 764e3bb..701475c 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -38,6 +38,7 @@
 #include "nodes/parsenodes.h"
 #include "nodes/plannodes.h"
 #include "nodes/readfuncs.h"
+#include "optimizer/cost.h"
 #include "utils/builtins.h"
 
 
@@ -113,6 +114,12 @@
 	token = pg_strtok(&length);		/* get field value */ \
 	local_node->fldname = atof(token)
 
+/* Read a float field */
+#define READ_COST_FIELD(fldname) \
+	token = pg_strtok(&length);		/* skip :fldname */ \
+	token = pg_strtok(&length);		/* get field value */ \
+	local_node->fldname.disable_cost = atof(token) // XXX
+
 /* Read a boolean field */
 #define READ_BOOL_FIELD(fldname) \
 	token = pg_strtok(&length);		/* skip :fldname */ \
@@ -1534,8 +1541,8 @@ ReadCommonPlan(Plan *local_node)
 {
 	READ_TEMP_LOCALS();
 
-	READ_FLOAT_FIELD(startup_cost);
-	READ_FLOAT_FIELD(total_cost);
+	READ_COST_FIELD(startup_cost);
+	READ_COST_FIELD(total_cost);
 	READ_FLOAT_FIELD(plan_rows);
 	READ_INT_FIELD(plan_width);
 	READ_BOOL_FIELD(parallel_aware);
@@ -2466,8 +2473,8 @@ _readSubPlan(void)
 	READ_NODE_FIELD(setParam);
 	READ_NODE_FIELD(parParam);
 	READ_NODE_FIELD(args);
-	READ_FLOAT_FIELD(startup_cost);
-	READ_FLOAT_FIELD(per_call_cost);
+	READ_COST_FIELD(startup_cost);
+	READ_COST_FIELD(per_call_cost);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/geqo/geqo_eval.c b/src/backend/optimizer/geqo/geqo_eval.c
index 7b67a29..9f827ba 100644
--- a/src/backend/optimizer/geqo/geqo_eval.c
+++ b/src/backend/optimizer/geqo/geqo_eval.c
@@ -29,6 +29,7 @@
 #include "optimizer/joininfo.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
+#include "optimizer/cost.h"
 #include "utils/memutils.h"
 
 
@@ -62,7 +63,7 @@ geqo_eval(PlannerInfo *root, Gene *tour, int num_gene)
 	Cost		fitness;
 	int			savelength;
 	struct HTAB *savehash;
-
+	extern double disable_cost;
 	/*
 	 * Create a private memory context that will hold all temp storage
 	 * allocated inside gimme_tree().
@@ -115,7 +116,7 @@ geqo_eval(PlannerInfo *root, Gene *tour, int num_gene)
 		fitness = best_path->total_cost;
 	}
 	else
-		fitness = DBL_MAX;
+		cost_set_member(&fitness, disable_cost);
 
 	/*
 	 * Restore join_rel_list to its former state, and put back original
diff --git a/src/backend/optimizer/geqo/geqo_pool.c b/src/backend/optimizer/geqo/geqo_pool.c
index f29fcc2..3ce7f8d 100644
--- a/src/backend/optimizer/geqo/geqo_pool.c
+++ b/src/backend/optimizer/geqo/geqo_pool.c
@@ -30,6 +30,7 @@
 #include "optimizer/geqo_copy.h"
 #include "optimizer/geqo_pool.h"
 #include "optimizer/geqo_recombination.h"
+#include "optimizer/cost.h"
 
 
 static int	compare(const void *arg1, const void *arg2);
@@ -108,7 +109,7 @@ random_init_pool(PlannerInfo *root, Pool *pool)
 		init_tour(root, chromo[i].string, pool->string_length);
 		pool->data[i].worth = geqo_eval(root, chromo[i].string,
 										pool->string_length);
-		if (pool->data[i].worth < DBL_MAX)
+		if (cost_asscalar(&pool->data[i].worth) < disable_cost)
 			i++;
 		else
 		{
@@ -147,9 +148,9 @@ compare(const void *arg1, const void *arg2)
 	const Chromosome *chromo1 = (const Chromosome *) arg1;
 	const Chromosome *chromo2 = (const Chromosome *) arg2;
 
-	if (chromo1->worth == chromo2->worth)
+	if (cost_asscalar(&chromo1->worth) == cost_asscalar(&chromo2->worth))
 		return 0;
-	else if (chromo1->worth > chromo2->worth)
+	else if (cost_isgt(&chromo1->worth, &chromo2->worth))
 		return 1;
 	else
 		return -1;
@@ -195,7 +196,7 @@ spread_chromo(PlannerInfo *root, Chromosome *chromo, Pool *pool)
 				tmp_chromo;
 
 	/* new chromo is so bad we can't use it */
-	if (chromo->worth > pool->data[pool->size - 1].worth)
+	if (cost_isgt(&chromo->worth, &pool->data[pool->size - 1].worth))
 		return;
 
 	/* do a binary search to find the index of the new chromo */
@@ -209,11 +210,11 @@ spread_chromo(PlannerInfo *root, Chromosome *chromo, Pool *pool)
 	{
 		/* these 4 cases find a new location */
 
-		if (chromo->worth <= pool->data[top].worth)
+		if (!cost_isgt(&chromo->worth, &pool->data[top].worth))
 			index = top;
-		else if (chromo->worth == pool->data[mid].worth)
+		else if (cost_asscalar(&chromo->worth) == cost_asscalar(&pool->data[mid].worth))
 			index = mid;
-		else if (chromo->worth == pool->data[bot].worth)
+		else if (cost_asscalar(&chromo->worth) == cost_asscalar(&pool->data[bot].worth))
 			index = bot;
 		else if (bot - top <= 1)
 			index = bot;
@@ -224,7 +225,7 @@ spread_chromo(PlannerInfo *root, Chromosome *chromo, Pool *pool)
 		 * yet been found.
 		 */
 
-		else if (chromo->worth < pool->data[mid].worth)
+		else if (cost_islt(&chromo->worth, &pool->data[mid].worth))
 		{
 			bot = mid;
 			mid = top + ((bot - top) / 2);
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index db3a68a..d227eef 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -1419,7 +1419,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
 			}
 			else if (nppath == NULL ||
 					 (cheapest_partial_path != NULL &&
-					  cheapest_partial_path->total_cost < nppath->total_cost))
+					  cost_islt(&cheapest_partial_path->total_cost, &nppath->total_cost)))
 			{
 				/* Partial path is cheaper or the only option. */
 				Assert(cheapest_partial_path != NULL);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index c5f6593..1789f0b 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -117,7 +117,7 @@ double		parallel_setup_cost = DEFAULT_PARALLEL_SETUP_COST;
 
 int			effective_cache_size = DEFAULT_EFFECTIVE_CACHE_SIZE;
 
-Cost		disable_cost = 1.0e10;
+double		disable_cost = 1.0e10;
 
 int			max_parallel_workers_per_gather = 2;
 
@@ -171,7 +171,7 @@ static Selectivity get_foreign_key_join_selectivity(PlannerInfo *root,
 													Relids inner_relids,
 													SpecialJoinInfo *sjinfo,
 													List **restrictlist);
-static Cost append_nonpartial_cost(List *subpaths, int numpaths,
+static Cost *append_nonpartial_cost(List *subpaths, int numpaths,
 								   int parallel_workers);
 static void set_rel_width(PlannerInfo *root, RelOptInfo *rel);
 static double relation_byte_size(double tuples, int width);
@@ -211,7 +211,7 @@ void
 cost_seqscan(Path *path, PlannerInfo *root,
 			 RelOptInfo *baserel, ParamPathInfo *param_info)
 {
-	Cost		startup_cost = 0;
+	Cost		startup_cost = {0};
 	Cost		cpu_run_cost;
 	Cost		disk_run_cost;
 	double		spc_seq_page_cost;
@@ -229,7 +229,7 @@ cost_seqscan(Path *path, PlannerInfo *root,
 		path->rows = baserel->rows;
 
 	if (!enable_seqscan)
-		startup_cost += disable_cost;
+		cost_add_member(&startup_cost, disable_cost);
 
 	/* fetch estimated page cost for tablespace containing table */
 	get_tablespace_page_costs(baserel->reltablespace,
@@ -239,17 +239,18 @@ cost_seqscan(Path *path, PlannerInfo *root,
 	/*
 	 * disk costs
 	 */
-	disk_run_cost = spc_seq_page_cost * baserel->pages;
+	cost_set_member_mul_spc(&disk_run_cost, spc_, seq_page_cost, baserel->pages);
 
 	/* CPU costs */
 	get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);
 
-	startup_cost += qpqual_cost.startup;
-	cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
-	cpu_run_cost = cpu_per_tuple * baserel->tuples;
+	cost_add(&startup_cost, &qpqual_cost.startup);
+	cost_set_member_add(&cpu_per_tuple, cpu_tuple_cost, &qpqual_cost.per_tuple);
+	cpu_run_cost = cpu_per_tuple;
+	cost_mul_scalar(&cpu_run_cost, baserel->tuples);
 	/* tlist eval costs are paid per output row, not per tuple scanned */
-	startup_cost += path->pathtarget->cost.startup;
-	cpu_run_cost += path->pathtarget->cost.per_tuple * path->rows;
+	cost_add(&startup_cost, &path->pathtarget->cost.startup);
+	cost_add_mul(&cpu_run_cost, &path->pathtarget->cost.per_tuple, path->rows);
 
 	/* Adjust costing for parallelism, if used. */
 	if (path->parallel_workers > 0)
@@ -257,7 +258,7 @@ cost_seqscan(Path *path, PlannerInfo *root,
 		double		parallel_divisor = get_parallel_divisor(path);
 
 		/* The CPU cost is divided among all the workers. */
-		cpu_run_cost /= parallel_divisor;
+		cost_mul_scalar(&cpu_run_cost, 1.0/parallel_divisor);
 
 		/*
 		 * It may be possible to amortize some of the I/O cost, but probably
@@ -274,7 +275,7 @@ cost_seqscan(Path *path, PlannerInfo *root,
 	}
 
 	path->startup_cost = startup_cost;
-	path->total_cost = startup_cost + cpu_run_cost + disk_run_cost;
+	cost_set_sum3(&path->total_cost, &startup_cost, &cpu_run_cost, &disk_run_cost);
 }
 
 /*
@@ -288,14 +289,13 @@ void
 cost_samplescan(Path *path, PlannerInfo *root,
 				RelOptInfo *baserel, ParamPathInfo *param_info)
 {
-	Cost		startup_cost = 0;
-	Cost		run_cost = 0;
+	Cost		startup_cost = {0};
+	Cost		run_cost = {0};
 	RangeTblEntry *rte;
 	TableSampleClause *tsc;
 	TsmRoutine *tsm;
 	double		spc_seq_page_cost,
-				spc_random_page_cost,
-				spc_page_cost;
+				spc_random_page_cost;
 	QualCost	qpqual_cost;
 	Cost		cpu_per_tuple;
 
@@ -319,14 +319,16 @@ cost_samplescan(Path *path, PlannerInfo *root,
 							  &spc_seq_page_cost);
 
 	/* if NextSampleBlock is used, assume random access, else sequential */
-	spc_page_cost = (tsm->NextSampleBlock != NULL) ?
-		spc_random_page_cost : spc_seq_page_cost;
 
 	/*
 	 * disk costs (recall that baserel->pages has already been set to the
 	 * number of pages the sampling method will visit)
 	 */
-	run_cost += spc_page_cost * baserel->pages;
+
+	if (tsm->NextSampleBlock != NULL)
+		cost_add_member_mul(&run_cost, random_page_cost, baserel->pages);
+	else
+		cost_add_member_mul(&run_cost, seq_page_cost, baserel->pages);
 
 	/*
 	 * CPU costs (recall that baserel->tuples has already been set to the
@@ -338,15 +340,15 @@ cost_samplescan(Path *path, PlannerInfo *root,
 	 */
 	get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);
 
-	startup_cost += qpqual_cost.startup;
-	cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
-	run_cost += cpu_per_tuple * baserel->tuples;
+	cost_add(&startup_cost, &qpqual_cost.startup);
+	cost_set_member_add(&cpu_per_tuple, cpu_tuple_cost, &qpqual_cost.per_tuple);
+	cost_add_mul(&run_cost, &cpu_per_tuple, baserel->tuples);
 	/* tlist eval costs are paid per output row, not per tuple scanned */
-	startup_cost += path->pathtarget->cost.startup;
-	run_cost += path->pathtarget->cost.per_tuple * path->rows;
+	cost_add(&startup_cost, &path->pathtarget->cost.startup);
+	cost_add_mul(&run_cost, &path->pathtarget->cost.per_tuple, path->rows);
 
 	path->startup_cost = startup_cost;
-	path->total_cost = startup_cost + run_cost;
+	cost_set_sum2(&path->total_cost, &startup_cost, &run_cost);
 }
 
 /*
@@ -364,8 +366,8 @@ cost_gather(GatherPath *path, PlannerInfo *root,
 			RelOptInfo *rel, ParamPathInfo *param_info,
 			double *rows)
 {
-	Cost		startup_cost = 0;
-	Cost		run_cost = 0;
+	Cost		startup_cost = {0};
+	Cost		run_cost = {0};
 
 	/* Mark the path with the correct row estimate */
 	if (rows)
@@ -377,14 +379,14 @@ cost_gather(GatherPath *path, PlannerInfo *root,
 
 	startup_cost = path->subpath->startup_cost;
 
-	run_cost = path->subpath->total_cost - path->subpath->startup_cost;
+	cost_set_diff(&run_cost, &path->subpath->total_cost, &path->subpath->startup_cost);
 
 	/* Parallel setup and communication cost. */
-	startup_cost += parallel_setup_cost;
-	run_cost += parallel_tuple_cost * path->path.rows;
+	cost_add_member(&startup_cost, parallel_setup_cost);
+	cost_add_member_mul(&run_cost, parallel_tuple_cost, path->path.rows);
 
 	path->path.startup_cost = startup_cost;
-	path->path.total_cost = (startup_cost + run_cost);
+	cost_set_sum2(&path->path.total_cost, &startup_cost, &run_cost);
 }
 
 /*
@@ -403,8 +405,8 @@ cost_gather_merge(GatherMergePath *path, PlannerInfo *root,
 				  Cost input_startup_cost, Cost input_total_cost,
 				  double *rows)
 {
-	Cost		startup_cost = 0;
-	Cost		run_cost = 0;
+	Cost		startup_cost = {0};
+	Cost		run_cost = {0};
 	Cost		comparison_cost;
 	double		N;
 	double		logN;
@@ -418,7 +420,7 @@ cost_gather_merge(GatherMergePath *path, PlannerInfo *root,
 		path->path.rows = rel->rows;
 
 	if (!enable_gathermerge)
-		startup_cost += disable_cost;
+		cost_add_member(&startup_cost, disable_cost);
 
 	/*
 	 * Add one to the number of workers to account for the leader.  This might
@@ -430,16 +432,16 @@ cost_gather_merge(GatherMergePath *path, PlannerInfo *root,
 	logN = LOG2(N);
 
 	/* Assumed cost per tuple comparison */
-	comparison_cost = 2.0 * cpu_operator_cost;
+	cost_set_member_mul(&comparison_cost, cpu_operator_cost, 2.0);
 
 	/* Heap creation cost */
-	startup_cost += comparison_cost * N * logN;
+	cost_add_mul(&startup_cost, &comparison_cost, N * logN);
 
 	/* Per-tuple heap maintenance cost */
-	run_cost += path->path.rows * comparison_cost * logN;
+	cost_add_mul(&run_cost, &comparison_cost, path->path.rows * logN);
 
 	/* small cost for heap management, like cost_merge_append */
-	run_cost += cpu_operator_cost * path->path.rows;
+	cost_add_member_mul(&run_cost, cpu_operator_cost, path->path.rows);
 
 	/*
 	 * Parallel setup and communication cost.  Since Gather Merge, unlike
@@ -447,11 +449,11 @@ cost_gather_merge(GatherMergePath *path, PlannerInfo *root,
 	 * worker, we bump the IPC cost up a little bit as compared with Gather.
 	 * For lack of a better idea, charge an extra 5%.
 	 */
-	startup_cost += parallel_setup_cost;
-	run_cost += parallel_tuple_cost * path->path.rows * 1.05;
+	cost_add_member(&startup_cost, parallel_setup_cost);
+	cost_add_member_mul(&run_cost, parallel_tuple_cost, path->path.rows * 1.05);
 
-	path->path.startup_cost = startup_cost + input_startup_cost;
-	path->path.total_cost = (startup_cost + run_cost + input_total_cost);
+	cost_set_sum2(&path->path.startup_cost, &startup_cost, &input_startup_cost);
+	cost_set_sum3(&path->path.total_cost, &startup_cost, &run_cost, &input_total_cost);
 }
 
 /*
@@ -481,9 +483,9 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count,
 	bool		indexonly = (path->path.pathtype == T_IndexOnlyScan);
 	amcostestimate_function amcostestimate;
 	List	   *qpquals;
-	Cost		startup_cost = 0;
-	Cost		run_cost = 0;
-	Cost		cpu_run_cost = 0;
+	Cost		startup_cost = {0};
+	Cost		run_cost = {0};
+	Cost		cpu_run_cost = {0};
 	Cost		indexStartupCost;
 	Cost		indexTotalCost;
 	Selectivity indexSelectivity;
@@ -531,7 +533,7 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count,
 	}
 
 	if (!enable_indexscan)
-		startup_cost += disable_cost;
+		cost_add_member(&startup_cost, disable_cost);
 	/* we don't need to check enable_indexonlyscan; indxpath.c does that */
 
 	/*
@@ -556,8 +558,9 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count,
 	path->indexselectivity = indexSelectivity;
 
 	/* all costs for touching index itself included here */
-	startup_cost += indexStartupCost;
-	run_cost += indexTotalCost - indexStartupCost;
+	cost_add(&startup_cost, &indexStartupCost);
+	cost_add(&run_cost, &indexTotalCost);
+	cost_sub(&run_cost, &indexStartupCost);
 
 	/* estimate number of main-table tuples fetched */
 	tuples_fetched = clamp_row_est(indexSelectivity * baserel->tuples);
@@ -614,7 +617,7 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count,
 
 		rand_heap_pages = pages_fetched;
 
-		max_IO_cost = (pages_fetched * spc_random_page_cost) / loop_count;
+		cost_set_member_mul_spc(&max_IO_cost, spc_, random_page_cost, pages_fetched / loop_count);
 
 		/*
 		 * In the perfectly correlated case, the number of pages touched by
@@ -636,7 +639,7 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count,
 		if (indexonly)
 			pages_fetched = ceil(pages_fetched * (1.0 - baserel->allvisfrac));
 
-		min_IO_cost = (pages_fetched * spc_random_page_cost) / loop_count;
+		cost_set_member_mul_spc(&min_IO_cost, spc_, seq_page_cost, pages_fetched / loop_count);
 	}
 	else
 	{
@@ -655,7 +658,7 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count,
 		rand_heap_pages = pages_fetched;
 
 		/* max_IO_cost is for the perfectly uncorrelated case (csquared=0) */
-		max_IO_cost = pages_fetched * spc_random_page_cost;
+		cost_set_member_mul_spc(&max_IO_cost, spc_, random_page_cost, pages_fetched);
 
 		/* min_IO_cost is for the perfectly correlated case (csquared=1) */
 		pages_fetched = ceil(indexSelectivity * (double) baserel->pages);
@@ -665,12 +668,12 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count,
 
 		if (pages_fetched > 0)
 		{
-			min_IO_cost = spc_random_page_cost;
+			cost_set_member_mul_spc(&min_IO_cost, spc_, random_page_cost, 1.0);
 			if (pages_fetched > 1)
-				min_IO_cost += (pages_fetched - 1) * spc_seq_page_cost;
+				cost_set_member_mul_spc(&min_IO_cost, spc_, seq_page_cost, pages_fetched - 1);
 		}
 		else
-			min_IO_cost = 0;
+			cost_zero(&min_IO_cost);
 	}
 
 	if (partial_path)
@@ -711,7 +714,10 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count,
 	 */
 	csquared = indexCorrelation * indexCorrelation;
 
-	run_cost += max_IO_cost + csquared * (min_IO_cost - max_IO_cost);
+	cost_add(&run_cost, &max_IO_cost);
+	cost_sub(&min_IO_cost, &max_IO_cost);
+	cost_mul_scalar(&min_IO_cost, csquared);
+	cost_add(&run_cost, &min_IO_cost);
 
 	/*
 	 * Estimate CPU costs per tuple.
@@ -721,14 +727,14 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count,
 	 */
 	cost_qual_eval(&qpqual_cost, qpquals, root);
 
-	startup_cost += qpqual_cost.startup;
-	cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
+	cost_add(&startup_cost, &qpqual_cost.startup);
+	cost_set_member_add(&cpu_per_tuple, cpu_tuple_cost, &qpqual_cost.per_tuple);
 
-	cpu_run_cost += cpu_per_tuple * tuples_fetched;
+	cost_add_mul(&cpu_run_cost, &cpu_per_tuple, tuples_fetched);
 
 	/* tlist eval costs are paid per output row, not per tuple scanned */
-	startup_cost += path->path.pathtarget->cost.startup;
-	cpu_run_cost += path->path.pathtarget->cost.per_tuple * path->path.rows;
+	cost_add(&startup_cost, &path->path.pathtarget->cost.startup);
+	cost_add_mul(&cpu_run_cost, &path->path.pathtarget->cost.per_tuple, path->path.rows);
 
 	/* Adjust costing for parallelism, if used. */
 	if (path->path.parallel_workers > 0)
@@ -738,13 +744,13 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count,
 		path->path.rows = clamp_row_est(path->path.rows / parallel_divisor);
 
 		/* The CPU cost is divided among all the workers. */
-		cpu_run_cost /= parallel_divisor;
+		cost_mul_scalar(&cpu_run_cost, 1.0/parallel_divisor);
 	}
 
-	run_cost += cpu_run_cost;
+	cost_add(&run_cost, &cpu_run_cost);
 
 	path->path.startup_cost = startup_cost;
-	path->path.total_cost = startup_cost + run_cost;
+	cost_set_sum2(&path->path.total_cost, &startup_cost, &run_cost);
 }
 
 /*
@@ -941,8 +947,8 @@ cost_bitmap_heap_scan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
 					  ParamPathInfo *param_info,
 					  Path *bitmapqual, double loop_count)
 {
-	Cost		startup_cost = 0;
-	Cost		run_cost = 0;
+	Cost		startup_cost = {0};
+	Cost		run_cost = {0};
 	Cost		indexTotalCost;
 	QualCost	qpqual_cost;
 	Cost		cpu_per_tuple;
@@ -966,13 +972,13 @@ cost_bitmap_heap_scan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
 		path->rows = baserel->rows;
 
 	if (!enable_bitmapscan)
-		startup_cost += disable_cost;
+		cost_add_member(&startup_cost, disable_cost);
 
 	pages_fetched = compute_bitmap_pages(root, baserel, bitmapqual,
 										 loop_count, &indexTotalCost,
 										 &tuples_fetched);
 
-	startup_cost += indexTotalCost;
+	cost_add(&startup_cost, &indexTotalCost);
 	T = (baserel->pages > 1) ? (double) baserel->pages : 1.0;
 
 	/* Fetch estimated page costs for tablespace containing table. */
@@ -987,14 +993,15 @@ cost_bitmap_heap_scan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
 	 * nonlinear, too. For lack of a better idea, interpolate like this to
 	 * determine the cost per page.
 	 */
-	if (pages_fetched >= 2.0)
-		cost_per_page = spc_random_page_cost -
-			(spc_random_page_cost - spc_seq_page_cost)
-			* sqrt(pages_fetched / T);
-	else
-		cost_per_page = spc_random_page_cost;
+	if (pages_fetched >= 2.0) {
+		cost_set_member_mul_spc(&cost_per_page, spc_, random_page_cost,
+				1-sqrt(pages_fetched/T));
+
+		cost_set_member_mul_spc(&cost_per_page, spc_, seq_page_cost, sqrt(pages_fetched/T));
+	} else
+		cost_set_member_mul_spc(&cost_per_page, spc_, random_page_cost, 1.0);
 
-	run_cost += pages_fetched * cost_per_page;
+	cost_add_mul(&run_cost, &cost_per_page, pages_fetched);
 
 	/*
 	 * Estimate CPU costs per tuple.
@@ -1007,9 +1014,9 @@ cost_bitmap_heap_scan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
 	 */
 	get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);
 
-	startup_cost += qpqual_cost.startup;
-	cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
-	cpu_run_cost = cpu_per_tuple * tuples_fetched;
+	cost_add(&startup_cost, &qpqual_cost.startup);
+	cost_set_member_add(&cpu_per_tuple, cpu_tuple_cost, &qpqual_cost.per_tuple);
+	cost_add_mul(&cpu_run_cost, &cpu_per_tuple, tuples_fetched);
 
 	/* Adjust costing for parallelism, if used. */
 	if (path->parallel_workers > 0)
@@ -1017,20 +1024,20 @@ cost_bitmap_heap_scan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
 		double		parallel_divisor = get_parallel_divisor(path);
 
 		/* The CPU cost is divided among all the workers. */
-		cpu_run_cost /= parallel_divisor;
+		cost_mul_scalar(&cpu_run_cost, 1.0/parallel_divisor);
 
 		path->rows = clamp_row_est(path->rows / parallel_divisor);
 	}
 
 
-	run_cost += cpu_run_cost;
+	cost_add(&run_cost, &cpu_run_cost);
 
 	/* tlist eval costs are paid per output row, not per tuple scanned */
-	startup_cost += path->pathtarget->cost.startup;
-	run_cost += path->pathtarget->cost.per_tuple * path->rows;
+	cost_add(&startup_cost, &path->pathtarget->cost.startup);
+	cost_add_mul(&run_cost, &path->pathtarget->cost.per_tuple, path->rows);
 
 	path->startup_cost = startup_cost;
-	path->total_cost = startup_cost + run_cost;
+	cost_set_sum2(&path->total_cost, &startup_cost, &run_cost);
 }
 
 /*
@@ -1051,7 +1058,7 @@ cost_bitmap_tree_node(Path *path, Cost *cost, Selectivity *selec)
 		 * scan doesn't look to be the same cost as an indexscan to retrieve a
 		 * single tuple.
 		 */
-		*cost += 0.1 * cpu_operator_cost * path->rows;
+		cost_add_member_mul(cost, cpu_operator_cost, 0.1 * path->rows);
 	}
 	else if (IsA(path, BitmapAndPath))
 	{
@@ -1066,7 +1073,8 @@ cost_bitmap_tree_node(Path *path, Cost *cost, Selectivity *selec)
 	else
 	{
 		elog(ERROR, "unrecognized node type: %d", nodeTag(path));
-		*cost = *selec = 0;		/* keep compiler quiet */
+		*selec = 0;		/* keep compiler quiet */
+		cost_zero(cost);
 	}
 }
 
@@ -1083,7 +1091,7 @@ cost_bitmap_tree_node(Path *path, Cost *cost, Selectivity *selec)
 void
 cost_bitmap_and_node(BitmapAndPath *path, PlannerInfo *root)
 {
-	Cost		totalCost;
+	Cost		totalCost = {0};
 	Selectivity selec;
 	ListCell   *l;
 
@@ -1096,7 +1104,6 @@ cost_bitmap_and_node(BitmapAndPath *path, PlannerInfo *root)
 	 * cpu_operator_cost for each tbm_intersect needed.  Probably too small,
 	 * definitely too simplistic?
 	 */
-	totalCost = 0.0;
 	selec = 1.0;
 	foreach(l, path->bitmapquals)
 	{
@@ -1108,9 +1115,9 @@ cost_bitmap_and_node(BitmapAndPath *path, PlannerInfo *root)
 
 		selec *= subselec;
 
-		totalCost += subCost;
+		cost_add(&totalCost, &subCost);
 		if (l != list_head(path->bitmapquals))
-			totalCost += 100.0 * cpu_operator_cost;
+			cost_add_member_mul(&totalCost, cpu_operator_cost, 100.0);
 	}
 	path->bitmapselectivity = selec;
 	path->path.rows = 0;		/* per above, not used */
@@ -1127,7 +1134,7 @@ cost_bitmap_and_node(BitmapAndPath *path, PlannerInfo *root)
 void
 cost_bitmap_or_node(BitmapOrPath *path, PlannerInfo *root)
 {
-	Cost		totalCost;
+	Cost		totalCost = {0};
 	Selectivity selec;
 	ListCell   *l;
 
@@ -1141,7 +1148,6 @@ cost_bitmap_or_node(BitmapOrPath *path, PlannerInfo *root)
 	 * definitely too simplistic?  We are aware that the tbm_unions are
 	 * optimized out when the inputs are BitmapIndexScans.
 	 */
-	totalCost = 0.0;
 	selec = 0.0;
 	foreach(l, path->bitmapquals)
 	{
@@ -1153,10 +1159,10 @@ cost_bitmap_or_node(BitmapOrPath *path, PlannerInfo *root)
 
 		selec += subselec;
 
-		totalCost += subCost;
+		cost_add(&totalCost, &subCost);
 		if (l != list_head(path->bitmapquals) &&
 			!IsA(subpath, IndexPath))
-			totalCost += 100.0 * cpu_operator_cost;
+			cost_add_member_mul(&totalCost, cpu_operator_cost, 100.0);
 	}
 	path->bitmapselectivity = Min(selec, 1.0);
 	path->path.rows = 0;		/* per above, not used */
@@ -1176,8 +1182,8 @@ void
 cost_tidscan(Path *path, PlannerInfo *root,
 			 RelOptInfo *baserel, List *tidquals, ParamPathInfo *param_info)
 {
-	Cost		startup_cost = 0;
-	Cost		run_cost = 0;
+	Cost		startup_cost = {0};
+	Cost		run_cost = {0};
 	bool		isCurrentOf = false;
 	QualCost	qpqual_cost;
 	Cost		cpu_per_tuple;
@@ -1234,11 +1240,12 @@ cost_tidscan(Path *path, PlannerInfo *root,
 	 */
 	if (isCurrentOf)
 	{
-		Assert(baserel->baserestrictcost.startup >= disable_cost);
-		startup_cost -= disable_cost;
+		// Assert(cost_asscalar(&baserel->baserestrictcost.startup) >= disable_cost);
+		Assert(&baserel->baserestrictcost.startup.disable_cost!=0);
+		cost_add_member_mul(&startup_cost, disable_cost, -1);
 	}
 	else if (!enable_tidscan)
-		startup_cost += disable_cost;
+		cost_add_member(&startup_cost, disable_cost);
 
 	/*
 	 * The TID qual expressions will be computed once, any other baserestrict
@@ -1252,23 +1259,23 @@ cost_tidscan(Path *path, PlannerInfo *root,
 							  NULL);
 
 	/* disk costs --- assume each tuple on a different page */
-	run_cost += spc_random_page_cost * ntuples;
+	cost_add_member_mul_spc(&run_cost, spc_, random_page_cost, ntuples);
 
 	/* Add scanning CPU costs */
 	get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);
 
 	/* XXX currently we assume TID quals are a subset of qpquals */
-	startup_cost += qpqual_cost.startup + tid_qual_cost.per_tuple;
-	cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple -
-		tid_qual_cost.per_tuple;
-	run_cost += cpu_per_tuple * ntuples;
+	cost_add2(&startup_cost, &qpqual_cost.startup, &tid_qual_cost.per_tuple);
+	cost_set_member_add(&cpu_per_tuple, cpu_tuple_cost, &qpqual_cost.per_tuple);
+	cost_sub(&cpu_per_tuple, &tid_qual_cost.per_tuple);
+	cost_add_mul(&run_cost, &cpu_per_tuple, ntuples);
 
 	/* tlist eval costs are paid per output row, not per tuple scanned */
-	startup_cost += path->pathtarget->cost.startup;
-	run_cost += path->pathtarget->cost.per_tuple * path->rows;
+	cost_add(&startup_cost, &path->pathtarget->cost.startup);
+	cost_add_mul(&run_cost, &path->pathtarget->cost.per_tuple, path->rows);
 
 	path->startup_cost = startup_cost;
-	path->total_cost = startup_cost + run_cost;
+	cost_set_sum2(&path->total_cost, &startup_cost, &run_cost);
 }
 
 /*
@@ -1309,15 +1316,16 @@ cost_subqueryscan(SubqueryScanPath *path, PlannerInfo *root,
 	get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);
 
 	startup_cost = qpqual_cost.startup;
-	cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
-	run_cost = cpu_per_tuple * baserel->tuples;
+	cost_set_member_add(&cpu_per_tuple, cpu_tuple_cost, &qpqual_cost.per_tuple);
+	run_cost = cpu_per_tuple;
+	cost_mul_scalar(&run_cost, baserel->tuples);
 
 	/* tlist eval costs are paid per output row, not per tuple scanned */
-	startup_cost += path->path.pathtarget->cost.startup;
-	run_cost += path->path.pathtarget->cost.per_tuple * path->path.rows;
+	cost_add(&startup_cost, &path->path.pathtarget->cost.startup);
+	cost_add_mul(&run_cost, &path->path.pathtarget->cost.per_tuple, path->path.rows);
 
-	path->path.startup_cost += startup_cost;
-	path->path.total_cost += startup_cost + run_cost;
+	cost_add(&path->path.startup_cost, &startup_cost);
+	cost_add2(&path->path.total_cost, &startup_cost, &run_cost);
 }
 
 /*
@@ -1331,8 +1339,8 @@ void
 cost_functionscan(Path *path, PlannerInfo *root,
 				  RelOptInfo *baserel, ParamPathInfo *param_info)
 {
-	Cost		startup_cost = 0;
-	Cost		run_cost = 0;
+	Cost		startup_cost = {0};
+	Cost		run_cost = {0};
 	QualCost	qpqual_cost;
 	Cost		cpu_per_tuple;
 	RangeTblEntry *rte;
@@ -1364,21 +1372,21 @@ cost_functionscan(Path *path, PlannerInfo *root,
 	 */
 	cost_qual_eval_node(&exprcost, (Node *) rte->functions, root);
 
-	startup_cost += exprcost.startup + exprcost.per_tuple;
+	cost_add2(&startup_cost, &exprcost.startup, &exprcost.per_tuple);
 
 	/* Add scanning CPU costs */
 	get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);
 
-	startup_cost += qpqual_cost.startup;
-	cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
-	run_cost += cpu_per_tuple * baserel->tuples;
+	cost_add(&startup_cost, &qpqual_cost.startup);
+	cost_set_member_add(&cpu_per_tuple, cpu_tuple_cost, &qpqual_cost.per_tuple);
+	cost_add_mul(&run_cost, &cpu_per_tuple, baserel->tuples);
 
 	/* tlist eval costs are paid per output row, not per tuple scanned */
-	startup_cost += path->pathtarget->cost.startup;
-	run_cost += path->pathtarget->cost.per_tuple * path->rows;
+	cost_add(&startup_cost, &path->pathtarget->cost.startup);
+	cost_add_mul(&run_cost, &path->pathtarget->cost.per_tuple, path->rows);
 
 	path->startup_cost = startup_cost;
-	path->total_cost = startup_cost + run_cost;
+	cost_set_sum2(&path->total_cost, &startup_cost, &run_cost);
 }
 
 /*
@@ -1392,8 +1400,8 @@ void
 cost_tablefuncscan(Path *path, PlannerInfo *root,
 				   RelOptInfo *baserel, ParamPathInfo *param_info)
 {
-	Cost		startup_cost = 0;
-	Cost		run_cost = 0;
+	Cost		startup_cost = {0};
+	Cost		run_cost = {0};
 	QualCost	qpqual_cost;
 	Cost		cpu_per_tuple;
 	RangeTblEntry *rte;
@@ -1420,21 +1428,21 @@ cost_tablefuncscan(Path *path, PlannerInfo *root,
 	 */
 	cost_qual_eval_node(&exprcost, (Node *) rte->tablefunc, root);
 
-	startup_cost += exprcost.startup + exprcost.per_tuple;
+	cost_add2(&startup_cost, &exprcost.startup, &exprcost.per_tuple);
 
 	/* Add scanning CPU costs */
 	get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);
 
-	startup_cost += qpqual_cost.startup;
-	cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
-	run_cost += cpu_per_tuple * baserel->tuples;
+	cost_add(&startup_cost, &qpqual_cost.startup);
+	cost_set_member_add(&cpu_per_tuple, cpu_tuple_cost, &qpqual_cost.per_tuple);
+	cost_add_mul(&run_cost, &cpu_per_tuple, baserel->tuples);
 
 	/* tlist eval costs are paid per output row, not per tuple scanned */
-	startup_cost += path->pathtarget->cost.startup;
-	run_cost += path->pathtarget->cost.per_tuple * path->rows;
+	cost_add(&startup_cost, &path->pathtarget->cost.startup);
+	cost_add_mul(&run_cost, &path->pathtarget->cost.per_tuple, path->rows);
 
 	path->startup_cost = startup_cost;
-	path->total_cost = startup_cost + run_cost;
+	cost_set_sum2(&path->total_cost, &startup_cost, &run_cost);
 }
 
 /*
@@ -1448,8 +1456,8 @@ void
 cost_valuesscan(Path *path, PlannerInfo *root,
 				RelOptInfo *baserel, ParamPathInfo *param_info)
 {
-	Cost		startup_cost = 0;
-	Cost		run_cost = 0;
+	Cost		startup_cost = {0};
+	Cost		run_cost = {0};
 	QualCost	qpqual_cost;
 	Cost		cpu_per_tuple;
 
@@ -1467,21 +1475,22 @@ cost_valuesscan(Path *path, PlannerInfo *root,
 	 * For now, estimate list evaluation cost at one operator eval per list
 	 * (probably pretty bogus, but is it worth being smarter?)
 	 */
-	cpu_per_tuple = cpu_operator_cost;
+	cost_set_member(&cpu_per_tuple, cpu_operator_cost);
 
 	/* Add scanning CPU costs */
 	get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);
 
-	startup_cost += qpqual_cost.startup;
-	cpu_per_tuple += cpu_tuple_cost + qpqual_cost.per_tuple;
-	run_cost += cpu_per_tuple * baserel->tuples;
+	cost_add(&startup_cost, &qpqual_cost.startup);
+	cost_add_member(&cpu_per_tuple, cpu_tuple_cost);
+	cost_add(&cpu_per_tuple, &qpqual_cost.per_tuple);
+	cost_add_mul(&run_cost, &cpu_per_tuple, baserel->tuples);
 
 	/* tlist eval costs are paid per output row, not per tuple scanned */
-	startup_cost += path->pathtarget->cost.startup;
-	run_cost += path->pathtarget->cost.per_tuple * path->rows;
+	cost_add(&startup_cost, &path->pathtarget->cost.startup);
+	cost_add_mul(&run_cost, &path->pathtarget->cost.per_tuple, path->rows);
 
 	path->startup_cost = startup_cost;
-	path->total_cost = startup_cost + run_cost;
+	cost_set_sum2(&path->total_cost, &startup_cost, &run_cost);
 }
 
 /*
@@ -1498,8 +1507,8 @@ void
 cost_ctescan(Path *path, PlannerInfo *root,
 			 RelOptInfo *baserel, ParamPathInfo *param_info)
 {
-	Cost		startup_cost = 0;
-	Cost		run_cost = 0;
+	Cost		startup_cost = {0};
+	Cost		run_cost = {0};
 	QualCost	qpqual_cost;
 	Cost		cpu_per_tuple;
 
@@ -1514,21 +1523,21 @@ cost_ctescan(Path *path, PlannerInfo *root,
 		path->rows = baserel->rows;
 
 	/* Charge one CPU tuple cost per row for tuplestore manipulation */
-	cpu_per_tuple = cpu_tuple_cost;
+	cost_set_member(&cpu_per_tuple, cpu_tuple_cost);
 
 	/* Add scanning CPU costs */
 	get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);
 
-	startup_cost += qpqual_cost.startup;
-	cpu_per_tuple += cpu_tuple_cost + qpqual_cost.per_tuple;
-	run_cost += cpu_per_tuple * baserel->tuples;
+	cost_add(&startup_cost, &qpqual_cost.startup);
+	cost_set_member_add(&cpu_per_tuple, cpu_tuple_cost, &qpqual_cost.per_tuple);
+	cost_add_mul(&run_cost, &cpu_per_tuple, baserel->tuples);
 
 	/* tlist eval costs are paid per output row, not per tuple scanned */
-	startup_cost += path->pathtarget->cost.startup;
-	run_cost += path->pathtarget->cost.per_tuple * path->rows;
+	cost_add(&startup_cost, &path->pathtarget->cost.startup);
+	cost_add_mul(&run_cost, &path->pathtarget->cost.per_tuple, path->rows);
 
 	path->startup_cost = startup_cost;
-	path->total_cost = startup_cost + run_cost;
+	cost_set_sum2(&path->total_cost, &startup_cost, &run_cost);
 }
 
 /*
@@ -1539,8 +1548,8 @@ void
 cost_namedtuplestorescan(Path *path, PlannerInfo *root,
 						 RelOptInfo *baserel, ParamPathInfo *param_info)
 {
-	Cost		startup_cost = 0;
-	Cost		run_cost = 0;
+	Cost		startup_cost = {0};
+	Cost		run_cost = {0};
 	QualCost	qpqual_cost;
 	Cost		cpu_per_tuple;
 
@@ -1555,17 +1564,17 @@ cost_namedtuplestorescan(Path *path, PlannerInfo *root,
 		path->rows = baserel->rows;
 
 	/* Charge one CPU tuple cost per row for tuplestore manipulation */
-	cpu_per_tuple = cpu_tuple_cost;
+	cost_set_member(&cpu_per_tuple, cpu_tuple_cost);
 
 	/* Add scanning CPU costs */
 	get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);
 
-	startup_cost += qpqual_cost.startup;
-	cpu_per_tuple += cpu_tuple_cost + qpqual_cost.per_tuple;
-	run_cost += cpu_per_tuple * baserel->tuples;
+	cost_add(&startup_cost, &qpqual_cost.startup);
+	cost_set_member_add(&cpu_per_tuple, cpu_tuple_cost, &qpqual_cost.per_tuple);
+	cost_add_mul(&run_cost, &cpu_per_tuple, baserel->tuples);
 
 	path->startup_cost = startup_cost;
-	path->total_cost = startup_cost + run_cost;
+	cost_set_sum2(&path->total_cost, &startup_cost, &run_cost);
 }
 
 /*
@@ -1576,8 +1585,8 @@ void
 cost_resultscan(Path *path, PlannerInfo *root,
 				RelOptInfo *baserel, ParamPathInfo *param_info)
 {
-	Cost		startup_cost = 0;
-	Cost		run_cost = 0;
+	Cost		startup_cost = {0};
+	Cost		run_cost = {0};
 	QualCost	qpqual_cost;
 	Cost		cpu_per_tuple;
 
@@ -1594,12 +1603,12 @@ cost_resultscan(Path *path, PlannerInfo *root,
 	/* We charge qual cost plus cpu_tuple_cost */
 	get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);
 
-	startup_cost += qpqual_cost.startup;
-	cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
-	run_cost += cpu_per_tuple * baserel->tuples;
+	cost_add(&startup_cost, &qpqual_cost.startup);
+	cost_set_member_add(&cpu_per_tuple, cpu_tuple_cost, &qpqual_cost.per_tuple);
+	cost_add_mul(&run_cost, &cpu_per_tuple, baserel->tuples);
 
 	path->startup_cost = startup_cost;
-	path->total_cost = startup_cost + run_cost;
+	cost_set_sum2(&path->total_cost, &startup_cost, &run_cost);
 }
 
 /*
@@ -1627,7 +1636,7 @@ cost_recursive_union(Path *runion, Path *nrterm, Path *rterm)
 	 * size of each one of them.  These are mighty shaky assumptions but it's
 	 * hard to see how to do better.
 	 */
-	total_cost += 10 * rterm->total_cost;
+	cost_add_mul(&total_cost, &rterm->total_cost, 10);
 	total_rows += 10 * rterm->rows;
 
 	/*
@@ -1635,7 +1644,7 @@ cost_recursive_union(Path *runion, Path *nrterm, Path *rterm)
 	 * manipulating the tuplestores.  (We don't worry about possible
 	 * spill-to-disk costs.)
 	 */
-	total_cost += cpu_tuple_cost * total_rows;
+	cost_add_member_mul(&total_cost, cpu_tuple_cost, total_rows);
 
 	runion->startup_cost = startup_cost;
 	runion->total_cost = total_cost;
@@ -1691,19 +1700,19 @@ cost_recursive_union(Path *runion, Path *nrterm, Path *rterm)
  */
 void
 cost_sort(Path *path, PlannerInfo *root,
-		  List *pathkeys, Cost input_cost, double tuples, int width,
-		  Cost comparison_cost, int sort_mem,
+		  List *pathkeys, Cost *input_cost, double tuples, int width,
+		  Cost *comparison_cost, int sort_mem,
 		  double limit_tuples)
 {
-	Cost		startup_cost = input_cost;
-	Cost		run_cost = 0;
+	Cost		startup_cost = *input_cost;
+	Cost		run_cost = {0};
 	double		input_bytes = relation_byte_size(tuples, width);
 	double		output_bytes;
 	double		output_tuples;
 	long		sort_mem_bytes = sort_mem * 1024L;
 
 	if (!enable_sort)
-		startup_cost += disable_cost;
+		cost_add_member(&startup_cost, disable_cost);
 
 	path->rows = tuples;
 
@@ -1715,7 +1724,7 @@ cost_sort(Path *path, PlannerInfo *root,
 		tuples = 2.0;
 
 	/* Include the default cost-per-comparison */
-	comparison_cost += 2.0 * cpu_operator_cost;
+	cost_add_member_mul(comparison_cost, cpu_operator_cost, 2.0);
 
 	/* Do we have a useful LIMIT? */
 	if (limit_tuples > 0 && limit_tuples < tuples)
@@ -1745,7 +1754,7 @@ cost_sort(Path *path, PlannerInfo *root,
 		 *
 		 * Assume about N log2 N comparisons
 		 */
-		startup_cost += comparison_cost * tuples * LOG2(tuples);
+		cost_add_mul(&startup_cost, comparison_cost, tuples * LOG2(tuples));
 
 		/* Disk costs */
 
@@ -1756,8 +1765,8 @@ cost_sort(Path *path, PlannerInfo *root,
 			log_runs = 1.0;
 		npageaccesses = 2.0 * npages * log_runs;
 		/* Assume 3/4ths of accesses are sequential, 1/4th are not */
-		startup_cost += npageaccesses *
-			(seq_page_cost * 0.75 + random_page_cost * 0.25);
+		cost_add_member_mul(&startup_cost, seq_page_cost, npageaccesses * 0.75);
+		cost_add_member_mul(&startup_cost, random_page_cost, npageaccesses * 0.25);
 	}
 	else if (tuples > 2 * output_tuples || input_bytes > sort_mem_bytes)
 	{
@@ -1767,12 +1776,12 @@ cost_sort(Path *path, PlannerInfo *root,
 		 * factor is a bit higher than for quicksort.  Tweak it so that the
 		 * cost curve is continuous at the crossover point.
 		 */
-		startup_cost += comparison_cost * tuples * LOG2(2.0 * output_tuples);
+		cost_add_mul(&startup_cost, comparison_cost, tuples * LOG2(2.0 * output_tuples));
 	}
 	else
 	{
 		/* We'll use plain quicksort on all the input tuples */
-		startup_cost += comparison_cost * tuples * LOG2(tuples);
+		cost_add_mul(&startup_cost, comparison_cost, tuples * LOG2(tuples));
 	}
 
 	/*
@@ -1783,10 +1792,10 @@ cost_sort(Path *path, PlannerInfo *root,
 	 * here --- the upper LIMIT will pro-rate the run cost so we'd be double
 	 * counting the LIMIT otherwise.
 	 */
-	run_cost += cpu_operator_cost * tuples;
+	cost_add_member_mul(&run_cost, cpu_operator_cost, tuples);
 
 	path->startup_cost = startup_cost;
-	path->total_cost = startup_cost + run_cost;
+	cost_set_sum2(&path->total_cost, &startup_cost, &run_cost);
 }
 
 /*
@@ -1795,7 +1804,7 @@ cost_sort(Path *path, PlannerInfo *root,
  *	  The non-partial paths are assumed to be the first "numpaths" paths
  *	  from the subpaths list, and to be in order of decreasing cost.
  */
-static Cost
+static Cost *
 append_nonpartial_cost(List *subpaths, int numpaths, int parallel_workers)
 {
 	Cost	   *costarr;
@@ -1808,7 +1817,7 @@ append_nonpartial_cost(List *subpaths, int numpaths, int parallel_workers)
 	int			max_index;
 
 	if (numpaths == 0)
-		return 0;
+		return NULL;
 
 	/*
 	 * Array length is number of workers or number of relevant paths,
@@ -1847,12 +1856,12 @@ append_nonpartial_cost(List *subpaths, int numpaths, int parallel_workers)
 		if (path_index++ == numpaths)
 			break;
 
-		costarr[min_index] += subpath->total_cost;
+		cost_add(&costarr[min_index], &subpath->total_cost);
 
 		/* Update the new min cost array index */
 		for (min_index = i = 0; i < arrlen; i++)
 		{
-			if (costarr[i] < costarr[min_index])
+			if (cost_islt(&costarr[i], &costarr[min_index]))
 				min_index = i;
 		}
 	}
@@ -1860,11 +1869,11 @@ append_nonpartial_cost(List *subpaths, int numpaths, int parallel_workers)
 	/* Return the highest cost from the array */
 	for (max_index = i = 0; i < arrlen; i++)
 	{
-		if (costarr[i] > costarr[max_index])
+		if (cost_isgt(&costarr[i], &costarr[max_index]))
 			max_index = i;
 	}
 
-	return costarr[max_index];
+	return costarr+max_index;
 }
 
 /*
@@ -1876,8 +1885,8 @@ cost_append(AppendPath *apath)
 {
 	ListCell   *l;
 
-	apath->path.startup_cost = 0;
-	apath->path.total_cost = 0;
+	cost_zero(&apath->path.startup_cost);
+	cost_zero(&apath->path.total_cost);
 	apath->path.rows = 0;
 
 	if (apath->subpaths == NIL)
@@ -1903,7 +1912,7 @@ cost_append(AppendPath *apath)
 				Path	   *subpath = (Path *) lfirst(l);
 
 				apath->path.rows += subpath->rows;
-				apath->path.total_cost += subpath->total_cost;
+				cost_add(&apath->path.total_cost, &subpath->total_cost);
 			}
 		}
 		else
@@ -1933,6 +1942,7 @@ cost_append(AppendPath *apath)
 
 				if (!pathkeys_contained_in(pathkeys, subpath->pathkeys))
 				{
+					Cost zerocost = {0};
 					/*
 					 * We'll need to insert a Sort node, so include costs for
 					 * that.  We can use the parent's LIMIT if any, since we
@@ -1942,18 +1952,18 @@ cost_append(AppendPath *apath)
 					cost_sort(&sort_path,
 							  NULL, /* doesn't currently need root */
 							  pathkeys,
-							  subpath->total_cost,
+							  &subpath->total_cost,
 							  subpath->rows,
 							  subpath->pathtarget->width,
-							  0.0,
+							  &zerocost,
 							  work_mem,
 							  apath->limit_tuples);
 					subpath = &sort_path;
 				}
 
 				apath->path.rows += subpath->rows;
-				apath->path.startup_cost += subpath->startup_cost;
-				apath->path.total_cost += subpath->total_cost;
+				cost_add(&apath->path.startup_cost, &subpath->startup_cost);
+				cost_add(&apath->path.total_cost, &subpath->total_cost);
 			}
 		}
 	}
@@ -1978,8 +1988,10 @@ cost_append(AppendPath *apath)
 			if (i == 0)
 				apath->path.startup_cost = subpath->startup_cost;
 			else if (i < apath->path.parallel_workers)
-				apath->path.startup_cost = Min(apath->path.startup_cost,
-											   subpath->startup_cost);
+				apath->path.startup_cost = cost_islt(&apath->path.startup_cost,
+											   &subpath->startup_cost) ?
+												apath->path.startup_cost :
+												subpath->startup_cost;
 
 			/*
 			 * Apply parallel divisor to subpaths.  Scale the number of rows
@@ -1997,7 +2009,7 @@ cost_append(AppendPath *apath)
 				subpath_parallel_divisor = get_parallel_divisor(subpath);
 				apath->path.rows += subpath->rows * (subpath_parallel_divisor /
 													 parallel_divisor);
-				apath->path.total_cost += subpath->total_cost;
+				cost_add(&apath->path.total_cost, &subpath->total_cost);
 			}
 
 			apath->path.rows = clamp_row_est(apath->path.rows);
@@ -2006,18 +2018,18 @@ cost_append(AppendPath *apath)
 		}
 
 		/* Add cost for non-partial subpaths. */
-		apath->path.total_cost +=
+		cost_add(&apath->path.total_cost,
 			append_nonpartial_cost(apath->subpaths,
 								   apath->first_partial_path,
-								   apath->path.parallel_workers);
+								   apath->path.parallel_workers));
 	}
 
 	/*
 	 * Although Append does not do any selection or projection, it's not free;
 	 * add a small per-tuple overhead.
 	 */
-	apath->path.total_cost +=
-		cpu_tuple_cost * APPEND_CPU_COST_MULTIPLIER * apath->path.rows;
+	cost_add_member_mul(&apath->path.total_cost,
+		cpu_tuple_cost, APPEND_CPU_COST_MULTIPLIER * apath->path.rows);
 }
 
 /*
@@ -2050,8 +2062,8 @@ cost_merge_append(Path *path, PlannerInfo *root,
 				  Cost input_startup_cost, Cost input_total_cost,
 				  double tuples)
 {
-	Cost		startup_cost = 0;
-	Cost		run_cost = 0;
+	Cost		startup_cost = {0};
+	Cost		run_cost = {0};
 	Cost		comparison_cost;
 	double		N;
 	double		logN;
@@ -2063,22 +2075,22 @@ cost_merge_append(Path *path, PlannerInfo *root,
 	logN = LOG2(N);
 
 	/* Assumed cost per tuple comparison */
-	comparison_cost = 2.0 * cpu_operator_cost;
+	cost_add_member_mul(&comparison_cost, cpu_operator_cost, 2.0);
 
 	/* Heap creation cost */
-	startup_cost += comparison_cost * N * logN;
+	cost_add_mul(&startup_cost, &comparison_cost, N * logN);
 
 	/* Per-tuple heap maintenance cost */
-	run_cost += tuples * comparison_cost * logN;
+	cost_add_mul(&run_cost, &comparison_cost, logN);
 
 	/*
 	 * Although MergeAppend does not do any selection or projection, it's not
 	 * free; add a small per-tuple overhead.
 	 */
-	run_cost += cpu_tuple_cost * APPEND_CPU_COST_MULTIPLIER * tuples;
+	cost_add_member_mul(&run_cost, cpu_tuple_cost, APPEND_CPU_COST_MULTIPLIER * tuples);
 
-	path->startup_cost = startup_cost + input_startup_cost;
-	path->total_cost = startup_cost + run_cost + input_total_cost;
+	cost_set_sum2(&path->startup_cost, &startup_cost, &input_startup_cost);
+	cost_set_sum3(&path->total_cost, &startup_cost, &run_cost, &input_total_cost);
 }
 
 /*
@@ -2099,10 +2111,11 @@ cost_material(Path *path,
 			  double tuples, int width)
 {
 	Cost		startup_cost = input_startup_cost;
-	Cost		run_cost = input_total_cost - input_startup_cost;
+	Cost		run_cost;
 	double		nbytes = relation_byte_size(tuples, width);
 	long		work_mem_bytes = work_mem * 1024L;
 
+	cost_set_diff(&run_cost, &input_total_cost, &input_startup_cost);
 	path->rows = tuples;
 
 	/*
@@ -2117,7 +2130,7 @@ cost_material(Path *path,
 	 * doesn't do qual-checking or projection, so it's got less overhead than
 	 * most plan nodes.
 	 */
-	run_cost += 2 * cpu_operator_cost * tuples;
+	cost_add_member_mul(&run_cost, cpu_operator_cost, 2 * tuples);
 
 	/*
 	 * If we will spill to disk, charge at the rate of seq_page_cost per page.
@@ -2129,11 +2142,11 @@ cost_material(Path *path,
 	{
 		double		npages = ceil(nbytes / BLCKSZ);
 
-		run_cost += seq_page_cost * npages;
+		cost_add_member_mul(&run_cost, seq_page_cost, npages);
 	}
 
 	path->startup_cost = startup_cost;
-	path->total_cost = startup_cost + run_cost;
+	cost_set_sum2(&path->total_cost, &startup_cost, &run_cost);
 }
 
 /*
@@ -2152,7 +2165,7 @@ cost_agg(Path *path, PlannerInfo *root,
 		 AggStrategy aggstrategy, const AggClauseCosts *aggcosts,
 		 int numGroupCols, double numGroups,
 		 List *quals,
-		 Cost input_startup_cost, Cost input_total_cost,
+		 Cost *input_startup_cost, Cost *input_total_cost,
 		 double input_tuples)
 {
 	double		output_tuples;
@@ -2192,47 +2205,48 @@ cost_agg(Path *path, PlannerInfo *root,
 	 */
 	if (aggstrategy == AGG_PLAIN)
 	{
-		startup_cost = input_total_cost;
-		startup_cost += aggcosts->transCost.startup;
-		startup_cost += aggcosts->transCost.per_tuple * input_tuples;
-		startup_cost += aggcosts->finalCost.startup;
-		startup_cost += aggcosts->finalCost.per_tuple;
+		startup_cost = *input_total_cost;
+		cost_add(&startup_cost, &aggcosts->transCost.startup);
+		cost_add_mul(&startup_cost, &aggcosts->transCost.per_tuple, input_tuples);
+		cost_add(&startup_cost, &aggcosts->finalCost.startup);
+		cost_add(&startup_cost, &aggcosts->finalCost.per_tuple);
 		/* we aren't grouping */
-		total_cost = startup_cost + cpu_tuple_cost;
+		total_cost = startup_cost;
+		cost_add_member(&total_cost, cpu_tuple_cost);
 		output_tuples = 1;
 	}
 	else if (aggstrategy == AGG_SORTED || aggstrategy == AGG_MIXED)
 	{
 		/* Here we are able to deliver output on-the-fly */
-		startup_cost = input_startup_cost;
-		total_cost = input_total_cost;
+		startup_cost = *input_startup_cost;
+		total_cost = *input_total_cost;
 		if (aggstrategy == AGG_MIXED && !enable_hashagg)
 		{
-			startup_cost += disable_cost;
-			total_cost += disable_cost;
+			cost_add_member(&startup_cost, disable_cost);
+			cost_add_member(&total_cost, disable_cost);
 		}
 		/* calcs phrased this way to match HASHED case, see note above */
-		total_cost += aggcosts->transCost.startup;
-		total_cost += aggcosts->transCost.per_tuple * input_tuples;
-		total_cost += (cpu_operator_cost * numGroupCols) * input_tuples;
-		total_cost += aggcosts->finalCost.startup;
-		total_cost += aggcosts->finalCost.per_tuple * numGroups;
-		total_cost += cpu_tuple_cost * numGroups;
+		cost_add(&total_cost, &aggcosts->transCost.startup);
+		cost_add_mul(&total_cost, &aggcosts->transCost.per_tuple, input_tuples);
+		cost_add_member_mul(&total_cost, cpu_operator_cost, numGroupCols * input_tuples);
+		cost_add(&total_cost, &aggcosts->finalCost.startup);
+		cost_add_mul(&total_cost, &aggcosts->finalCost.per_tuple, numGroups);
+		cost_add_member_mul(&total_cost, cpu_tuple_cost, numGroups);
 		output_tuples = numGroups;
 	}
 	else
 	{
 		/* must be AGG_HASHED */
-		startup_cost = input_total_cost;
+		startup_cost = *input_total_cost;
 		if (!enable_hashagg)
-			startup_cost += disable_cost;
-		startup_cost += aggcosts->transCost.startup;
-		startup_cost += aggcosts->transCost.per_tuple * input_tuples;
-		startup_cost += (cpu_operator_cost * numGroupCols) * input_tuples;
-		startup_cost += aggcosts->finalCost.startup;
+			cost_add_member(&startup_cost, disable_cost);
+		cost_add(&startup_cost, &aggcosts->transCost.startup);
+		cost_add_mul(&startup_cost, &aggcosts->transCost.per_tuple, input_tuples);
+		cost_add_member_mul(&startup_cost, cpu_operator_cost, numGroupCols * input_tuples);
+		cost_add(&startup_cost, &aggcosts->finalCost.startup);
 		total_cost = startup_cost;
-		total_cost += aggcosts->finalCost.per_tuple * numGroups;
-		total_cost += cpu_tuple_cost * numGroups;
+		cost_add_mul(&total_cost, &aggcosts->finalCost.per_tuple, numGroups);
+		cost_add_member_mul(&total_cost, cpu_tuple_cost, numGroups);
 		output_tuples = numGroups;
 	}
 
@@ -2245,8 +2259,9 @@ cost_agg(Path *path, PlannerInfo *root,
 		QualCost	qual_cost;
 
 		cost_qual_eval(&qual_cost, quals, root);
-		startup_cost += qual_cost.startup;
-		total_cost += qual_cost.startup + output_tuples * qual_cost.per_tuple;
+		cost_add(&startup_cost, &qual_cost.startup);
+		cost_add(&total_cost, &qual_cost.startup);
+		cost_add_mul(&total_cost, &qual_cost.per_tuple, output_tuples);
 
 		output_tuples = clamp_row_est(output_tuples *
 									  clauselist_selectivity(root,
@@ -2296,26 +2311,27 @@ cost_windowagg(Path *path, PlannerInfo *root,
 		Cost		wfunccost;
 		QualCost	argcosts;
 
-		argcosts.startup = argcosts.per_tuple = 0;
+		cost_zero(&argcosts.startup);
+		cost_zero(&argcosts.per_tuple);
 		add_function_cost(root, wfunc->winfnoid, (Node *) wfunc,
 						  &argcosts);
-		startup_cost += argcosts.startup;
+		cost_add(&startup_cost, &argcosts.startup);
 		wfunccost = argcosts.per_tuple;
 
 		/* also add the input expressions' cost to per-input-row costs */
 		cost_qual_eval_node(&argcosts, (Node *) wfunc->args, root);
-		startup_cost += argcosts.startup;
-		wfunccost += argcosts.per_tuple;
+		cost_add(&startup_cost, &argcosts.startup);
+		cost_add(&wfunccost, &argcosts.per_tuple);
 
 		/*
 		 * Add the filter's cost to per-input-row costs.  XXX We should reduce
 		 * input expression costs according to filter selectivity.
 		 */
 		cost_qual_eval_node(&argcosts, (Node *) wfunc->aggfilter, root);
-		startup_cost += argcosts.startup;
-		wfunccost += argcosts.per_tuple;
+		cost_add(&startup_cost, &argcosts.startup);
+		cost_add(&wfunccost, &argcosts.per_tuple);
 
-		total_cost += wfunccost * input_tuples;
+		cost_add_mul(&total_cost, &wfunccost, input_tuples);
 	}
 
 	/*
@@ -2326,8 +2342,8 @@ cost_windowagg(Path *path, PlannerInfo *root,
 	 * XXX this neglects costs of spooling the data to disk when it overflows
 	 * work_mem.  Sooner or later that should get accounted for.
 	 */
-	total_cost += cpu_operator_cost * (numPartCols + numOrderCols) * input_tuples;
-	total_cost += cpu_tuple_cost * input_tuples;
+	cost_add_member_mul(&total_cost, cpu_operator_cost, (numPartCols + numOrderCols) * input_tuples);
+	cost_add_member_mul(&total_cost, cpu_tuple_cost, input_tuples);
 
 	path->rows = input_tuples;
 	path->startup_cost = startup_cost;
@@ -2361,7 +2377,7 @@ cost_group(Path *path, PlannerInfo *root,
 	 * Charge one cpu_operator_cost per comparison per input tuple. We assume
 	 * all columns get compared at most of the tuples.
 	 */
-	total_cost += cpu_operator_cost * input_tuples * numGroupCols;
+	cost_add_member_mul(&total_cost, cpu_operator_cost, input_tuples * numGroupCols);
 
 	/*
 	 * If there are quals (HAVING quals), account for their cost and
@@ -2372,8 +2388,9 @@ cost_group(Path *path, PlannerInfo *root,
 		QualCost	qual_cost;
 
 		cost_qual_eval(&qual_cost, quals, root);
-		startup_cost += qual_cost.startup;
-		total_cost += qual_cost.startup + output_tuples * qual_cost.per_tuple;
+		cost_add(&startup_cost, &qual_cost.startup);;
+		cost_add(&total_cost, &qual_cost.startup);
+		cost_add_mul(&total_cost, &qual_cost.per_tuple, output_tuples);
 
 		output_tuples = clamp_row_est(output_tuples *
 									  clauselist_selectivity(root,
@@ -2418,8 +2435,8 @@ initial_cost_nestloop(PlannerInfo *root, JoinCostWorkspace *workspace,
 					  Path *outer_path, Path *inner_path,
 					  JoinPathExtraData *extra)
 {
-	Cost		startup_cost = 0;
-	Cost		run_cost = 0;
+	Cost		startup_cost = {0};
+	Cost		run_cost = {0};
 	double		outer_path_rows = outer_path->rows;
 	Cost		inner_rescan_start_cost;
 	Cost		inner_rescan_total_cost;
@@ -2439,13 +2456,14 @@ initial_cost_nestloop(PlannerInfo *root, JoinCostWorkspace *workspace,
 	 * their sum.  We'll also pay the inner path's rescan startup cost
 	 * multiple times.
 	 */
-	startup_cost += outer_path->startup_cost + inner_path->startup_cost;
-	run_cost += outer_path->total_cost - outer_path->startup_cost;
+	cost_add2(&startup_cost, &outer_path->startup_cost, &inner_path->startup_cost);
+	cost_add(&run_cost, &outer_path->total_cost);
+	cost_sub(&run_cost, &outer_path->startup_cost);
 	if (outer_path_rows > 1)
-		run_cost += (outer_path_rows - 1) * inner_rescan_start_cost;
+		cost_add_mul(&run_cost, &inner_rescan_start_cost, outer_path_rows - 1);
 
-	inner_run_cost = inner_path->total_cost - inner_path->startup_cost;
-	inner_rescan_run_cost = inner_rescan_total_cost - inner_rescan_start_cost;
+	cost_set_diff(&inner_run_cost, &inner_path->total_cost, &inner_path->startup_cost);
+	cost_set_diff(&inner_rescan_run_cost, &inner_rescan_total_cost, &inner_rescan_start_cost);
 
 	if (jointype == JOIN_SEMI || jointype == JOIN_ANTI ||
 		extra->inner_unique)
@@ -2465,16 +2483,16 @@ initial_cost_nestloop(PlannerInfo *root, JoinCostWorkspace *workspace,
 	else
 	{
 		/* Normal case; we'll scan whole input rel for each outer row */
-		run_cost += inner_run_cost;
+		cost_add(&run_cost, &inner_run_cost);
 		if (outer_path_rows > 1)
-			run_cost += (outer_path_rows - 1) * inner_rescan_run_cost;
+			cost_add_mul(&run_cost, &inner_rescan_run_cost, outer_path_rows - 1);
 	}
 
 	/* CPU costs left for later */
 
 	/* Public result fields */
 	workspace->startup_cost = startup_cost;
-	workspace->total_cost = startup_cost + run_cost;
+	cost_set_sum2(&workspace->total_cost, &startup_cost, &run_cost);
 	/* Save private data for final_cost_nestloop */
 	workspace->run_cost = run_cost;
 }
@@ -2529,7 +2547,7 @@ final_cost_nestloop(PlannerInfo *root, NestPath *path,
 	 * disabled, which doesn't seem like the way to bet.
 	 */
 	if (!enable_nestloop)
-		startup_cost += disable_cost;
+		cost_add_member(&startup_cost, disable_cost);
 
 	/* cost of inner-relation source data (we already dealt with outer rel) */
 
@@ -2590,9 +2608,9 @@ final_cost_nestloop(PlannerInfo *root, NestPath *path,
 			 * case, use inner_run_cost for the first matched tuple and
 			 * inner_rescan_run_cost for additional ones.
 			 */
-			run_cost += inner_run_cost * inner_scan_frac;
+			cost_add_mul(&run_cost, &inner_run_cost, inner_scan_frac);
 			if (outer_matched_rows > 1)
-				run_cost += (outer_matched_rows - 1) * inner_rescan_run_cost * inner_scan_frac;
+				cost_add_mul(&run_cost, &inner_rescan_run_cost, (outer_matched_rows - 1) * inner_scan_frac);
 
 			/*
 			 * Add the cost of inner-scan executions for unmatched outer rows.
@@ -2600,8 +2618,7 @@ final_cost_nestloop(PlannerInfo *root, NestPath *path,
 			 * of a nonempty scan.  We consider that these are all rescans,
 			 * since we used inner_run_cost once already.
 			 */
-			run_cost += outer_unmatched_rows *
-				inner_rescan_run_cost / inner_path_rows;
+			cost_add_mul(&run_cost, &inner_rescan_run_cost, outer_unmatched_rows / inner_path_rows);
 
 			/*
 			 * We won't be evaluating any quals at all for unmatched rows, so
@@ -2627,7 +2644,7 @@ final_cost_nestloop(PlannerInfo *root, NestPath *path,
 			ntuples += outer_unmatched_rows * inner_path_rows;
 
 			/* Now add the forced full scan, and decrement appropriate count */
-			run_cost += inner_run_cost;
+			cost_add(&run_cost, &inner_run_cost);
 			if (outer_unmatched_rows >= 1)
 				outer_unmatched_rows -= 1;
 			else
@@ -2635,11 +2652,11 @@ final_cost_nestloop(PlannerInfo *root, NestPath *path,
 
 			/* Add inner run cost for additional outer tuples having matches */
 			if (outer_matched_rows > 0)
-				run_cost += outer_matched_rows * inner_rescan_run_cost * inner_scan_frac;
+				cost_add_mul(&run_cost, &inner_rescan_run_cost, outer_matched_rows * inner_scan_frac);
 
 			/* Add inner run cost for additional unmatched outer tuples */
 			if (outer_unmatched_rows > 0)
-				run_cost += outer_unmatched_rows * inner_rescan_run_cost;
+				cost_add_mul(&run_cost, &inner_rescan_run_cost, outer_unmatched_rows);
 		}
 	}
 	else
@@ -2652,16 +2669,17 @@ final_cost_nestloop(PlannerInfo *root, NestPath *path,
 
 	/* CPU costs */
 	cost_qual_eval(&restrict_qual_cost, path->joinrestrictinfo, root);
-	startup_cost += restrict_qual_cost.startup;
-	cpu_per_tuple = cpu_tuple_cost + restrict_qual_cost.per_tuple;
-	run_cost += cpu_per_tuple * ntuples;
+	cost_add(&startup_cost, &restrict_qual_cost.startup);
+	cpu_per_tuple = restrict_qual_cost.per_tuple;
+	cost_add_member(&cpu_per_tuple, cpu_tuple_cost);
+	cost_add_mul(&run_cost, &cpu_per_tuple, ntuples);
 
 	/* tlist eval costs are paid per output row, not per tuple scanned */
-	startup_cost += path->path.pathtarget->cost.startup;
-	run_cost += path->path.pathtarget->cost.per_tuple * path->path.rows;
+	cost_add(&startup_cost, &path->path.pathtarget->cost.startup);
+	cost_add_mul(&run_cost, &path->path.pathtarget->cost.per_tuple, path->path.rows);
 
 	path->path.startup_cost = startup_cost;
-	path->path.total_cost = startup_cost + run_cost;
+	cost_set_sum2(&path->path.total_cost, &startup_cost, &run_cost);
 }
 
 /*
@@ -2702,8 +2720,9 @@ initial_cost_mergejoin(PlannerInfo *root, JoinCostWorkspace *workspace,
 					   List *outersortkeys, List *innersortkeys,
 					   JoinPathExtraData *extra)
 {
-	Cost		startup_cost = 0;
-	Cost		run_cost = 0;
+	Cost		startup_cost = {0};
+	Cost		run_cost = {0};
+	Cost		zerocost = {0};
 	double		outer_path_rows = outer_path->rows;
 	double		inner_path_rows = inner_path->rows;
 	Cost		inner_run_cost;
@@ -2825,28 +2844,28 @@ initial_cost_mergejoin(PlannerInfo *root, JoinCostWorkspace *workspace,
 
 	if (outersortkeys)			/* do we need to sort outer? */
 	{
+		Cost tmp;
 		cost_sort(&sort_path,
 				  root,
 				  outersortkeys,
-				  outer_path->total_cost,
+				  &outer_path->total_cost,
 				  outer_path_rows,
 				  outer_path->pathtarget->width,
-				  0.0,
+				  &zerocost,
 				  work_mem,
 				  -1.0);
-		startup_cost += sort_path.startup_cost;
-		startup_cost += (sort_path.total_cost - sort_path.startup_cost)
-			* outerstartsel;
-		run_cost += (sort_path.total_cost - sort_path.startup_cost)
-			* (outerendsel - outerstartsel);
+		cost_add(&startup_cost, &sort_path.startup_cost);
+		cost_set_diff(&tmp, &sort_path.total_cost, &sort_path.startup_cost);
+		cost_add_mul(&startup_cost, &tmp, outerstartsel);
+		cost_add_mul(&run_cost, &tmp, outerendsel - outerstartsel);
 	}
 	else
 	{
-		startup_cost += outer_path->startup_cost;
-		startup_cost += (outer_path->total_cost - outer_path->startup_cost)
-			* outerstartsel;
-		run_cost += (outer_path->total_cost - outer_path->startup_cost)
-			* (outerendsel - outerstartsel);
+		Cost tmp;
+		cost_add(&startup_cost, &outer_path->startup_cost);
+		cost_set_diff(&tmp, &outer_path->total_cost, &outer_path->startup_cost);
+		cost_add_mul(&startup_cost, &tmp, outerstartsel);
+		cost_add_mul(&run_cost, &tmp, outerendsel - outerstartsel);
 	}
 
 	if (innersortkeys)			/* do we need to sort inner? */
@@ -2854,25 +2873,23 @@ initial_cost_mergejoin(PlannerInfo *root, JoinCostWorkspace *workspace,
 		cost_sort(&sort_path,
 				  root,
 				  innersortkeys,
-				  inner_path->total_cost,
+				  &inner_path->total_cost,
 				  inner_path_rows,
 				  inner_path->pathtarget->width,
-				  0.0,
+				  &zerocost,
 				  work_mem,
 				  -1.0);
-		startup_cost += sort_path.startup_cost;
-		startup_cost += (sort_path.total_cost - sort_path.startup_cost)
-			* innerstartsel;
-		inner_run_cost = (sort_path.total_cost - sort_path.startup_cost)
-			* (innerendsel - innerstartsel);
+		cost_add(&startup_cost, &sort_path.startup_cost);
+		cost_set_diff(&inner_run_cost, &sort_path.total_cost, &sort_path.startup_cost);
+		cost_add_mul(&startup_cost, &inner_run_cost, innerstartsel);
+		cost_mul_scalar(&inner_run_cost, innerendsel - innerstartsel);
 	}
 	else
 	{
-		startup_cost += inner_path->startup_cost;
-		startup_cost += (inner_path->total_cost - inner_path->startup_cost)
-			* innerstartsel;
-		inner_run_cost = (inner_path->total_cost - inner_path->startup_cost)
-			* (innerendsel - innerstartsel);
+		cost_add(&startup_cost, &inner_path->startup_cost);
+		cost_set_diff(&inner_run_cost, &inner_path->total_cost, &inner_path->startup_cost);
+		cost_add_mul(&startup_cost, &inner_run_cost, innerstartsel);
+		cost_mul_scalar(&inner_run_cost, innerendsel - innerstartsel);
 	}
 
 	/*
@@ -2887,7 +2904,7 @@ initial_cost_mergejoin(PlannerInfo *root, JoinCostWorkspace *workspace,
 
 	/* Public result fields */
 	workspace->startup_cost = startup_cost;
-	workspace->total_cost = startup_cost + run_cost + inner_run_cost;
+	cost_set_sum3(&workspace->total_cost, &startup_cost, &run_cost, &inner_run_cost);
 	/* Save private data for final_cost_mergejoin */
 	workspace->run_cost = run_cost;
 	workspace->inner_run_cost = inner_run_cost;
@@ -2975,7 +2992,7 @@ final_cost_mergejoin(PlannerInfo *root, MergePath *path,
 	 * disabled, which doesn't seem like the way to bet.
 	 */
 	if (!enable_mergejoin)
-		startup_cost += disable_cost;
+		cost_add_member(&startup_cost, disable_cost);
 
 	/*
 	 * Compute cost of the mergequals and qpquals (other restriction clauses)
@@ -2983,8 +3000,8 @@ final_cost_mergejoin(PlannerInfo *root, MergePath *path,
 	 */
 	cost_qual_eval(&merge_qual_cost, mergeclauses, root);
 	cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo, root);
-	qp_qual_cost.startup -= merge_qual_cost.startup;
-	qp_qual_cost.per_tuple -= merge_qual_cost.per_tuple;
+	cost_sub(&qp_qual_cost.startup, &merge_qual_cost.startup);
+	cost_sub(&qp_qual_cost.per_tuple, &merge_qual_cost.per_tuple);
 
 	/*
 	 * With a SEMI or ANTI join, or if the innerrel is known unique, the
@@ -3059,7 +3076,8 @@ final_cost_mergejoin(PlannerInfo *root, MergePath *path,
 	 * a more refined model.  So we just need to inflate the inner run cost by
 	 * rescanratio.
 	 */
-	bare_inner_cost = inner_run_cost * rescanratio;
+	bare_inner_cost = inner_run_cost;
+	cost_mul_scalar(&bare_inner_cost, rescanratio);
 
 	/*
 	 * When we interpose a Material node the re-fetch cost is assumed to be
@@ -3074,8 +3092,8 @@ final_cost_mergejoin(PlannerInfo *root, MergePath *path,
 	 * Note: keep this estimate in sync with create_mergejoin_plan's labeling
 	 * of the generated Material node.
 	 */
-	mat_inner_cost = inner_run_cost +
-		cpu_operator_cost * inner_rows * rescanratio;
+	mat_inner_cost = inner_run_cost;
+	cost_add_member_mul(&mat_inner_cost, cpu_operator_cost, inner_rows * rescanratio);
 
 	/*
 	 * If we don't need mark/restore at all, we don't need materialization.
@@ -3087,7 +3105,7 @@ final_cost_mergejoin(PlannerInfo *root, MergePath *path,
 	 * Prefer materializing if it looks cheaper, unless the user has asked to
 	 * suppress materialization.
 	 */
-	else if (enable_material && mat_inner_cost < bare_inner_cost)
+	else if (enable_material && cost_asscalar(&mat_inner_cost) < cost_asscalar(&bare_inner_cost))
 		path->materialize_inner = true;
 
 	/*
@@ -3131,9 +3149,9 @@ final_cost_mergejoin(PlannerInfo *root, MergePath *path,
 
 	/* Charge the right incremental cost for the chosen case */
 	if (path->materialize_inner)
-		run_cost += mat_inner_cost;
+		cost_add(&run_cost, &mat_inner_cost);
 	else
-		run_cost += bare_inner_cost;
+		cost_add(&run_cost, &bare_inner_cost);
 
 	/* CPU costs */
 
@@ -3142,12 +3160,12 @@ final_cost_mergejoin(PlannerInfo *root, MergePath *path,
 	 * rows plus number of inner rows plus number of rescanned tuples (can we
 	 * refine this?).  At each one, we need to evaluate the mergejoin quals.
 	 */
-	startup_cost += merge_qual_cost.startup;
-	startup_cost += merge_qual_cost.per_tuple *
-		(outer_skip_rows + inner_skip_rows * rescanratio);
-	run_cost += merge_qual_cost.per_tuple *
+	cost_add(&startup_cost, &merge_qual_cost.startup);
+	cost_add_mul(&startup_cost, &merge_qual_cost.per_tuple,
+		outer_skip_rows + inner_skip_rows * rescanratio);
+	cost_add_mul(&run_cost, &merge_qual_cost.per_tuple,
 		((outer_rows - outer_skip_rows) +
-		 (inner_rows - inner_skip_rows) * rescanratio);
+		 (inner_rows - inner_skip_rows) * rescanratio));
 
 	/*
 	 * For each tuple that gets through the mergejoin proper, we charge
@@ -3158,16 +3176,16 @@ final_cost_mergejoin(PlannerInfo *root, MergePath *path,
 	 * Note: we could adjust for SEMI/ANTI joins skipping some qual
 	 * evaluations here, but it's probably not worth the trouble.
 	 */
-	startup_cost += qp_qual_cost.startup;
-	cpu_per_tuple = cpu_tuple_cost + qp_qual_cost.per_tuple;
-	run_cost += cpu_per_tuple * mergejointuples;
+	cost_add(&startup_cost, &qp_qual_cost.startup);
+	cost_set_member_add(&cpu_per_tuple, cpu_tuple_cost, &qp_qual_cost.per_tuple);
+	cost_add_mul(&run_cost, &cpu_per_tuple, mergejointuples);
 
 	/* tlist eval costs are paid per output row, not per tuple scanned */
-	startup_cost += path->jpath.path.pathtarget->cost.startup;
-	run_cost += path->jpath.path.pathtarget->cost.per_tuple * path->jpath.path.rows;
+	cost_add(&startup_cost, &path->jpath.path.pathtarget->cost.startup);
+	cost_add_mul(&run_cost, &path->jpath.path.pathtarget->cost.per_tuple, path->jpath.path.rows);
 
 	path->jpath.path.startup_cost = startup_cost;
-	path->jpath.path.total_cost = startup_cost + run_cost;
+	cost_set_sum2(&path->jpath.path.total_cost, &startup_cost, &run_cost);
 }
 
 /*
@@ -3260,8 +3278,8 @@ initial_cost_hashjoin(PlannerInfo *root, JoinCostWorkspace *workspace,
 					  JoinPathExtraData *extra,
 					  bool parallel_hash)
 {
-	Cost		startup_cost = 0;
-	Cost		run_cost = 0;
+	Cost		startup_cost = {0};
+	Cost		run_cost = {0};
 	double		outer_path_rows = outer_path->rows;
 	double		inner_path_rows = inner_path->rows;
 	double		inner_path_rows_total = inner_path_rows;
@@ -3272,9 +3290,10 @@ initial_cost_hashjoin(PlannerInfo *root, JoinCostWorkspace *workspace,
 	size_t		space_allowed;	/* unused */
 
 	/* cost of source data */
-	startup_cost += outer_path->startup_cost;
-	run_cost += outer_path->total_cost - outer_path->startup_cost;
-	startup_cost += inner_path->total_cost;
+	cost_add(&startup_cost, &outer_path->startup_cost);
+	cost_add(&run_cost, &outer_path->total_cost);
+	cost_sub(&run_cost, &outer_path->startup_cost);
+	cost_add(&startup_cost, &inner_path->total_cost);
 
 	/*
 	 * Cost of computing hash function: must do it once per input tuple. We
@@ -3286,9 +3305,9 @@ initial_cost_hashjoin(PlannerInfo *root, JoinCostWorkspace *workspace,
 	 * should charge the extra eval costs of the left or right side, as
 	 * appropriate, here.  This seems more work than it's worth at the moment.
 	 */
-	startup_cost += (cpu_operator_cost * num_hashclauses + cpu_tuple_cost)
-		* inner_path_rows;
-	run_cost += cpu_operator_cost * num_hashclauses * outer_path_rows;
+	cost_add_member_mul(&startup_cost, cpu_operator_cost, num_hashclauses * inner_path_rows);
+	cost_add_member_mul(&startup_cost, cpu_tuple_cost, inner_path_rows);
+	cost_add_member_mul(&run_cost, cpu_operator_cost, num_hashclauses * outer_path_rows);
 
 	/*
 	 * If this is a parallel hash build, then the value we have for
@@ -3333,15 +3352,15 @@ initial_cost_hashjoin(PlannerInfo *root, JoinCostWorkspace *workspace,
 		double		innerpages = page_size(inner_path_rows,
 										   inner_path->pathtarget->width);
 
-		startup_cost += seq_page_cost * innerpages;
-		run_cost += seq_page_cost * (innerpages + 2 * outerpages);
+		cost_add_member_mul(&startup_cost, seq_page_cost, innerpages);
+		cost_add_member_mul(&run_cost, seq_page_cost, (innerpages + 2 * outerpages));
 	}
 
 	/* CPU costs left for later */
 
 	/* Public result fields */
 	workspace->startup_cost = startup_cost;
-	workspace->total_cost = startup_cost + run_cost;
+	cost_set_sum2(&workspace->total_cost, &startup_cost, &run_cost);
 	/* Save private data for final_cost_hashjoin */
 	workspace->run_cost = run_cost;
 	workspace->numbuckets = numbuckets;
@@ -3405,7 +3424,7 @@ final_cost_hashjoin(PlannerInfo *root, HashPath *path,
 	 * disabled, which doesn't seem like the way to bet.
 	 */
 	if (!enable_hashjoin)
-		startup_cost += disable_cost;
+		cost_add_member(&startup_cost, disable_cost);
 
 	/* mark the path with estimated # of batches */
 	path->num_batches = numbatches;
@@ -3503,7 +3522,7 @@ final_cost_hashjoin(PlannerInfo *root, HashPath *path,
 	if (relation_byte_size(clamp_row_est(inner_path_rows * innermcvfreq),
 						   inner_path->pathtarget->width) >
 		(work_mem * 1024L))
-		startup_cost += disable_cost;
+		cost_add_member(&startup_cost, disable_cost);
 
 	/*
 	 * Compute cost of the hashquals and qpquals (other restriction clauses)
@@ -3511,8 +3530,8 @@ final_cost_hashjoin(PlannerInfo *root, HashPath *path,
 	 */
 	cost_qual_eval(&hash_qual_cost, hashclauses, root);
 	cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo, root);
-	qp_qual_cost.startup -= hash_qual_cost.startup;
-	qp_qual_cost.per_tuple -= hash_qual_cost.per_tuple;
+	cost_sub(&qp_qual_cost.startup, &hash_qual_cost.startup);
+	cost_sub(&qp_qual_cost.per_tuple, &hash_qual_cost.per_tuple);
 
 	/* CPU costs */
 
@@ -3538,9 +3557,9 @@ final_cost_hashjoin(PlannerInfo *root, HashPath *path,
 		outer_matched_rows = rint(outer_path_rows * extra->semifactors.outer_match_frac);
 		inner_scan_frac = 2.0 / (extra->semifactors.match_count + 1.0);
 
-		startup_cost += hash_qual_cost.startup;
-		run_cost += hash_qual_cost.per_tuple * outer_matched_rows *
-			clamp_row_est(inner_path_rows * innerbucketsize * inner_scan_frac) * 0.5;
+		cost_add(&startup_cost, &hash_qual_cost.startup);
+		cost_add_mul(&run_cost, &hash_qual_cost.per_tuple, outer_matched_rows *
+			clamp_row_est(inner_path_rows * innerbucketsize * inner_scan_frac) * 0.5);
 
 		/*
 		 * For unmatched outer-rel rows, the picture is quite a lot different.
@@ -3555,9 +3574,9 @@ final_cost_hashjoin(PlannerInfo *root, HashPath *path,
 		 * effective cost per bucket entry is one-tenth what it is for
 		 * matchable tuples.
 		 */
-		run_cost += hash_qual_cost.per_tuple *
+		cost_add_mul(&run_cost, &hash_qual_cost.per_tuple,
 			(outer_path_rows - outer_matched_rows) *
-			clamp_row_est(inner_path_rows / virtualbuckets) * 0.05;
+			clamp_row_est(inner_path_rows / virtualbuckets) * 0.05);
 
 		/* Get # of tuples that will pass the basic join */
 		if (path->jpath.jointype == JOIN_ANTI)
@@ -3577,9 +3596,9 @@ final_cost_hashjoin(PlannerInfo *root, HashPath *path,
 		 * exactly.  For lack of a better idea, halve the cost estimate to
 		 * allow for that.
 		 */
-		startup_cost += hash_qual_cost.startup;
-		run_cost += hash_qual_cost.per_tuple * outer_path_rows *
-			clamp_row_est(inner_path_rows * innerbucketsize) * 0.5;
+		cost_add(&startup_cost, &hash_qual_cost.startup);
+		cost_add_mul(&run_cost, &hash_qual_cost.per_tuple, outer_path_rows *
+			clamp_row_est(inner_path_rows * innerbucketsize) * 0.5);
 
 		/*
 		 * Get approx # tuples passing the hashquals.  We use
@@ -3595,16 +3614,17 @@ final_cost_hashjoin(PlannerInfo *root, HashPath *path,
 	 * clauses that are to be applied at the join.  (This is pessimistic since
 	 * not all of the quals may get evaluated at each tuple.)
 	 */
-	startup_cost += qp_qual_cost.startup;
-	cpu_per_tuple = cpu_tuple_cost + qp_qual_cost.per_tuple;
-	run_cost += cpu_per_tuple * hashjointuples;
+	cost_add(&startup_cost, &qp_qual_cost.startup);
+	cpu_per_tuple = qp_qual_cost.per_tuple;
+	cost_add_member(&cpu_per_tuple, cpu_tuple_cost);
+	cost_add_mul(&run_cost, &cpu_per_tuple, hashjointuples);
 
 	/* tlist eval costs are paid per output row, not per tuple scanned */
-	startup_cost += path->jpath.path.pathtarget->cost.startup;
-	run_cost += path->jpath.path.pathtarget->cost.per_tuple * path->jpath.path.rows;
+	cost_add(&startup_cost, &path->jpath.path.pathtarget->cost.startup);
+	cost_add_mul(&run_cost, &path->jpath.path.pathtarget->cost.per_tuple, path->jpath.path.rows);
 
 	path->jpath.path.startup_cost = startup_cost;
-	path->jpath.path.total_cost = startup_cost + run_cost;
+	cost_set_sum2(&path->jpath.path.total_cost, &startup_cost, &run_cost);
 }
 
 
@@ -3633,8 +3653,8 @@ cost_subplan(PlannerInfo *root, SubPlan *subplan, Plan *plan)
 		 * cpu_operator_cost per tuple for the work of loading the hashtable,
 		 * too.
 		 */
-		sp_cost.startup += plan->total_cost +
-			cpu_operator_cost * plan->plan_rows;
+		cost_add(&sp_cost.startup, &plan->total_cost);
+		cost_add_member_mul(&sp_cost.startup, cpu_operator_cost, plan->plan_rows);
 
 		/*
 		 * The per-tuple costs include the cost of evaluating the lefthand
@@ -3654,25 +3674,26 @@ cost_subplan(PlannerInfo *root, SubPlan *subplan, Plan *plan)
 		 * tuple_fraction estimates used by make_subplan() in
 		 * plan/subselect.c.
 		 */
-		Cost		plan_run_cost = plan->total_cost - plan->startup_cost;
+		Cost		plan_run_cost;
+		cost_set_diff(&plan_run_cost, &plan->total_cost, &plan->startup_cost);
 
 		if (subplan->subLinkType == EXISTS_SUBLINK)
 		{
 			/* we only need to fetch 1 tuple; clamp to avoid zero divide */
-			sp_cost.per_tuple += plan_run_cost / clamp_row_est(plan->plan_rows);
+			cost_add_mul(&sp_cost.per_tuple, &plan_run_cost, clamp_row_est(plan->plan_rows));
 		}
 		else if (subplan->subLinkType == ALL_SUBLINK ||
 				 subplan->subLinkType == ANY_SUBLINK)
 		{
 			/* assume we need 50% of the tuples */
-			sp_cost.per_tuple += 0.50 * plan_run_cost;
+			cost_add_mul(&sp_cost.per_tuple, &plan_run_cost, 0.50);
 			/* also charge a cpu_operator_cost per row examined */
-			sp_cost.per_tuple += 0.50 * plan->plan_rows * cpu_operator_cost;
+			cost_add_member_mul(&sp_cost.per_tuple, cpu_operator_cost, 0.50 * plan->plan_rows);
 		}
 		else
 		{
 			/* assume we need all tuples */
-			sp_cost.per_tuple += plan_run_cost;
+			cost_add(&sp_cost.per_tuple, &plan_run_cost);
 		}
 
 		/*
@@ -3684,9 +3705,9 @@ cost_subplan(PlannerInfo *root, SubPlan *subplan, Plan *plan)
 		 */
 		if (subplan->parParam == NIL &&
 			ExecMaterializesOutput(nodeTag(plan)))
-			sp_cost.startup += plan->startup_cost;
+			cost_add(&sp_cost.startup, &plan->startup_cost);
 		else
-			sp_cost.per_tuple += plan->startup_cost;
+			cost_add(&sp_cost.per_tuple, &plan->startup_cost);
 	}
 
 	subplan->startup_cost = sp_cost.startup;
@@ -3724,8 +3745,8 @@ cost_rescan(PlannerInfo *root, Path *path,
 			 * and isn't paid over again on rescans. However, all run costs
 			 * will be paid over again.
 			 */
-			*rescan_startup_cost = 0;
-			*rescan_total_cost = path->total_cost - path->startup_cost;
+			cost_zero(rescan_startup_cost);
+			cost_set_diff(rescan_total_cost, &path->total_cost, &path->startup_cost);
 			break;
 		case T_HashJoin:
 
@@ -3736,8 +3757,8 @@ cost_rescan(PlannerInfo *root, Path *path,
 			if (((HashPath *) path)->num_batches == 1)
 			{
 				/* Startup cost is exactly the cost of hash table building */
-				*rescan_startup_cost = 0;
-				*rescan_total_cost = path->total_cost - path->startup_cost;
+				cost_zero(rescan_startup_cost);
+				cost_set_diff(rescan_total_cost, &path->total_cost, &path->startup_cost);
 			}
 			else
 			{
@@ -3755,19 +3776,20 @@ cost_rescan(PlannerInfo *root, Path *path,
 				 * cpu_tuple_cost per tuple, unless the result is large enough
 				 * to spill to disk.
 				 */
-				Cost		run_cost = cpu_tuple_cost * path->rows;
+				Cost		run_cost;
 				double		nbytes = relation_byte_size(path->rows,
 														path->pathtarget->width);
 				long		work_mem_bytes = work_mem * 1024L;
 
+				cost_set_member_mul(&run_cost, cpu_tuple_cost, path->rows);
 				if (nbytes > work_mem_bytes)
 				{
 					/* It will spill, so account for re-read cost */
 					double		npages = ceil(nbytes / BLCKSZ);
 
-					run_cost += seq_page_cost * npages;
+					cost_add_member_mul(&run_cost, seq_page_cost, npages);
 				}
-				*rescan_startup_cost = 0;
+				cost_zero(rescan_startup_cost);
 				*rescan_total_cost = run_cost;
 			}
 			break;
@@ -3782,19 +3804,20 @@ cost_rescan(PlannerInfo *root, Path *path,
 				 * the run_cost charge in cost_sort, and also see comments in
 				 * cost_material before you change it.)
 				 */
-				Cost		run_cost = cpu_operator_cost * path->rows;
+				Cost		run_cost;
 				double		nbytes = relation_byte_size(path->rows,
 														path->pathtarget->width);
 				long		work_mem_bytes = work_mem * 1024L;
 
+				cost_set_member_mul(&run_cost, cpu_operator_cost, path->rows);
 				if (nbytes > work_mem_bytes)
 				{
 					/* It will spill, so account for re-read cost */
 					double		npages = ceil(nbytes / BLCKSZ);
 
-					run_cost += seq_page_cost * npages;
+					cost_add_member_mul(&run_cost, seq_page_cost, npages);
 				}
-				*rescan_startup_cost = 0;
+				cost_zero(rescan_startup_cost);
 				*rescan_total_cost = run_cost;
 			}
 			break;
@@ -3822,8 +3845,8 @@ cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root)
 	ListCell   *l;
 
 	context.root = root;
-	context.total.startup = 0;
-	context.total.per_tuple = 0;
+	cost_zero(&context.total.startup);
+	cost_zero(&context.total.per_tuple);
 
 	/* We don't charge any cost for the implicit ANDing at top level ... */
 
@@ -3847,8 +3870,8 @@ cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root)
 	cost_qual_eval_context context;
 
 	context.root = root;
-	context.total.startup = 0;
-	context.total.per_tuple = 0;
+	cost_zero(&context.total.startup);
+	cost_zero(&context.total.per_tuple);
 
 	cost_qual_eval_walker(qual, &context);
 
@@ -3871,13 +3894,13 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
 	{
 		RestrictInfo *rinfo = (RestrictInfo *) node;
 
-		if (rinfo->eval_cost.startup < 0)
+		if (cost_asscalar(&rinfo->eval_cost.startup) < 0)
 		{
 			cost_qual_eval_context locContext;
 
 			locContext.root = context->root;
-			locContext.total.startup = 0;
-			locContext.total.per_tuple = 0;
+			cost_zero(&locContext.total.startup);
+			cost_zero(&locContext.total.per_tuple);
 
 			/*
 			 * For an OR clause, recurse into the marked-up tree so that we
@@ -3895,13 +3918,13 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
 			if (rinfo->pseudoconstant)
 			{
 				/* count one execution during startup */
-				locContext.total.startup += locContext.total.per_tuple;
-				locContext.total.per_tuple = 0;
+				cost_add(&locContext.total.startup, &locContext.total.per_tuple);
+				cost_zero(&locContext.total.per_tuple);
 			}
 			rinfo->eval_cost = locContext.total;
 		}
-		context->total.startup += rinfo->eval_cost.startup;
-		context->total.per_tuple += rinfo->eval_cost.per_tuple;
+		cost_add(&context->total.startup, &rinfo->eval_cost.startup);
+		cost_add(&context->total.per_tuple, &rinfo->eval_cost.per_tuple);
 		/* do NOT recurse into children */
 		return false;
 	}
@@ -3953,12 +3976,13 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
 		QualCost	sacosts;
 
 		set_sa_opfuncid(saop);
-		sacosts.startup = sacosts.per_tuple = 0;
+		cost_zero(&sacosts.startup);
+		cost_zero(&sacosts.per_tuple);
 		add_function_cost(context->root, saop->opfuncid, NULL,
 						  &sacosts);
-		context->total.startup += sacosts.startup;
-		context->total.per_tuple += sacosts.per_tuple *
-			estimate_array_length(arraynode) * 0.5;
+		cost_add(&context->total.startup, &sacosts.startup);
+		cost_add_mul(&context->total.per_tuple, &sacosts.per_tuple,
+			estimate_array_length(arraynode) * 0.5);
 	}
 	else if (IsA(node, Aggref) ||
 			 IsA(node, WindowFunc))
@@ -3999,10 +4023,10 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
 
 		cost_qual_eval_node(&perelemcost, (Node *) acoerce->elemexpr,
 							context->root);
-		context->total.startup += perelemcost.startup;
-		if (perelemcost.per_tuple > 0)
-			context->total.per_tuple += perelemcost.per_tuple *
-				estimate_array_length((Node *) acoerce->arg);
+		cost_add(&context->total.startup, &perelemcost.startup);
+		if (cost_asscalar(&perelemcost.per_tuple) > 0)
+			cost_add_mul(&context->total.per_tuple, &perelemcost.per_tuple,
+				estimate_array_length((Node *) acoerce->arg));
 	}
 	else if (IsA(node, RowCompareExpr))
 	{
@@ -4025,12 +4049,12 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
 			 IsA(node, NextValueExpr))
 	{
 		/* Treat all these as having cost 1 */
-		context->total.per_tuple += cpu_operator_cost;
+		cost_add_member(&context->total.per_tuple, cpu_operator_cost);
 	}
 	else if (IsA(node, CurrentOfExpr))
 	{
 		/* Report high cost to prevent selection of anything but TID scan */
-		context->total.startup += disable_cost;
+		cost_add_member(&context->total.startup, disable_cost);
 	}
 	else if (IsA(node, SubLink))
 	{
@@ -4047,8 +4071,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
 		 */
 		SubPlan    *subplan = (SubPlan *) node;
 
-		context->total.startup += subplan->startup_cost;
-		context->total.per_tuple += subplan->per_call_cost;
+		cost_add(&context->total.startup, &subplan->startup_cost);
+		cost_add(&context->total.per_tuple, &subplan->per_call_cost);
 
 		/*
 		 * We don't want to recurse into the testexpr, because it was already
@@ -4111,8 +4135,8 @@ get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel,
 		/* Include costs of pushed-down clauses */
 		cost_qual_eval(qpqual_cost, param_info->ppi_clauses, root);
 
-		qpqual_cost->startup += baserel->baserestrictcost.startup;
-		qpqual_cost->per_tuple += baserel->baserestrictcost.per_tuple;
+		cost_add(&qpqual_cost->startup, &baserel->baserestrictcost.startup);
+		cost_add(&qpqual_cost->per_tuple, &baserel->baserestrictcost.per_tuple);
 	}
 	else
 		*qpqual_cost = baserel->baserestrictcost;
@@ -5223,8 +5247,8 @@ set_rel_width(PlannerInfo *root, RelOptInfo *rel)
 	ListCell   *lc;
 
 	/* Vars are assumed to have cost zero, but other exprs do not */
-	rel->reltarget->cost.startup = 0;
-	rel->reltarget->cost.per_tuple = 0;
+	cost_zero(&rel->reltarget->cost.startup);
+	cost_zero(&rel->reltarget->cost.per_tuple);
 
 	foreach(lc, rel->reltarget->exprs)
 	{
@@ -5301,8 +5325,8 @@ set_rel_width(PlannerInfo *root, RelOptInfo *rel)
 
 			tuple_width += phinfo->ph_width;
 			cost_qual_eval_node(&cost, (Node *) phv->phexpr, root);
-			rel->reltarget->cost.startup += cost.startup;
-			rel->reltarget->cost.per_tuple += cost.per_tuple;
+			cost_add(&rel->reltarget->cost.startup, &cost.startup);
+			cost_add(&rel->reltarget->cost.per_tuple, &cost.per_tuple);
 		}
 		else
 		{
@@ -5319,8 +5343,8 @@ set_rel_width(PlannerInfo *root, RelOptInfo *rel)
 			tuple_width += item_width;
 			/* Not entirely clear if we need to account for cost, but do so */
 			cost_qual_eval_node(&cost, node, root);
-			rel->reltarget->cost.startup += cost.startup;
-			rel->reltarget->cost.per_tuple += cost.per_tuple;
+			cost_add(&rel->reltarget->cost.startup, &cost.startup);
+			cost_add(&rel->reltarget->cost.per_tuple, &cost.per_tuple);
 		}
 	}
 
@@ -5379,8 +5403,8 @@ set_pathtarget_cost_width(PlannerInfo *root, PathTarget *target)
 	ListCell   *lc;
 
 	/* Vars are assumed to have cost zero, but other exprs do not */
-	target->cost.startup = 0;
-	target->cost.per_tuple = 0;
+	cost_zero(&target->cost.startup);
+	cost_zero(&target->cost.per_tuple);
 
 	foreach(lc, target->exprs)
 	{
@@ -5434,8 +5458,8 @@ set_pathtarget_cost_width(PlannerInfo *root, PathTarget *target)
 
 			/* Account for cost, too */
 			cost_qual_eval_node(&cost, node, root);
-			target->cost.startup += cost.startup;
-			target->cost.per_tuple += cost.per_tuple;
+			cost_add(&target->cost.startup, &cost.startup);
+			cost_add(&target->cost.per_tuple, &cost.per_tuple);
 		}
 	}
 
@@ -5603,3 +5627,178 @@ compute_bitmap_pages(PlannerInfo *root, RelOptInfo *baserel, Path *bitmapqual,
 
 	return pages_fetched;
 }
+
+void cost_zero(Cost *to)
+{
+	memset(to, 0, sizeof(*to));
+}
+
+void cost_set_sum2(Cost *to, const Cost *from1, const Cost *from2)
+{
+        to->cpu_index_tuple_cost = from1->cpu_index_tuple_cost + from2 ->cpu_index_tuple_cost;
+        to->cpu_operator_cost = from1->cpu_operator_cost + from2->cpu_operator_cost;
+        to->cpu_tuple_cost = from1->cpu_tuple_cost  + from2->cpu_tuple_cost;
+        to->parallel_setup_cost = from1->parallel_setup_cost  + from2->parallel_setup_cost;
+        to->parallel_tuple_cost = from1->parallel_tuple_cost  + from2->parallel_tuple_cost;
+        to->random_page_cost = from1->random_page_cost  + from2->random_page_cost;
+        to->seq_page_cost = from1->seq_page_cost  + from2->seq_page_cost;
+        to->disable_cost = from1->disable_cost  + from2->disable_cost;
+}
+
+void cost_set_sum3(Cost *to, const Cost *from1, const Cost *from2, const Cost *from3)
+{
+        to->cpu_index_tuple_cost =
+		from1->cpu_index_tuple_cost +
+		from2->cpu_index_tuple_cost +
+		from3->cpu_index_tuple_cost;
+        to->cpu_operator_cost =
+		from1->cpu_operator_cost +
+		from2->cpu_operator_cost +
+		from3->cpu_operator_cost;
+        to->cpu_tuple_cost =
+		from1->cpu_tuple_cost +
+		from2->cpu_tuple_cost +
+		from3->cpu_tuple_cost;
+        to->parallel_setup_cost =
+		from1->parallel_setup_cost +
+		from2->parallel_setup_cost +
+		from3->parallel_setup_cost;
+        to->parallel_tuple_cost =
+		from1->parallel_tuple_cost +
+		from2->parallel_tuple_cost +
+		from3->parallel_tuple_cost;
+        to->random_page_cost =
+		from1->random_page_cost +
+		from2->random_page_cost +
+		from3->random_page_cost;
+        to->seq_page_cost =
+		from1->seq_page_cost +
+		from2->seq_page_cost +
+		from3->seq_page_cost;
+        to->disable_cost =
+		from1->disable_cost +
+		from2->disable_cost +
+		from3->disable_cost;
+}
+
+void cost_set_diff(Cost *to, const Cost *from1, const Cost *from2)
+{
+        to->cpu_index_tuple_cost = from1->cpu_index_tuple_cost - from2 ->cpu_index_tuple_cost;
+        to->cpu_operator_cost = from1->cpu_operator_cost - from2->cpu_operator_cost;
+        to->cpu_tuple_cost = from1->cpu_tuple_cost  - from2->cpu_tuple_cost;
+        to->parallel_setup_cost = from1->parallel_setup_cost  - from2->parallel_setup_cost;
+        to->parallel_tuple_cost = from1->parallel_tuple_cost  - from2->parallel_tuple_cost;
+        to->random_page_cost = from1->random_page_cost  - from2->random_page_cost;
+        to->seq_page_cost = from1->seq_page_cost  - from2->seq_page_cost;
+        to->disable_cost = from1->disable_cost  - from2->disable_cost;
+}
+
+void cost_add(Cost *to, const Cost *from1)
+{
+        /* Hack to allow calling append_nonpartial_cost... */
+        if (!from1)
+		return;
+
+        to->cpu_index_tuple_cost += from1->cpu_index_tuple_cost;
+        to->cpu_operator_cost += from1->cpu_operator_cost;
+        to->cpu_tuple_cost += from1->cpu_tuple_cost;
+        to->parallel_setup_cost += from1->parallel_setup_cost;
+        to->parallel_tuple_cost += from1->parallel_tuple_cost;
+        to->random_page_cost += from1->random_page_cost;
+        to->seq_page_cost += from1->seq_page_cost;
+        to->disable_cost += from1->disable_cost;
+}
+
+void cost_add2(Cost *to, const Cost *from1, const Cost *from2)
+{
+        to->cpu_index_tuple_cost +=
+		from1->cpu_index_tuple_cost +
+		from2->cpu_index_tuple_cost;
+        to->cpu_operator_cost +=
+		from1->cpu_operator_cost +
+		from2->cpu_operator_cost;
+        to->cpu_tuple_cost +=
+		from1->cpu_tuple_cost +
+		from2->cpu_tuple_cost;
+        to->parallel_setup_cost +=
+		from1->parallel_setup_cost +
+		from2->parallel_setup_cost;
+        to->parallel_tuple_cost +=
+		from1->parallel_tuple_cost +
+		from2->parallel_tuple_cost;
+        to->random_page_cost +=
+		from1->random_page_cost +
+		from2->random_page_cost;
+        to->seq_page_cost +=
+		from1->seq_page_cost +
+		from2->seq_page_cost;
+        to->disable_cost +=
+		from1->disable_cost +
+		from2->disable_cost;
+}
+
+void cost_add_mul(Cost *to, const Cost *from1, double mul)
+{
+        to->cpu_index_tuple_cost +=
+		mul * from1->cpu_index_tuple_cost;
+        to->cpu_operator_cost +=
+		mul * from1->cpu_operator_cost;
+        to->cpu_tuple_cost +=
+		mul * from1->cpu_tuple_cost;
+        to->parallel_setup_cost +=
+		mul * from1->parallel_setup_cost;
+        to->parallel_tuple_cost +=
+		mul * from1->parallel_tuple_cost;
+        to->random_page_cost +=
+		mul * from1->random_page_cost;
+        to->seq_page_cost +=
+		mul * from1->seq_page_cost;
+        to->disable_cost +=
+		mul * from1->disable_cost;
+}
+
+void cost_sub(Cost *to, const Cost *from1)
+{
+        to->cpu_index_tuple_cost -= from1->cpu_index_tuple_cost;
+        to->cpu_operator_cost -= from1->cpu_operator_cost;
+        to->cpu_tuple_cost -= from1->cpu_tuple_cost;
+        to->parallel_setup_cost -= from1->parallel_setup_cost;
+        to->parallel_tuple_cost -= from1->parallel_tuple_cost;
+        to->random_page_cost -= from1->random_page_cost;
+        to->seq_page_cost -= from1->seq_page_cost;
+        to->disable_cost -= from1->disable_cost;
+}
+
+void cost_mul_scalar(Cost *to, double mul)
+{
+        to->cpu_index_tuple_cost *= mul;
+        to->cpu_operator_cost *= mul;
+        to->cpu_tuple_cost *= mul;
+        to->parallel_setup_cost *= mul;
+        to->parallel_tuple_cost *= mul;
+        to->random_page_cost *= mul;
+        to->seq_page_cost *= mul;
+        to->disable_cost *= mul;
+}
+
+double cost_asscalar(const Cost *to)
+{
+	return to->cpu_index_tuple_cost 
+		+ to->cpu_operator_cost 
+		+ to->cpu_tuple_cost 
+		+ to->parallel_setup_cost 
+		+ to->parallel_tuple_cost 
+		+ to->random_page_cost 
+		+ to->seq_page_cost 
+		+ to->disable_cost;
+}
+
+bool cost_isgt(const Cost *to, const Cost *than)
+{
+	return cost_asscalar(to) > cost_asscalar(than);
+}
+
+bool cost_islt(const Cost *to, const Cost *than)
+{
+	return cost_asscalar(to) < cost_asscalar(than);
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 37b257c..1277dec 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -1372,7 +1372,7 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths)
 	PathClauseUsage *pathinfo;
 	List	   *clauselist;
 	List	   *bestpaths = NIL;
-	Cost		bestcost = 0;
+	Cost		bestcost = {0};
 	int			i,
 				j;
 	ListCell   *l;
@@ -1469,7 +1469,7 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths)
 
 			cost_bitmap_tree_node(pathinfo->path, &ncost, &nselec);
 			cost_bitmap_tree_node(pathinfoarray[i]->path, &ocost, &oselec);
-			if (ncost < ocost)
+			if (cost_islt(&ncost, &ocost))
 				pathinfoarray[i] = pathinfo;
 		}
 		else
@@ -1537,7 +1537,7 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths)
 			/* tentatively add new path to paths, so we can estimate cost */
 			paths = lappend(paths, pathinfo->path);
 			newcost = bitmap_and_cost_est(root, rel, paths);
-			if (newcost < costsofar)
+			if (cost_islt(&newcost, &costsofar))
 			{
 				/* keep new path in paths, update subsidiary variables */
 				costsofar = newcost;
@@ -1554,7 +1554,7 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths)
 		}
 
 		/* Keep the cheapest AND-group (or singleton) */
-		if (i == 0 || costsofar < bestcost)
+		if (i == 0 || cost_islt(&costsofar, &bestcost))
 		{
 			bestpaths = paths;
 			bestcost = costsofar;
@@ -1586,9 +1586,9 @@ path_usage_comparator(const void *a, const void *b)
 	/*
 	 * If costs are the same, sort by selectivity.
 	 */
-	if (acost < bcost)
+	if (cost_islt(&acost, &bcost))
 		return -1;
-	if (acost > bcost)
+	if (cost_isgt(&acost, &bcost))
 		return 1;
 
 	if (aselec < bselec)
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index aee81bd..561151d 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -3211,7 +3211,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
 											  iscan->indexqual,
 											  iscan->indexqualorig);
 		/* and set its cost/width fields appropriately */
-		plan->startup_cost = 0.0;
+		cost_zero(&plan->startup_cost);
 		plan->total_cost = ipath->indextotalcost;
 		plan->plan_rows =
 			clamp_row_est(ipath->indexselectivity * ipath->path.parent->tuples);
@@ -4197,7 +4197,7 @@ create_mergejoin_plan(PlannerInfo *root,
 		 * sync with final_cost_mergejoin.)
 		 */
 		copy_plan_costsize(matplan, inner_plan);
-		matplan->total_cost += cpu_operator_cost * matplan->plan_rows;
+		cost_add_member_mul(&matplan->total_cost, cpu_operator_cost, matplan->plan_rows);
 
 		inner_plan = matplan;
 	}
@@ -4994,7 +4994,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
 			 * security level, which is not so great, but we can alleviate
 			 * that risk by applying the cost limit cutoff.
 			 */
-			if (rinfo->leakproof && items[i].cost < 10 * cpu_operator_cost)
+			if (rinfo->leakproof && cost_asscalar(&items[i].cost) < 10 * cpu_operator_cost)
 				items[i].security_level = 0;
 			else
 				items[i].security_level = rinfo->security_level;
@@ -5021,7 +5021,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
 
 			if (newitem.security_level > olditem->security_level ||
 				(newitem.security_level == olditem->security_level &&
-				 newitem.cost >= olditem->cost))
+				 !cost_islt(&newitem.cost, &olditem->cost)))
 				break;
 			items[j] = *olditem;
 		}
@@ -5083,12 +5083,13 @@ label_sort_with_costsize(PlannerInfo *root, Sort *plan, double limit_tuples)
 {
 	Plan	   *lefttree = plan->plan.lefttree;
 	Path		sort_path;		/* dummy for result of cost_sort */
+	Cost		zero = {0};
 
 	cost_sort(&sort_path, root, NIL,
-			  lefttree->total_cost,
+			  &lefttree->total_cost,
 			  lefttree->plan_rows,
 			  lefttree->plan_width,
-			  0.0,
+			  &zero,
 			  work_mem,
 			  limit_tuples);
 	plan->plan.startup_cost = sort_path.startup_cost;
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index 974f620..0fd3897 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -314,7 +314,7 @@ find_minmax_aggs_walker(Node *node, List **context)
 		mminfo->target = curTarget->expr;
 		mminfo->subroot = NULL; /* don't compute path yet */
 		mminfo->path = NULL;
-		mminfo->pathcost = 0;
+		cost_zero(&mminfo->pathcost);
 		mminfo->param = NULL;
 
 		*context = lappend(*context, mminfo);
@@ -484,8 +484,9 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
 	 * Note: cost calculation here should match
 	 * compare_fractional_path_costs().
 	 */
-	path_cost = sorted_path->startup_cost +
-		path_fraction * (sorted_path->total_cost - sorted_path->startup_cost);
+	cost_set_diff(&path_cost, &sorted_path->total_cost, &sorted_path->startup_cost);
+	cost_mul_scalar(&path_cost, path_fraction);
+	cost_add(&path_cost, &sorted_path->startup_cost);
 
 	/* Save state for further processing */
 	mminfo->subroot = subroot;
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 7fe11b5..1280388 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -455,10 +455,11 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
 		 * Ideally we'd use cost_gather here, but setting up dummy path data
 		 * to satisfy it doesn't seem much cleaner than knowing what it does.
 		 */
-		gather->plan.startup_cost = top_plan->startup_cost +
-			parallel_setup_cost;
-		gather->plan.total_cost = top_plan->total_cost +
-			parallel_setup_cost + parallel_tuple_cost * top_plan->plan_rows;
+		cost_set_member_add(&gather->plan.startup_cost, parallel_setup_cost, &top_plan->startup_cost);
+
+		cost_set_member_mul(&gather->plan.total_cost, parallel_tuple_cost, top_plan->plan_rows);
+		cost_add_member(&gather->plan.total_cost, parallel_setup_cost);
+		cost_add(&gather->plan.total_cost, &top_plan->total_cost);
 		gather->plan.plan_rows = top_plan->plan_rows;
 		gather->plan.plan_width = top_plan->plan_width;
 		gather->plan.parallel_aware = false;
@@ -533,7 +534,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
 
 	result->jitFlags = PGJIT_NONE;
 	if (jit_enabled && jit_above_cost >= 0 &&
-		top_plan->total_cost > jit_above_cost)
+		cost_asscalar(&top_plan->total_cost) > jit_above_cost)
 	{
 		result->jitFlags |= PGJIT_PERFORM;
 
@@ -541,10 +542,10 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
 		 * Decide how much effort should be put into generating better code.
 		 */
 		if (jit_optimize_above_cost >= 0 &&
-			top_plan->total_cost > jit_optimize_above_cost)
+			cost_asscalar(&top_plan->total_cost) > jit_optimize_above_cost)
 			result->jitFlags |= PGJIT_OPT3;
 		if (jit_inline_above_cost >= 0 &&
-			top_plan->total_cost > jit_inline_above_cost)
+			cost_asscalar(&top_plan->total_cost) > jit_inline_above_cost)
 			result->jitFlags |= PGJIT_INLINE;
 
 		/*
@@ -5790,7 +5791,7 @@ make_sort_input_target(PlannerInfo *root,
 				 * cpu_operator_cost".  Note this will take in any PL function
 				 * with default cost.
 				 */
-				if (cost.per_tuple > 10 * cpu_operator_cost)
+				if (cost_asscalar(&cost.per_tuple) > 10 * cpu_operator_cost)
 				{
 					postpone_col[i] = true;
 					have_expensive = true;
@@ -6206,13 +6207,14 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid)
 	 * expressions each time.  (XXX that's pretty inefficient...)
 	 */
 	cost_qual_eval(&indexExprCost, indexInfo->indexprs, root);
-	comparisonCost = 2.0 * (indexExprCost.startup + indexExprCost.per_tuple);
+	cost_set_sum2(&comparisonCost, &indexExprCost.startup, &indexExprCost.per_tuple);
+	cost_mul_scalar(&comparisonCost, 2);
 
 	/* Estimate the cost of seq scan + sort */
 	seqScanPath = create_seqscan_path(root, rel, NULL, 0);
 	cost_sort(&seqScanAndSortPath, root, NIL,
-			  seqScanPath->total_cost, rel->tuples, rel->reltarget->width,
-			  comparisonCost, maintenance_work_mem, -1.0);
+			  &seqScanPath->total_cost, rel->tuples, rel->reltarget->width,
+			  &comparisonCost, maintenance_work_mem, -1.0);
 
 	/* Estimate the cost of index scan */
 	indexScanPath = create_index_path(root, indexInfo,
@@ -6220,7 +6222,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid)
 									  ForwardScanDirection, false,
 									  NULL, 1.0, false);
 
-	return (seqScanAndSortPath.total_cost < indexScanPath->path.total_cost);
+	return cost_islt(&seqScanAndSortPath.total_cost, &indexScanPath->path.total_cost);
 }
 
 /*
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 48b62a5..5780534 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -2060,7 +2060,7 @@ SS_identify_outer_params(PlannerInfo *root)
 void
 SS_charge_for_initplans(PlannerInfo *root, RelOptInfo *final_rel)
 {
-	Cost		initplan_cost;
+	Cost		initplan_cost = {0};
 	ListCell   *lc;
 
 	/* Nothing to do if no initPlans */
@@ -2073,12 +2073,11 @@ SS_charge_for_initplans(PlannerInfo *root, RelOptInfo *final_rel)
 	 * This is a conservative overestimate, since in fact an initPlan might be
 	 * executed later than plan startup, or even not at all.
 	 */
-	initplan_cost = 0;
 	foreach(lc, root->init_plans)
 	{
 		SubPlan    *initsubplan = (SubPlan *) lfirst(lc);
 
-		initplan_cost += initsubplan->startup_cost + initsubplan->per_call_cost;
+		cost_add2(&initplan_cost, &initsubplan->startup_cost, &initsubplan->per_call_cost);
 	}
 
 	/*
@@ -2088,8 +2087,8 @@ SS_charge_for_initplans(PlannerInfo *root, RelOptInfo *final_rel)
 	{
 		Path	   *path = (Path *) lfirst(lc);
 
-		path->startup_cost += initplan_cost;
-		path->total_cost += initplan_cost;
+		cost_add(&path->startup_cost, &initplan_cost);
+		cost_add(&path->total_cost, &initplan_cost);
 		path->parallel_safe = false;
 	}
 
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index b01c9bb..a9bf448 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1025,6 +1025,7 @@ choose_hashed_setop(PlannerInfo *root, List *groupClauses,
 	Path		hashed_p;
 	Path		sorted_p;
 	double		tuple_fraction;
+	Cost 		zerocost = {0};
 
 	/* Check whether the operators support sorting or hashing */
 	can_sort = grouping_is_sortable(groupClauses);
@@ -1071,7 +1072,7 @@ choose_hashed_setop(PlannerInfo *root, List *groupClauses,
 	cost_agg(&hashed_p, root, AGG_HASHED, NULL,
 			 numGroupCols, dNumGroups,
 			 NIL,
-			 input_path->startup_cost, input_path->total_cost,
+			 &input_path->startup_cost, &input_path->total_cost,
 			 input_path->rows);
 
 	/*
@@ -1081,9 +1082,9 @@ choose_hashed_setop(PlannerInfo *root, List *groupClauses,
 	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.total_cost,
 			  input_path->rows, input_path->pathtarget->width,
-			  0.0, work_mem, -1.0);
+			  &zerocost, work_mem, -1.0);
 	cost_group(&sorted_p, root, numGroupCols, dNumGroups,
 			   NIL,
 			   sorted_p.startup_cost, sorted_p.total_cost,
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 1890f25..099bb43 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -18,6 +18,7 @@
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/appendinfo.h"
+#include "optimizer/cost.h"
 #include "parser/parsetree.h"
 #include "utils/lsyscache.h"
 #include "utils/rel.h"
@@ -461,7 +462,7 @@ adjust_appendrel_attrs_mutator(Node *node,
 		 * don't reset left_ec/right_ec: each child variable is implicitly
 		 * equivalent to its parent, so still a member of the same EC if any.
 		 */
-		newinfo->eval_cost.startup = -1;
+		cost_zero(&newinfo->eval_cost.startup); // XXX
 		newinfo->norm_selec = -1;
 		newinfo->outer_selec = -1;
 		newinfo->left_em = NULL;
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index a04b622..9668762 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -370,8 +370,8 @@ get_agg_clause_costs_walker(Node *node, get_agg_clause_costs_context *context)
 		{
 			/* add the input expressions' cost to per-input-row costs */
 			cost_qual_eval_node(&argcosts, (Node *) aggref->args, context->root);
-			costs->transCost.startup += argcosts.startup;
-			costs->transCost.per_tuple += argcosts.per_tuple;
+			cost_add(&costs->transCost.startup, &argcosts.startup);
+			cost_add(&costs->transCost.per_tuple, &argcosts.per_tuple);
 
 			/*
 			 * Add any filter's cost to per-input-row costs.
@@ -384,8 +384,8 @@ get_agg_clause_costs_walker(Node *node, get_agg_clause_costs_context *context)
 			{
 				cost_qual_eval_node(&argcosts, (Node *) aggref->aggfilter,
 									context->root);
-				costs->transCost.startup += argcosts.startup;
-				costs->transCost.per_tuple += argcosts.per_tuple;
+				cost_add(&costs->transCost.startup, &argcosts.startup);
+				cost_add(&costs->transCost.per_tuple, &argcosts.per_tuple);
 			}
 		}
 
@@ -397,8 +397,8 @@ get_agg_clause_costs_walker(Node *node, get_agg_clause_costs_context *context)
 		{
 			cost_qual_eval_node(&argcosts, (Node *) aggref->aggdirectargs,
 								context->root);
-			costs->finalCost.startup += argcosts.startup;
-			costs->finalCost.per_tuple += argcosts.per_tuple;
+			cost_add(&costs->finalCost.startup, &argcosts.startup);
+			cost_add(&costs->finalCost.per_tuple, &argcosts.per_tuple);
 		}
 
 		/*
@@ -4631,7 +4631,7 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid,
 			if (contain_subplans(param))
 				goto fail;
 			cost_qual_eval(&eval_cost, list_make1(param), NULL);
-			if (eval_cost.startup + eval_cost.per_tuple >
+			if (cost_asscalar(&eval_cost.startup) + cost_asscalar(&eval_cost.per_tuple) >
 				10 * cpu_operator_cost)
 				goto fail;
 
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 60c93ee..6026f62 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -58,6 +58,8 @@ static List *reparameterize_pathlist_by_child(PlannerInfo *root,
 											  RelOptInfo *child_rel);
 
 
+static Cost zerocost = {0};
+
 /*****************************************************************************
  *		MISC. PATH UTILITIES
  *****************************************************************************/
@@ -72,33 +74,33 @@ compare_path_costs(Path *path1, Path *path2, CostSelector criterion)
 {
 	if (criterion == STARTUP_COST)
 	{
-		if (path1->startup_cost < path2->startup_cost)
+		if (cost_asscalar(&path1->startup_cost) < cost_asscalar(&path2->startup_cost))
 			return -1;
-		if (path1->startup_cost > path2->startup_cost)
+		if (cost_asscalar(&path1->startup_cost) > cost_asscalar(&path2->startup_cost))
 			return +1;
 
 		/*
 		 * If paths have the same startup cost (not at all unlikely), order
 		 * them by total cost.
 		 */
-		if (path1->total_cost < path2->total_cost)
+		if (cost_asscalar(&path1->total_cost) < cost_asscalar(&path2->total_cost))
 			return -1;
-		if (path1->total_cost > path2->total_cost)
+		if (cost_asscalar(&path1->total_cost) > cost_asscalar(&path2->total_cost))
 			return +1;
 	}
 	else
 	{
-		if (path1->total_cost < path2->total_cost)
+		if (cost_asscalar(&path1->total_cost) < cost_asscalar(&path2->total_cost))
 			return -1;
-		if (path1->total_cost > path2->total_cost)
+		if (cost_asscalar(&path1->total_cost) > cost_asscalar(&path2->total_cost))
 			return +1;
 
 		/*
 		 * If paths have the same total cost, order them by startup cost.
 		 */
-		if (path1->startup_cost < path2->startup_cost)
+		if (cost_asscalar(&path1->startup_cost) < cost_asscalar(&path2->startup_cost))
 			return -1;
-		if (path1->startup_cost > path2->startup_cost)
+		if (cost_asscalar(&path1->startup_cost) > cost_asscalar(&path2->startup_cost))
 			return +1;
 	}
 	return 0;
@@ -122,13 +124,17 @@ compare_fractional_path_costs(Path *path1, Path *path2,
 
 	if (fraction <= 0.0 || fraction >= 1.0)
 		return compare_path_costs(path1, path2, TOTAL_COST);
-	cost1 = path1->startup_cost +
-		fraction * (path1->total_cost - path1->startup_cost);
-	cost2 = path2->startup_cost +
-		fraction * (path2->total_cost - path2->startup_cost);
-	if (cost1 < cost2)
+	cost_set_diff(&cost1, &path1->total_cost, &path1->startup_cost);
+	cost_mul_scalar(&cost1, fraction);
+	cost_add(&cost1, &path1->startup_cost);
+
+	cost_set_diff(&cost2, &path2->total_cost, &path2->startup_cost);
+	cost_mul_scalar(&cost2, fraction);
+	cost_add(&cost2, &path2->startup_cost);
+
+	if (cost_asscalar(&cost1) < cost_asscalar(&cost2))
 		return -1;
-	if (cost1 > cost2)
+	if (cost_asscalar(&cost1) > cost_asscalar(&cost2))
 		return +1;
 	return 0;
 }
@@ -172,11 +178,11 @@ compare_path_costs_fuzzily(Path *path1, Path *path2, double fuzz_factor)
 	 * Check total cost first since it's more likely to be different; many
 	 * paths have zero startup cost.
 	 */
-	if (path1->total_cost > path2->total_cost * fuzz_factor)
+	if (cost_asscalar(&path1->total_cost) > cost_asscalar(&path2->total_cost) * fuzz_factor)
 	{
 		/* path1 fuzzily worse on total cost */
 		if (CONSIDER_PATH_STARTUP_COST(path1) &&
-			path2->startup_cost > path1->startup_cost * fuzz_factor)
+			cost_asscalar(&path2->startup_cost) > cost_asscalar(&path1->startup_cost) * fuzz_factor)
 		{
 			/* ... but path2 fuzzily worse on startup, so DIFFERENT */
 			return COSTS_DIFFERENT;
@@ -184,11 +190,11 @@ compare_path_costs_fuzzily(Path *path1, Path *path2, double fuzz_factor)
 		/* else path2 dominates */
 		return COSTS_BETTER2;
 	}
-	if (path2->total_cost > path1->total_cost * fuzz_factor)
+	if (cost_asscalar(&path2->total_cost) > cost_asscalar(&path1->total_cost) * fuzz_factor)
 	{
 		/* path2 fuzzily worse on total cost */
 		if (CONSIDER_PATH_STARTUP_COST(path2) &&
-			path1->startup_cost > path2->startup_cost * fuzz_factor)
+			cost_asscalar(&path1->startup_cost) > cost_asscalar(&path2->startup_cost) * fuzz_factor)
 		{
 			/* ... but path1 fuzzily worse on startup, so DIFFERENT */
 			return COSTS_DIFFERENT;
@@ -197,12 +203,12 @@ compare_path_costs_fuzzily(Path *path1, Path *path2, double fuzz_factor)
 		return COSTS_BETTER1;
 	}
 	/* fuzzily the same on total cost ... */
-	if (path1->startup_cost > path2->startup_cost * fuzz_factor)
+	if (cost_asscalar(&path1->startup_cost) > cost_asscalar(&path2->startup_cost) * fuzz_factor)
 	{
 		/* ... but path1 fuzzily worse on startup, so path2 wins */
 		return COSTS_BETTER2;
 	}
-	if (path2->startup_cost > path1->startup_cost * fuzz_factor)
+	if (cost_asscalar(&path2->startup_cost) > cost_asscalar(&path1->startup_cost) * fuzz_factor)
 	{
 		/* ... but path2 fuzzily worse on startup, so path1 wins */
 		return COSTS_BETTER1;
@@ -596,7 +602,7 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
 		else
 		{
 			/* new belongs after this old path if it has cost >= old's */
-			if (new_path->total_cost >= old_path->total_cost)
+			if (cost_asscalar(&new_path->total_cost) >= cost_asscalar(&old_path->total_cost))
 				insert_at = foreach_current_index(p1) + 1;
 		}
 
@@ -668,10 +674,10 @@ add_path_precheck(RelOptInfo *parent_rel,
 		 *
 		 * Cost comparisons here should match compare_path_costs_fuzzily.
 		 */
-		if (total_cost > old_path->total_cost * STD_FUZZ_FACTOR)
+		if (cost_asscalar(&total_cost) > cost_asscalar(&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 ||
+			if (cost_asscalar(&startup_cost) > cost_asscalar(&old_path->startup_cost) * STD_FUZZ_FACTOR ||
 				!consider_startup)
 			{
 				/* new path loses on cost, so check pathkeys... */
@@ -777,13 +783,13 @@ add_partial_path(RelOptInfo *parent_rel, Path *new_path)
 		/* 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 (cost_asscalar(&new_path->total_cost) > cost_asscalar(&old_path->total_cost) * STD_FUZZ_FACTOR)
 			{
 				/* New path costs more; keep it only if pathkeys are better. */
 				if (keyscmp != PATHKEYS_BETTER1)
 					accept_new = false;
 			}
-			else if (old_path->total_cost > new_path->total_cost
+			else if (cost_asscalar(&old_path->total_cost) > cost_asscalar(&new_path->total_cost)
 					 * STD_FUZZ_FACTOR)
 			{
 				/* Old path costs more; keep it only if pathkeys are better. */
@@ -800,7 +806,7 @@ add_partial_path(RelOptInfo *parent_rel, Path *new_path)
 				/* Costs are about the same, old path has better pathkeys. */
 				accept_new = false;
 			}
-			else if (old_path->total_cost > new_path->total_cost * 1.0000000001)
+			else if (cost_asscalar(&old_path->total_cost) > cost_asscalar(&new_path->total_cost) * 1.0000000001)
 			{
 				/* Pathkeys are the same, and the old path costs more. */
 				remove_old = true;
@@ -827,7 +833,7 @@ add_partial_path(RelOptInfo *parent_rel, Path *new_path)
 		else
 		{
 			/* new belongs after this old path if it has cost >= old's */
-			if (new_path->total_cost >= old_path->total_cost)
+			if (cost_asscalar(&new_path->total_cost) >= cost_asscalar(&old_path->total_cost))
 				insert_at = foreach_current_index(p1) + 1;
 		}
 
@@ -888,10 +894,10 @@ add_partial_path_precheck(RelOptInfo *parent_rel, Cost total_cost,
 		keyscmp = compare_pathkeys(pathkeys, old_path->pathkeys);
 		if (keyscmp != PATHKEYS_DIFFERENT)
 		{
-			if (total_cost > old_path->total_cost * STD_FUZZ_FACTOR &&
+			if (cost_asscalar(&total_cost) > cost_asscalar(&old_path->total_cost) * STD_FUZZ_FACTOR &&
 				keyscmp != PATHKEYS_BETTER1)
 				return false;
-			if (old_path->total_cost > total_cost * STD_FUZZ_FACTOR &&
+			if (cost_asscalar(&old_path->total_cost) > cost_asscalar(&total_cost) * STD_FUZZ_FACTOR &&
 				keyscmp != PATHKEYS_BETTER2)
 				return true;
 		}
@@ -1350,8 +1356,8 @@ create_merge_append_path(PlannerInfo *root,
 						 List *partitioned_rels)
 {
 	MergeAppendPath *pathnode = makeNode(MergeAppendPath);
-	Cost		input_startup_cost;
-	Cost		input_total_cost;
+	Cost		input_startup_cost = {0};
+	Cost		input_total_cost = {0};
 	ListCell   *l;
 
 	pathnode->path.pathtype = T_MergeAppend;
@@ -1379,8 +1385,6 @@ create_merge_append_path(PlannerInfo *root,
 	 * Add up the sizes and costs of the input paths.
 	 */
 	pathnode->path.rows = 0;
-	input_startup_cost = 0;
-	input_total_cost = 0;
 	foreach(l, subpaths)
 	{
 		Path	   *subpath = (Path *) lfirst(l);
@@ -1392,8 +1396,8 @@ create_merge_append_path(PlannerInfo *root,
 		if (pathkeys_contained_in(pathkeys, subpath->pathkeys))
 		{
 			/* Subpath is adequately ordered, we won't need to sort it */
-			input_startup_cost += subpath->startup_cost;
-			input_total_cost += subpath->total_cost;
+			cost_add(&input_startup_cost, &subpath->startup_cost);
+			cost_add(&input_total_cost, &subpath->total_cost);
 		}
 		else
 		{
@@ -1403,14 +1407,14 @@ create_merge_append_path(PlannerInfo *root,
 			cost_sort(&sort_path,
 					  root,
 					  pathkeys,
-					  subpath->total_cost,
+					  &subpath->total_cost,
 					  subpath->parent->tuples,
 					  subpath->pathtarget->width,
-					  0.0,
+					  &zerocost,
 					  work_mem,
 					  pathnode->limit_tuples);
-			input_startup_cost += sort_path.startup_cost;
-			input_total_cost += sort_path.total_cost;
+			cost_add(&input_startup_cost, &sort_path.startup_cost);
+			cost_add(&input_total_cost, &sort_path.total_cost);
 		}
 
 		/* All child paths must have same parameterization */
@@ -1466,8 +1470,9 @@ create_group_result_path(PlannerInfo *root, RelOptInfo *rel,
 	 */
 	pathnode->path.rows = 1;
 	pathnode->path.startup_cost = target->cost.startup;
-	pathnode->path.total_cost = target->cost.startup +
-		cpu_tuple_cost + target->cost.per_tuple;
+	cost_set_member(&pathnode->path.total_cost, cpu_tuple_cost);
+	cost_add2(&pathnode->path.total_cost, &target->cost.startup,
+		&target->cost.per_tuple);
 
 	/*
 	 * Add cost of qual, if any --- but we ignore its selectivity, since our
@@ -1479,8 +1484,8 @@ create_group_result_path(PlannerInfo *root, RelOptInfo *rel,
 
 		cost_qual_eval(&qual_cost, havingqual, root);
 		/* havingqual is evaluated once at startup */
-		pathnode->path.startup_cost += qual_cost.startup + qual_cost.per_tuple;
-		pathnode->path.total_cost += qual_cost.startup + qual_cost.per_tuple;
+		cost_add2(&pathnode->path.startup_cost, &qual_cost.startup, &qual_cost.per_tuple);
+		cost_add2(&pathnode->path.total_cost, &qual_cost.startup, &qual_cost.per_tuple);
 	}
 
 	return pathnode;
@@ -1665,10 +1670,10 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
 		 * Estimate cost for sort+unique implementation
 		 */
 		cost_sort(&sort_path, root, NIL,
-				  subpath->total_cost,
+				  &subpath->total_cost,
 				  rel->rows,
 				  subpath->pathtarget->width,
-				  0.0,
+				  &zerocost,
 				  work_mem,
 				  -1.0);
 
@@ -1678,7 +1683,7 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
 		 * probably this is an overestimate.)  This should agree with
 		 * create_upper_unique_path.
 		 */
-		sort_path.total_cost += cpu_operator_cost * rel->rows * numCols;
+		cost_add_member_mul(&sort_path.total_cost, cpu_operator_cost, rel->rows * numCols);
 	}
 
 	if (sjinfo->semi_can_hash)
@@ -1702,14 +1707,14 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
 					 AGG_HASHED, NULL,
 					 numCols, pathnode->path.rows,
 					 NIL,
-					 subpath->startup_cost,
-					 subpath->total_cost,
+					 &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 (cost_asscalar(&agg_path.total_cost) < cost_asscalar(&sort_path.total_cost))
 			pathnode->umethod = UNIQUE_PATH_HASH;
 		else
 			pathnode->umethod = UNIQUE_PATH_SORT;
@@ -1755,8 +1760,8 @@ create_gather_merge_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
 						 Relids required_outer, double *rows)
 {
 	GatherMergePath *pathnode = makeNode(GatherMergePath);
-	Cost		input_startup_cost = 0;
-	Cost		input_total_cost = 0;
+	Cost		input_startup_cost = {0};
+	Cost		input_total_cost = {0};
 
 	Assert(subpath->parallel_safe);
 	Assert(pathkeys);
@@ -1776,8 +1781,8 @@ create_gather_merge_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
 	if (pathkeys_contained_in(pathkeys, subpath->pathkeys))
 	{
 		/* Subpath is adequately ordered, we won't need to sort it */
-		input_startup_cost += subpath->startup_cost;
-		input_total_cost += subpath->total_cost;
+		cost_add(&input_startup_cost, &subpath->startup_cost);
+		cost_add(&input_total_cost, &subpath->total_cost);
 	}
 	else
 	{
@@ -1787,14 +1792,14 @@ create_gather_merge_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
 		cost_sort(&sort_path,
 				  root,
 				  pathkeys,
-				  subpath->total_cost,
+				  &subpath->total_cost,
 				  subpath->rows,
 				  subpath->pathtarget->width,
-				  0.0,
+				  &zerocost,
 				  work_mem,
 				  -1);
-		input_startup_cost += sort_path.startup_cost;
-		input_total_cost += sort_path.total_cost;
+		cost_add(&input_startup_cost, &sort_path.startup_cost);
+		cost_add(&input_total_cost, &sort_path.total_cost);
 	}
 
 	cost_gather_merge(pathnode, root, rel, pathnode->path.param_info,
@@ -2558,11 +2563,14 @@ create_projection_path(PlannerInfo *root,
 		 * Set cost of plan as subpath's cost, adjusted for tlist replacement.
 		 */
 		pathnode->path.rows = subpath->rows;
-		pathnode->path.startup_cost = subpath->startup_cost +
-			(target->cost.startup - oldtarget->cost.startup);
-		pathnode->path.total_cost = subpath->total_cost +
-			(target->cost.startup - oldtarget->cost.startup) +
-			(target->cost.per_tuple - oldtarget->cost.per_tuple) * subpath->rows;
+
+		cost_set_sum2(&pathnode->path.startup_cost, &subpath->startup_cost, &target->cost.startup);
+		cost_sub(&pathnode->path.startup_cost, &oldtarget->cost.startup);
+
+		cost_set_diff(&pathnode->path.total_cost, &target->cost.per_tuple, &oldtarget->cost.per_tuple);
+		cost_mul_scalar(&pathnode->path.total_cost, subpath->rows);
+		cost_add2(&pathnode->path.total_cost, &subpath->total_cost, &target->cost.startup);
+		cost_sub(&pathnode->path.total_cost, &oldtarget->cost.startup);
 	}
 	else
 	{
@@ -2574,11 +2582,12 @@ create_projection_path(PlannerInfo *root,
 		 * evaluating the tlist.  There is no qual to worry about.
 		 */
 		pathnode->path.rows = subpath->rows;
-		pathnode->path.startup_cost = subpath->startup_cost +
-			target->cost.startup;
-		pathnode->path.total_cost = subpath->total_cost +
-			target->cost.startup +
-			(cpu_tuple_cost + target->cost.per_tuple) * subpath->rows;
+
+		cost_set_sum2(&pathnode->path.startup_cost, &subpath->startup_cost, &target->cost.startup);
+
+		cost_set_member_add(&pathnode->path.total_cost, cpu_tuple_cost, &target->cost.per_tuple);
+		cost_mul_scalar(&pathnode->path.total_cost, subpath->rows);
+		cost_add2(&pathnode->path.total_cost, &subpath->total_cost, &target->cost.startup);
 	}
 
 	return pathnode;
@@ -2613,6 +2622,7 @@ apply_projection_to_path(PlannerInfo *root,
 						 PathTarget *target)
 {
 	QualCost	oldcost;
+	Cost		tmp;
 
 	/*
 	 * If given path can't project, we might need a Result node, so make a
@@ -2628,9 +2638,14 @@ apply_projection_to_path(PlannerInfo *root,
 	oldcost = path->pathtarget->cost;
 	path->pathtarget = target;
 
-	path->startup_cost += target->cost.startup - oldcost.startup;
-	path->total_cost += target->cost.startup - oldcost.startup +
-		(target->cost.per_tuple - oldcost.per_tuple) * path->rows;
+	cost_add(&path->startup_cost, &target->cost.startup);
+	cost_sub(&path->startup_cost, &oldcost.startup);
+
+	cost_set_diff(&tmp, &target->cost.per_tuple, &oldcost.per_tuple);
+	cost_mul_scalar(&tmp, path->rows);
+	cost_add(&path->total_cost, &target->cost.startup);
+	cost_sub(&path->total_cost, &oldcost.startup);
+	cost_add(&path->total_cost, &tmp);
 
 	/*
 	 * If the path happens to be a Gather or GatherMerge path, we'd like to
@@ -2704,6 +2719,7 @@ create_set_projection_path(PlannerInfo *root,
 	ProjectSetPath *pathnode = makeNode(ProjectSetPath);
 	double		tlist_rows;
 	ListCell   *lc;
+	Cost	tmp;
 
 	pathnode->path.pathtype = T_ProjectSet;
 	pathnode->path.parent = rel;
@@ -2742,12 +2758,15 @@ create_set_projection_path(PlannerInfo *root,
 	 * this estimate later.
 	 */
 	pathnode->path.rows = subpath->rows * tlist_rows;
-	pathnode->path.startup_cost = subpath->startup_cost +
-		target->cost.startup;
-	pathnode->path.total_cost = subpath->total_cost +
-		target->cost.startup +
-		(cpu_tuple_cost + target->cost.per_tuple) * subpath->rows +
-		(pathnode->path.rows - subpath->rows) * cpu_tuple_cost / 2;
+
+	cost_set_sum2(&pathnode->path.startup_cost, &subpath->startup_cost,
+		&target->cost.startup);
+	cost_set_sum2(&pathnode->path.total_cost, &subpath->total_cost, &target->cost.startup);
+
+	cost_set_member_add(&tmp, cpu_tuple_cost, &target->cost.per_tuple);
+	cost_add_mul(&pathnode->path.total_cost, &tmp, subpath->rows);
+
+	cost_add_member_mul(&pathnode->path.total_cost, cpu_tuple_cost, 0.5 * (pathnode->path.rows - subpath->rows) );
 
 	return pathnode;
 }
@@ -2786,10 +2805,10 @@ create_sort_path(PlannerInfo *root,
 	pathnode->subpath = subpath;
 
 	cost_sort(&pathnode->path, root, pathkeys,
-			  subpath->total_cost,
+			  &subpath->total_cost,
 			  subpath->rows,
 			  subpath->pathtarget->width,
-			  0.0,				/* XXX comparison_cost shouldn't be 0? */
+			  &zerocost,				/* XXX comparison_cost shouldn't be 0? */
 			  work_mem, limit_tuples);
 
 	return pathnode;
@@ -2842,9 +2861,9 @@ create_group_path(PlannerInfo *root,
 			   subpath->rows);
 
 	/* add tlist eval cost for each output row */
-	pathnode->path.startup_cost += target->cost.startup;
-	pathnode->path.total_cost += target->cost.startup +
-		target->cost.per_tuple * pathnode->path.rows;
+	cost_add(&pathnode->path.startup_cost, &target->cost.startup);
+	cost_add(&pathnode->path.total_cost, &target->cost.startup);
+	cost_add_mul(&pathnode->path.total_cost, &target->cost.per_tuple, pathnode->path.rows);
 
 	return pathnode;
 }
@@ -2896,8 +2915,8 @@ create_upper_unique_path(PlannerInfo *root,
 	 * an overestimate.)
 	 */
 	pathnode->path.startup_cost = subpath->startup_cost;
-	pathnode->path.total_cost = subpath->total_cost +
-		cpu_operator_cost * subpath->rows * numCols;
+	pathnode->path.total_cost = subpath->total_cost;
+	cost_add_member_mul(&pathnode->path.total_cost, cpu_operator_cost, subpath->rows * numCols);
 	pathnode->path.rows = numGroups;
 
 	return pathnode;
@@ -2956,13 +2975,13 @@ create_agg_path(PlannerInfo *root,
 			 aggstrategy, aggcosts,
 			 list_length(groupClause), numGroups,
 			 qual,
-			 subpath->startup_cost, subpath->total_cost,
+			 &subpath->startup_cost, &subpath->total_cost,
 			 subpath->rows);
 
 	/* add tlist eval cost for each output row */
-	pathnode->path.startup_cost += target->cost.startup;
-	pathnode->path.total_cost += target->cost.startup +
-		target->cost.per_tuple * pathnode->path.rows;
+	cost_add(&pathnode->path.startup_cost, &target->cost.startup);
+	cost_add(&pathnode->path.total_cost, &target->cost.startup);
+	cost_add_mul(&pathnode->path.total_cost, &target->cost.per_tuple, pathnode->path.rows);
 
 	return pathnode;
 }
@@ -3065,8 +3084,8 @@ create_groupingsets_path(PlannerInfo *root,
 					 numGroupCols,
 					 rollup->numGroups,
 					 having_qual,
-					 subpath->startup_cost,
-					 subpath->total_cost,
+					 &subpath->startup_cost,
+					 &subpath->total_cost,
 					 subpath->rows);
 			is_first = false;
 			if (!rollup->is_hashed)
@@ -3089,7 +3108,7 @@ create_groupingsets_path(PlannerInfo *root,
 						 numGroupCols,
 						 rollup->numGroups,
 						 having_qual,
-						 0.0, 0.0,
+						 &zerocost, &zerocost,
 						 subpath->rows);
 				if (!rollup->is_hashed)
 					is_first_sort = false;
@@ -3098,10 +3117,10 @@ create_groupingsets_path(PlannerInfo *root,
 			{
 				/* Account for cost of sort, but don't charge input cost again */
 				cost_sort(&sort_path, root, NIL,
-						  0.0,
+						  &zerocost,
 						  subpath->rows,
 						  subpath->pathtarget->width,
-						  0.0,
+						  &zerocost,
 						  work_mem,
 						  -1.0);
 
@@ -3113,20 +3132,20 @@ create_groupingsets_path(PlannerInfo *root,
 						 numGroupCols,
 						 rollup->numGroups,
 						 having_qual,
-						 sort_path.startup_cost,
-						 sort_path.total_cost,
+						 &sort_path.startup_cost,
+						 &sort_path.total_cost,
 						 sort_path.rows);
 			}
 
-			pathnode->path.total_cost += agg_path.total_cost;
+			cost_add(&pathnode->path.total_cost, &agg_path.total_cost);
 			pathnode->path.rows += agg_path.rows;
 		}
 	}
 
 	/* add tlist eval cost for each output row */
-	pathnode->path.startup_cost += target->cost.startup;
-	pathnode->path.total_cost += target->cost.startup +
-		target->cost.per_tuple * pathnode->path.rows;
+	cost_add(&pathnode->path.startup_cost,  &target->cost.startup);
+	cost_add(&pathnode->path.total_cost, &target->cost.startup);
+	cost_add_mul(&pathnode->path.total_cost, &target->cost.per_tuple, pathnode->path.rows);
 
 	return pathnode;
 }
@@ -3169,18 +3188,18 @@ create_minmaxagg_path(PlannerInfo *root,
 	pathnode->quals = quals;
 
 	/* Calculate cost of all the initplans ... */
-	initplan_cost = 0;
+	cost_zero(&initplan_cost); // = zerocost?
 	foreach(lc, mmaggregates)
 	{
 		MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
 
-		initplan_cost += mminfo->pathcost;
+		cost_add(&initplan_cost, &mminfo->pathcost);
 	}
 
 	/* add tlist eval cost for each output row, plus cpu_tuple_cost */
-	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_set_sum2(&pathnode->path.startup_cost, &initplan_cost, &target->cost.startup);
+	cost_set_sum3(&pathnode->path.total_cost, &initplan_cost, &target->cost.startup, &target->cost.per_tuple);
+	cost_add_member(&pathnode->path.total_cost, cpu_tuple_cost);
 
 	/*
 	 * Add cost of qual, if any --- but we ignore its selectivity, since our
@@ -3191,8 +3210,8 @@ create_minmaxagg_path(PlannerInfo *root,
 		QualCost	qual_cost;
 
 		cost_qual_eval(&qual_cost, quals, root);
-		pathnode->path.startup_cost += qual_cost.startup;
-		pathnode->path.total_cost += qual_cost.startup + qual_cost.per_tuple;
+		cost_add(&pathnode->path.startup_cost, &qual_cost.startup);
+		cost_add2(&pathnode->path.total_cost, &qual_cost.startup, &qual_cost.per_tuple);
 	}
 
 	return pathnode;
@@ -3251,9 +3270,9 @@ create_windowagg_path(PlannerInfo *root,
 				   subpath->rows);
 
 	/* add tlist eval cost for each output row */
-	pathnode->path.startup_cost += target->cost.startup;
-	pathnode->path.total_cost += target->cost.startup +
-		target->cost.per_tuple * pathnode->path.rows;
+	cost_add(&pathnode->path.startup_cost, &target->cost.startup);
+	cost_add(&pathnode->path.total_cost, &target->cost.startup);
+	cost_add_mul(&pathnode->path.total_cost, &target->cost.per_tuple, pathnode->path.rows);
 
 	return pathnode;
 }
@@ -3314,8 +3333,8 @@ create_setop_path(PlannerInfo *root,
 	 * all columns get compared at most of the tuples.
 	 */
 	pathnode->path.startup_cost = subpath->startup_cost;
-	pathnode->path.total_cost = subpath->total_cost +
-		cpu_operator_cost * subpath->rows * list_length(distinctList);
+	pathnode->path.total_cost = subpath->total_cost;
+	cost_add_member_mul(&pathnode->path.total_cost, cpu_operator_cost, subpath->rows * list_length(distinctList));
 	pathnode->path.rows = outputRows;
 
 	return pathnode;
@@ -3413,8 +3432,8 @@ create_lockrows_path(PlannerInfo *root, RelOptInfo *rel,
 	 * cpu_tuple_cost per row.
 	 */
 	pathnode->path.startup_cost = subpath->startup_cost;
-	pathnode->path.total_cost = subpath->total_cost +
-		cpu_tuple_cost * subpath->rows;
+	pathnode->path.total_cost = subpath->total_cost;
+	cost_add_member_mul(&pathnode->path.total_cost, cpu_tuple_cost, subpath->rows);
 
 	return pathnode;
 }
@@ -3482,8 +3501,8 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel,
 	 * costs to change any higher-level planning choices.  But we might want
 	 * to make it look better sometime.
 	 */
-	pathnode->path.startup_cost = 0;
-	pathnode->path.total_cost = 0;
+	cost_zero(&pathnode->path.startup_cost); // = zerocost?
+	cost_zero(&pathnode->path.total_cost); // = zerocost?
 	pathnode->path.rows = 0;
 	total_size = 0;
 	foreach(lc, subpaths)
@@ -3492,7 +3511,7 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel,
 
 		if (lc == list_head(subpaths))	/* first node? */
 			pathnode->path.startup_cost = subpath->startup_cost;
-		pathnode->path.total_cost += subpath->total_cost;
+		cost_add(&pathnode->path.total_cost, &subpath->total_cost);
 		pathnode->path.rows += subpath->rows;
 		total_size += subpath->pathtarget->width * subpath->rows;
 	}
@@ -3615,10 +3634,11 @@ adjust_limit_rows_costs(double *rows,	/* in/out parameter */
 			offset_rows = clamp_row_est(input_rows * 0.10);
 		if (offset_rows > *rows)
 			offset_rows = *rows;
-		if (input_rows > 0)
-			*startup_cost +=
-				(input_total_cost - input_startup_cost)
-				* offset_rows / input_rows;
+		if (input_rows > 0) {
+			Cost tmp;
+			cost_set_diff(&tmp, &input_total_cost, &input_startup_cost);
+			cost_add_mul(startup_cost, &tmp, offset_rows / input_rows);
+		}
 		*rows -= offset_rows;
 		if (*rows < 1)
 			*rows = 1;
@@ -3634,10 +3654,12 @@ adjust_limit_rows_costs(double *rows,	/* in/out parameter */
 			count_rows = clamp_row_est(input_rows * 0.10);
 		if (count_rows > *rows)
 			count_rows = *rows;
-		if (input_rows > 0)
-			*total_cost = *startup_cost +
-				(input_total_cost - input_startup_cost)
-				* count_rows / input_rows;
+
+		if (input_rows > 0) {
+			cost_set_diff(total_cost, &input_total_cost, &input_startup_cost);
+			cost_mul_scalar(total_cost, count_rows / input_rows);
+			cost_add(total_cost, startup_cost);
+		}
 		*rows = count_rows;
 		if (*rows < 1)
 			*rows = 1;
diff --git a/src/backend/optimizer/util/placeholder.c b/src/backend/optimizer/util/placeholder.c
index 798aa8c..bd3e712 100644
--- a/src/backend/optimizer/util/placeholder.c
+++ b/src/backend/optimizer/util/placeholder.c
@@ -447,8 +447,8 @@ add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
 
 					cost_qual_eval_node(&cost, (Node *) phinfo->ph_var->phexpr,
 										root);
-					joinrel->reltarget->cost.startup += cost.startup;
-					joinrel->reltarget->cost.per_tuple += cost.per_tuple;
+					cost_add(&joinrel->reltarget->cost.startup, &cost.startup);
+					cost_add(&joinrel->reltarget->cost.per_tuple, &cost.per_tuple);
 				}
 
 				/* Adjust joinrel's direct_lateral_relids as needed */
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 5e889d1..5c7c000 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1924,8 +1924,8 @@ add_function_cost(PlannerInfo *root, Oid funcid, Node *node,
 		req.node = node;
 
 		/* Initialize cost fields so that support function doesn't have to */
-		req.startup = 0;
-		req.per_tuple = 0;
+		cost_zero(&req.startup);
+		cost_zero(&req.per_tuple);
 
 		sresult = (SupportRequestCost *)
 			DatumGetPointer(OidFunctionCall1(procform->prosupport,
@@ -1934,15 +1934,15 @@ add_function_cost(PlannerInfo *root, Oid funcid, Node *node,
 		if (sresult == &req)
 		{
 			/* Success, so accumulate support function's estimate into *cost */
-			cost->startup += req.startup;
-			cost->per_tuple += req.per_tuple;
+			cost_add(&cost->startup, &req.startup);
+			cost_add(&cost->per_tuple, &req.per_tuple);
 			ReleaseSysCache(proctup);
 			return;
 		}
 	}
 
 	/* No support function, or it failed, so rely on procost */
-	cost->per_tuple += procform->procost * cpu_operator_cost;
+	cost_add_member_mul(&cost->per_tuple, cpu_operator_cost, procform->procost);
 
 	ReleaseSysCache(proctup);
 }
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index c9eb447..c5bf171 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -233,8 +233,8 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->unique_for_rels = NIL;
 	rel->non_unique_for_rels = NIL;
 	rel->baserestrictinfo = NIL;
-	rel->baserestrictcost.startup = 0;
-	rel->baserestrictcost.per_tuple = 0;
+	cost_zero(&rel->baserestrictcost.startup);
+	cost_zero(&rel->baserestrictcost.per_tuple);
 	rel->baserestrict_min_security = UINT_MAX;
 	rel->joininfo = NIL;
 	rel->has_eclass_joins = false;
@@ -645,8 +645,8 @@ build_join_rel(PlannerInfo *root,
 	joinrel->unique_for_rels = NIL;
 	joinrel->non_unique_for_rels = NIL;
 	joinrel->baserestrictinfo = NIL;
-	joinrel->baserestrictcost.startup = 0;
-	joinrel->baserestrictcost.per_tuple = 0;
+	cost_zero(&joinrel->baserestrictcost.startup);
+	cost_zero(&joinrel->baserestrictcost.per_tuple);
 	joinrel->baserestrict_min_security = UINT_MAX;
 	joinrel->joininfo = NIL;
 	joinrel->has_eclass_joins = false;
@@ -822,8 +822,8 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->fdwroutine = NULL;
 	joinrel->fdw_private = NULL;
 	joinrel->baserestrictinfo = NIL;
-	joinrel->baserestrictcost.startup = 0;
-	joinrel->baserestrictcost.per_tuple = 0;
+	cost_zero(&joinrel->baserestrictcost.startup);
+	cost_zero(&joinrel->baserestrictcost.per_tuple);
 	joinrel->joininfo = NIL;
 	joinrel->has_eclass_joins = false;
 	joinrel->consider_partitionwise_join = false;	/* might get changed later */
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index 3b50fd2..01a1a76 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -17,6 +17,7 @@
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
+#include "optimizer/cost.h"
 #include "optimizer/optimizer.h"
 #include "optimizer/restrictinfo.h"
 
@@ -183,7 +184,7 @@ make_restrictinfo_internal(Expr *clause,
 	 */
 	restrictinfo->parent_ec = NULL;
 
-	restrictinfo->eval_cost.startup = -1;
+	cost_zero(&restrictinfo->eval_cost.startup); // XXX
 	restrictinfo->norm_selec = -1;
 	restrictinfo->outer_selec = -1;
 
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index ff02b5a..2df6f0e 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -5592,12 +5592,12 @@ get_quals_from_indexclauses(List *indexclauses)
  * or directly on an indexorderbys list.  In both cases, we expect that the
  * index key expression is on the left side of binary clauses.
  */
-Cost
-index_other_operands_eval_cost(PlannerInfo *root, List *indexquals)
+void
+index_other_operands_eval_cost(PlannerInfo *root, List *indexquals, Cost *qual_arg_cost)
 {
-	Cost		qual_arg_cost = 0;
 	ListCell   *lc;
 
+	cost_zero(qual_arg_cost);
 	foreach(lc, indexquals)
 	{
 		Expr	   *clause = (Expr *) lfirst(lc);
@@ -5641,9 +5641,8 @@ index_other_operands_eval_cost(PlannerInfo *root, List *indexquals)
 		}
 
 		cost_qual_eval_node(&index_qual_cost, other_operand, root);
-		qual_arg_cost += index_qual_cost.startup + index_qual_cost.per_tuple;
+		cost_add2(qual_arg_cost, &index_qual_cost.startup, &index_qual_cost.per_tuple);
 	}
-	return qual_arg_cost;
 }
 
 void
@@ -5665,8 +5664,9 @@ genericcostestimate(PlannerInfo *root,
 	double		num_sa_scans;
 	double		num_outer_scans;
 	double		num_scans;
-	double		qual_op_cost;
-	double		qual_arg_cost;
+	Cost		qual_op_cost;
+	Cost		qual_arg_cost;
+	Cost		tmp;
 	List	   *selectivityQuals;
 	ListCell   *l;
 
@@ -5792,8 +5792,7 @@ genericcostestimate(PlannerInfo *root,
 		 * share for each outer scan.  (Don't pro-rate for ScalarArrayOpExpr,
 		 * since that's internal to the indexscan.)
 		 */
-		indexTotalCost = (pages_fetched * spc_random_page_cost)
-			/ num_outer_scans;
+		cost_add_member_mul_spc(&indexTotalCost, spc_, random_page_cost, pages_fetched/num_outer_scans);
 	}
 	else
 	{
@@ -5801,7 +5800,7 @@ genericcostestimate(PlannerInfo *root,
 		 * For a single index scan, we just charge spc_random_page_cost per
 		 * page touched.
 		 */
-		indexTotalCost = numIndexPages * spc_random_page_cost;
+		cost_add_member_mul_spc(&indexTotalCost, spc_, random_page_cost, numIndexPages);
 	}
 
 	/*
@@ -5818,14 +5817,16 @@ genericcostestimate(PlannerInfo *root,
 	 * Detecting that that might be needed seems more expensive than it's
 	 * worth, though, considering all the other inaccuracies here ...
 	 */
-	qual_arg_cost = index_other_operands_eval_cost(root, indexQuals) +
-		index_other_operands_eval_cost(root, indexOrderBys);
-	qual_op_cost = cpu_operator_cost *
-		(list_length(indexQuals) + list_length(indexOrderBys));
+	index_other_operands_eval_cost(root, indexQuals, &qual_arg_cost);
+	index_other_operands_eval_cost(root, indexOrderBys, &tmp);
+	cost_add(&qual_arg_cost, &tmp);
+
+	cost_set_member_mul(&qual_op_cost, cpu_operator_cost, list_length(indexQuals) + list_length(indexOrderBys));
 
 	indexStartupCost = qual_arg_cost;
-	indexTotalCost += qual_arg_cost;
-	indexTotalCost += numIndexTuples * num_sa_scans * (cpu_index_tuple_cost + qual_op_cost);
+	cost_add(&indexTotalCost, &qual_arg_cost);
+	cost_add_mul(&indexTotalCost, &qual_op_cost, numIndexTuples * num_sa_scans);
+	cost_add_member_mul(&indexTotalCost, cpu_index_tuple_cost, numIndexTuples * num_sa_scans);
 
 	/*
 	 * Generic assumption about index correlation: there isn't any.
@@ -6065,9 +6066,9 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 	 */
 	if (index->tuples > 1)		/* avoid computing log(0) */
 	{
-		descentCost = ceil(log(index->tuples) / log(2.0)) * cpu_operator_cost;
-		costs.indexStartupCost += descentCost;
-		costs.indexTotalCost += costs.num_sa_scans * descentCost;
+		cost_set_member_mul(&descentCost, cpu_operator_cost, ceil(log(index->tuples) / log(2.0)));
+		cost_add(&costs.indexStartupCost, &descentCost);
+		cost_add_mul(&costs.indexTotalCost, &descentCost, costs.num_sa_scans);
 	}
 
 	/*
@@ -6080,9 +6081,9 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 	 * touched.  The number of such pages is btree tree height plus one (ie,
 	 * we charge for the leaf page too).  As above, charge once per SA scan.
 	 */
-	descentCost = (index->tree_height + 1) * 50.0 * cpu_operator_cost;
-	costs.indexStartupCost += descentCost;
-	costs.indexTotalCost += costs.num_sa_scans * descentCost;
+	cost_set_member_mul(&descentCost, cpu_operator_cost, (index->tree_height + 1) * 50.0);
+	cost_add(&costs.indexStartupCost, &descentCost);
+	cost_add_mul(&costs.indexTotalCost, &descentCost, costs.num_sa_scans);
 
 	/*
 	 * If we can get an estimate of the first column's ordering correlation C
@@ -6273,17 +6274,17 @@ gistcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 	 */
 	if (index->tuples > 1)		/* avoid computing log(0) */
 	{
-		descentCost = ceil(log(index->tuples)) * cpu_operator_cost;
-		costs.indexStartupCost += descentCost;
-		costs.indexTotalCost += costs.num_sa_scans * descentCost;
+		cost_set_member_mul(&descentCost, cpu_operator_cost, ceil(log(index->tuples)));
+		cost_add(&costs.indexStartupCost, &descentCost);
+		cost_add_mul(&costs.indexTotalCost, &descentCost, costs.num_sa_scans);
 	}
 
 	/*
 	 * Likewise add a per-page charge, calculated the same as for btrees.
 	 */
-	descentCost = (index->tree_height + 1) * 50.0 * cpu_operator_cost;
-	costs.indexStartupCost += descentCost;
-	costs.indexTotalCost += costs.num_sa_scans * descentCost;
+	cost_set_member_mul(&descentCost, cpu_operator_cost, (index->tree_height + 1) * 50.0);
+	cost_add(&costs.indexStartupCost, &descentCost);
+	cost_add_mul(&costs.indexTotalCost, &descentCost, costs.num_sa_scans);
 
 	*indexStartupCost = costs.indexStartupCost;
 	*indexTotalCost = costs.indexTotalCost;
@@ -6330,17 +6331,17 @@ spgcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 	 */
 	if (index->tuples > 1)		/* avoid computing log(0) */
 	{
-		descentCost = ceil(log(index->tuples)) * cpu_operator_cost;
-		costs.indexStartupCost += descentCost;
-		costs.indexTotalCost += costs.num_sa_scans * descentCost;
+		cost_set_member_mul(&descentCost, cpu_operator_cost, ceil(log(index->tuples)));
+		cost_add(&costs.indexStartupCost, &descentCost);
+		cost_add_mul(&costs.indexTotalCost, &descentCost, costs.num_sa_scans);
 	}
 
 	/*
 	 * Likewise add a per-page charge, calculated the same as for btrees.
 	 */
-	descentCost = (index->tree_height + 1) * 50.0 * cpu_operator_cost;
-	costs.indexStartupCost += descentCost;
-	costs.indexTotalCost += costs.num_sa_scans * descentCost;
+	cost_set_member_mul(&descentCost, cpu_operator_cost, (index->tree_height + 1) * 50.0);
+	cost_add(&costs.indexStartupCost, &descentCost);
+	cost_add_mul(&costs.indexTotalCost, &descentCost, costs.num_sa_scans);
 
 	*indexStartupCost = costs.indexStartupCost;
 	*indexTotalCost = costs.indexTotalCost;
@@ -6658,9 +6659,9 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 	double		entryPagesFetched,
 				dataPagesFetched,
 				dataPagesFetchedBySel;
-	double		qual_op_cost,
-				qual_arg_cost,
-				spc_random_page_cost,
+	Cost		qual_op_cost,
+				qual_arg_cost;
+	double		spc_random_page_cost,
 				outer_scans;
 	Relation	indexRel;
 	GinStatsData ginStats;
@@ -6815,9 +6816,9 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 	/* Fall out if there were any provably-unsatisfiable quals */
 	if (!matchPossible)
 	{
-		*indexStartupCost = 0;
-		*indexTotalCost = 0;
-		*indexSelectivity = 0;
+		cost_zero(indexStartupCost);
+		cost_zero(indexTotalCost);
+		indexSelectivity = 0;
 		return;
 	}
 
@@ -6892,7 +6893,7 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 	 * Here we use random page cost because logically-close pages could be far
 	 * apart on disk.
 	 */
-	*indexStartupCost = (entryPagesFetched + dataPagesFetched) * spc_random_page_cost;
+	cost_set_member_mul_spc(indexStartupCost, spc_, random_page_cost, entryPagesFetched + dataPagesFetched);
 
 	/*
 	 * Now compute the number of data pages fetched during the scan.
@@ -6931,19 +6932,20 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 	}
 
 	/* And apply random_page_cost as the cost per page */
-	*indexTotalCost = *indexStartupCost +
-		dataPagesFetched * spc_random_page_cost;
+	*indexTotalCost = *indexStartupCost;
+	cost_add_member_mul_spc(indexTotalCost, spc_, random_page_cost, dataPagesFetched);
 
 	/*
 	 * Add on index qual eval costs, much as in genericcostestimate.  But we
 	 * can disregard indexorderbys, since GIN doesn't support those.
 	 */
-	qual_arg_cost = index_other_operands_eval_cost(root, indexQuals);
-	qual_op_cost = cpu_operator_cost * list_length(indexQuals);
+	index_other_operands_eval_cost(root, indexQuals, &qual_arg_cost);
+	cost_set_member_mul(&qual_op_cost, cpu_operator_cost, list_length(indexQuals));
 
-	*indexStartupCost += qual_arg_cost;
-	*indexTotalCost += qual_arg_cost;
-	*indexTotalCost += (numTuples * *indexSelectivity) * (cpu_index_tuple_cost + qual_op_cost);
+	cost_add(indexStartupCost, &qual_arg_cost);
+	cost_add(indexTotalCost, &qual_arg_cost);
+	cost_add_mul(indexTotalCost, &qual_op_cost, *indexSelectivity * numTuples);
+	cost_add_member_mul(indexTotalCost, cpu_index_tuple_cost, *indexSelectivity * numTuples);
 	*indexPages = dataPagesFetched;
 }
 
@@ -6961,9 +6963,9 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 	double		numPages = index->pages;
 	RelOptInfo *baserel = index->rel;
 	RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root);
-	Cost		spc_seq_page_cost;
-	Cost		spc_random_page_cost;
-	double		qual_arg_cost;
+	double		spc_seq_page_cost;
+	double		spc_random_page_cost;
+	Cost		qual_arg_cost;
 	double		qualSelectivity;
 	BrinStatsData statsData;
 	double		indexRanges;
@@ -7137,23 +7139,23 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 	 * the index costs.  We can disregard indexorderbys, since BRIN doesn't
 	 * support those.
 	 */
-	qual_arg_cost = index_other_operands_eval_cost(root, indexQuals);
+	index_other_operands_eval_cost(root, indexQuals, &qual_arg_cost);
 
 	/*
 	 * Compute the startup cost as the cost to read the whole revmap
 	 * sequentially, including the cost to execute the index quals.
 	 */
-	*indexStartupCost =
-		spc_seq_page_cost * statsData.revmapNumPages * loop_count;
-	*indexStartupCost += qual_arg_cost;
+	cost_set_member_mul_spc(indexStartupCost,
+		spc_, seq_page_cost, statsData.revmapNumPages * loop_count);
+	cost_add(indexStartupCost, &qual_arg_cost);
 
 	/*
 	 * To read a BRIN index there might be a bit of back and forth over
 	 * regular pages, as revmap might point to them out of sequential order;
 	 * calculate the total cost as reading the whole index in random order.
 	 */
-	*indexTotalCost = *indexStartupCost +
-		spc_random_page_cost * (numPages - statsData.revmapNumPages) * loop_count;
+	*indexTotalCost = *indexStartupCost;
+	cost_add_member_mul_spc(indexTotalCost, spc_, random_page_cost, (numPages - statsData.revmapNumPages) * loop_count);
 
 	/*
 	 * Charge a small amount per range tuple which we expect to match to. This
@@ -7162,8 +7164,8 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 	 * range, so we must multiply the charge by the number of pages in the
 	 * range.
 	 */
-	*indexTotalCost += 0.1 * cpu_operator_cost * estimatedRanges *
-		statsData.pagesPerRange;
+	cost_add_member_mul(indexTotalCost, cpu_operator_cost, 0.1 * estimatedRanges *
+		statsData.pagesPerRange);
 
 	*indexPages = index->pages;
 }
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index abc3062..c9b24dd 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -62,6 +62,7 @@
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/optimizer.h"
+#include "optimizer/cost.h"
 #include "parser/analyze.h"
 #include "parser/parsetree.h"
 #include "storage/lmgr.h"
@@ -1082,7 +1083,7 @@ cached_plan_cost(CachedPlan *plan, bool include_planner)
 		if (plannedstmt->commandType == CMD_UTILITY)
 			continue;			/* Ignore utility statements */
 
-		result += plannedstmt->planTree->total_cost;
+		result += cost_asscalar(&plannedstmt->planTree->total_cost);
 
 		if (include_planner)
 		{
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index bce2d59..f895951 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -656,7 +656,21 @@ extern bool equal(const void *a, const void *b);
  * depend on them...
  */
 typedef double Selectivity;		/* fraction of tuples a qualifier will pass */
-typedef double Cost;			/* execution cost (in page-access units) */
+
+/* execution cost (in page-access units) */
+/* these could be ints to be multipled by associated cost parameter */
+typedef struct {
+	double cpu_index_tuple_cost;
+	double cpu_operator_cost;
+	double cpu_tuple_cost;
+	double parallel_setup_cost;
+	// bool parallel_setup_cost;
+	double parallel_tuple_cost;
+	double random_page_cost;
+	double seq_page_cost;
+	double disable_cost;
+	// bool disable_cost;
+} Cost;
 
 
 /*
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index b3d0b4f..4a4be15 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -45,7 +45,7 @@ typedef enum
  */
 
 /* parameter variables and flags (see also optimizer.h) */
-extern PGDLLIMPORT Cost disable_cost;
+extern PGDLLIMPORT double disable_cost;
 extern PGDLLIMPORT int max_parallel_workers_per_gather;
 extern PGDLLIMPORT bool enable_seqscan;
 extern PGDLLIMPORT bool enable_indexscan;
@@ -98,8 +98,8 @@ extern void cost_resultscan(Path *path, PlannerInfo *root,
 							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,
-					  Cost comparison_cost, int sort_mem,
+					  List *pathkeys, Cost *input_cost, double tuples, int width,
+					  Cost *comparison_cost, int sort_mem,
 					  double limit_tuples);
 extern void cost_append(AppendPath *path);
 extern void cost_merge_append(Path *path, PlannerInfo *root,
@@ -113,7 +113,7 @@ extern void cost_agg(Path *path, PlannerInfo *root,
 					 AggStrategy aggstrategy, const AggClauseCosts *aggcosts,
 					 int numGroupCols, double numGroups,
 					 List *quals,
-					 Cost input_startup_cost, Cost input_total_cost,
+					 Cost *input_startup_cost, Cost *input_total_cost,
 					 double input_tuples);
 extern void cost_windowagg(Path *path, PlannerInfo *root,
 						   List *windowFuncs, int numPartCols, int numOrderCols,
@@ -196,5 +196,28 @@ extern void set_foreign_size_estimates(PlannerInfo *root, RelOptInfo *rel);
 extern PathTarget *set_pathtarget_cost_width(PlannerInfo *root, PathTarget *target);
 extern double compute_bitmap_pages(PlannerInfo *root, RelOptInfo *baserel,
 								   Path *bitmapqual, int loop_count, Cost *cost, double *tuple);
+#define cost_add_member(cost,member) (cost)->member += member
+#define cost_add_member_mul(cost,member,mul) (cost)->member += member*mul
+#define cost_add_member_mul_spc(cost,spc,member,mul) (cost)->member += spc##member*mul
+
+#define cost_set_member(cost,member) do{ cost_zero(cost); (cost)->member = member; } while(0);
+#define cost_set_member_mul(cost,member,mul) do{ cost_zero(cost); (cost)->member = member*mul; } while(0);
+#define cost_set_member_mul_spc(cost,spc,member,mul) do{ cost_zero(cost); (cost)->member = spc##member*mul; } while(0);
+#define cost_set_member_add(cost,member,from1) do{ *cost = *from1; (cost)->member += member; } while(0);
+
+extern void cost_zero(Cost *to);
+extern void cost_set_sum2(Cost *to, const Cost *from1, const Cost *from2);
+extern void cost_set_sum3(Cost *to, const Cost *from1, const Cost *from2, const Cost *from3);
+extern void cost_set_diff(Cost *to, const Cost *from1, const Cost *from2);
+extern void cost_add(Cost *to, const Cost *from);
+extern void cost_add2(Cost *to, const Cost *from1, const Cost *from2);
+extern void cost_add_mul(Cost *to, const Cost *from, double multiplier);
+extern void cost_sub(Cost *to, const Cost *from);
+extern void cost_mul_scalar(Cost *to, double by);
+
+/* Comparison */
+extern bool cost_isgt(const Cost *to, const Cost *than);
+extern bool cost_islt(const Cost *to, const Cost *than);
+extern double cost_asscalar(const Cost *to);
 
 #endif							/* COST_H */
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
index 353d38a..e08077c 100644
--- a/src/include/utils/selfuncs.h
+++ b/src/include/utils/selfuncs.h
@@ -192,8 +192,9 @@ extern double estimate_hashagg_tablesize(Path *path,
 										 double dNumGroups);
 
 extern List *get_quals_from_indexclauses(List *indexclauses);
-extern Cost index_other_operands_eval_cost(PlannerInfo *root,
-										   List *indexquals);
+extern void index_other_operands_eval_cost(PlannerInfo *root,
+										   List *indexquals,
+										   Cost *qual_arg_cost);
 extern List *add_predicate_to_index_quals(IndexOptInfo *index,
 										  List *indexQuals);
 extern void genericcostestimate(PlannerInfo *root, IndexPath *path,
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index 2f66d76..59f9140 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -31,6 +31,7 @@
 #include "executor/spi.h"
 #include "miscadmin.h"
 #include "nodes/supportnodes.h"
+#include "optimizer/cost.h"
 #include "optimizer/optimizer.h"
 #include "optimizer/plancat.h"
 #include "port/atomics.h"
@@ -853,8 +854,8 @@ test_support_func(PG_FUNCTION_ARGS)
 		/* Provide some generic estimate */
 		SupportRequestCost *req = (SupportRequestCost *) rawreq;
 
-		req->startup = 0;
-		req->per_tuple = 2 * cpu_operator_cost;
+		cost_zero(&req->startup);
+		cost_add_member_mul(&req->per_tuple, cpu_operator_cost, 2);
 		ret = (Node *) req;
 	}
 
-- 
2.7.4

