commit fa33fbebe3e33f09fdb32961b2ffdf5c7262b74a
Author: Alexander Korotkov <akorotkov@postgresql.org>
Date:   Mon Oct 15 21:33:31 2018 +0300

    Add costing function to tableam interface
    
    Costs of sequential scan and table sample scan are estimated using
    cost_seqscan() and cost_samplescan().  But they should be table access method
    specific, because different table AMs could have different costs.  This commit
    introduces zheap cost functions to be the same as heap.  That should be
    adjusted in future.  Make costs of heap lookups during index scan is TODO.

diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index aee7bfd8346..e13b0e0b8fa 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -13,6 +13,7 @@ top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
 OBJS = heapam.o heapam_handler.o heapam_visibility.o hio.o pruneheap.o \
-	rewriteheap.o syncscan.o tuptoaster.o vacuumlazy.o visibilitymap.o
+	rewriteheap.o syncscan.o tuptoaster.o vacuumlazy.o visibilitymap.o \
+	heapam_cost.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 28c475e7bdc..35b230c3606 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -2146,7 +2146,10 @@ static const TableAmRoutine heapam_methods = {
 
 	.index_build_range_scan = IndexBuildHeapRangeScan,
 
-	.index_validate_scan = validate_index_heapscan
+	.index_validate_scan = validate_index_heapscan,
+
+	.cost_scan = heapam_cost_scan,
+	.cost_samplescan = heapam_cost_samplescan
 };
 
 const TableAmRoutine *
diff --git a/src/backend/access/zheap/Makefile b/src/backend/access/zheap/Makefile
index 75b0ff69ebf..4458e1a4238 100644
--- a/src/backend/access/zheap/Makefile
+++ b/src/backend/access/zheap/Makefile
@@ -13,6 +13,6 @@ top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
 OBJS = prunetpd.o prunezheap.o tpd.o tpdxlog.o zheapam.o zheapam_handler.o zheapamutils.o zheapamxlog.o \
-	zhio.o zmultilocker.o ztuptoaster.o ztqual.o zvacuumlazy.o
+	zhio.o zmultilocker.o ztuptoaster.o ztqual.o zvacuumlazy.o zheapam_cost.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/zheap/zheapam_handler.c b/src/backend/access/zheap/zheapam_handler.c
index e707baa1b5d..da95c881a77 100644
--- a/src/backend/access/zheap/zheapam_handler.c
+++ b/src/backend/access/zheap/zheapam_handler.c
@@ -1627,6 +1627,8 @@ static const TableAmRoutine zheapam_methods = {
 	.index_build_range_scan = IndexBuildZHeapRangeScan,
 	.index_validate_scan = validate_index_zheapscan,
 
+	.cost_scan = zheapam_cost_scan,
+	.cost_samplescan = zheapam_cost_samplescan
 };
 
 Datum
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 7bf67a05295..abdddacf89a 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -75,6 +75,7 @@
 
 #include "access/amapi.h"
 #include "access/htup_details.h"
+#include "access/tableam.h"
 #include "access/tsmapi.h"
 #include "executor/executor.h"
 #include "executor/nodeHash.h"
@@ -150,9 +151,6 @@ static MergeScanSelCache *cached_scansel(PlannerInfo *root,
 static void cost_rescan(PlannerInfo *root, Path *path,
 			Cost *rescan_startup_cost, Cost *rescan_total_cost);
 static bool cost_qual_eval_walker(Node *node, cost_qual_eval_context *context);
-static void get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel,
-						  ParamPathInfo *param_info,
-						  QualCost *qpqual_cost);
 static bool has_indexed_join_quals(NestPath *joinpath);
 static double approx_tuple_count(PlannerInfo *root, JoinPath *path,
 				   List *quals);
@@ -174,7 +172,6 @@ static Cost append_nonpartial_cost(List *subpaths, int numpaths,
 static void set_rel_width(PlannerInfo *root, RelOptInfo *rel);
 static double relation_byte_size(double tuples, int width);
 static double page_size(double tuples, int width);
-static double get_parallel_divisor(Path *path);
 
 
 /*
@@ -209,70 +206,9 @@ void
 cost_seqscan(Path *path, PlannerInfo *root,
 			 RelOptInfo *baserel, ParamPathInfo *param_info)
 {
-	Cost		startup_cost = 0;
-	Cost		cpu_run_cost;
-	Cost		disk_run_cost;
-	double		spc_seq_page_cost;
-	QualCost	qpqual_cost;
-	Cost		cpu_per_tuple;
-
-	/* Should only be applied to base relations */
-	Assert(baserel->relid > 0);
-	Assert(baserel->rtekind == RTE_RELATION);
-
-	/* Mark the path with the correct row estimate */
-	if (param_info)
-		path->rows = param_info->ppi_rows;
-	else
-		path->rows = baserel->rows;
-
-	if (!enable_seqscan)
-		startup_cost += disable_cost;
-
-	/* fetch estimated page cost for tablespace containing table */
-	get_tablespace_page_costs(baserel->reltablespace,
-							  NULL,
-							  &spc_seq_page_cost);
-
-	/*
-	 * disk costs
-	 */
-	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;
-	/* 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;
-
-	/* Adjust costing for parallelism, if used. */
-	if (path->parallel_workers > 0)
-	{
-		double		parallel_divisor = get_parallel_divisor(path);
-
-		/* The CPU cost is divided among all the workers. */
-		cpu_run_cost /= parallel_divisor;
-
-		/*
-		 * It may be possible to amortize some of the I/O cost, but probably
-		 * not very much, because most operating systems already do aggressive
-		 * prefetching.  For now, we assume that the disk run cost can't be
-		 * amortized at all.
-		 */
-
-		/*
-		 * In the case of a parallel plan, the row count needs to represent
-		 * the number of tuples processed per worker.
-		 */
-		path->rows = clamp_row_est(path->rows / parallel_divisor);
-	}
-
-	path->startup_cost = startup_cost;
-	path->total_cost = startup_cost + cpu_run_cost + disk_run_cost;
+	TableScanCost_function cost_func = (TableScanCost_function) baserel->cost_scan;
+	Assert(cost_func != NULL);
+	return cost_func(path, root, baserel, param_info);
 }
 
 /*
@@ -286,65 +222,9 @@ void
 cost_samplescan(Path *path, PlannerInfo *root,
 				RelOptInfo *baserel, ParamPathInfo *param_info)
 {
-	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;
-	QualCost	qpqual_cost;
-	Cost		cpu_per_tuple;
-
-	/* Should only be applied to base relations with tablesample clauses */
-	Assert(baserel->relid > 0);
-	rte = planner_rt_fetch(baserel->relid, root);
-	Assert(rte->rtekind == RTE_RELATION);
-	tsc = rte->tablesample;
-	Assert(tsc != NULL);
-	tsm = GetTsmRoutine(tsc->tsmhandler);
-
-	/* Mark the path with the correct row estimate */
-	if (param_info)
-		path->rows = param_info->ppi_rows;
-	else
-		path->rows = baserel->rows;
-
-	/* fetch estimated page cost for tablespace containing table */
-	get_tablespace_page_costs(baserel->reltablespace,
-							  &spc_random_page_cost,
-							  &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;
-
-	/*
-	 * CPU costs (recall that baserel->tuples has already been set to the
-	 * number of tuples the sampling method will select).  Note that we ignore
-	 * execution cost of the TABLESAMPLE parameter expressions; they will be
-	 * evaluated only once per scan, and in most usages they'll likely be
-	 * simple constants anyway.  We also don't charge anything for the
-	 * calculations the sampling method might do internally.
-	 */
-	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;
-	/* 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;
-
-	path->startup_cost = startup_cost;
-	path->total_cost = startup_cost + run_cost;
+	TableScanCost_function cost_func = (TableScanCost_function) baserel->cost_samplescan;
+	Assert(cost_func != NULL);
+	return cost_func(path, root, baserel, param_info);
 }
 
 /*
@@ -3988,7 +3868,7 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
  * some of the quals.  We assume baserestrictcost was previously set by
  * set_baserel_size_estimates().
  */
-static void
+void
 get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel,
 						  ParamPathInfo *param_info,
 						  QualCost *qpqual_cost)
@@ -5343,7 +5223,7 @@ page_size(double tuples, int width)
  * Estimate the fraction of the work that each worker will do given the
  * number of workers budgeted for the path.
  */
-static double
+double
 get_parallel_divisor(Path *path)
 {
 	double		parallel_divisor = path->parallel_workers;
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 86029cd1327..45f3b0372b9 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -93,6 +93,8 @@ static void set_baserel_partition_key_exprs(Relation relation,
  *	pages		number of pages
  *	tuples		number of tuples
  *	rel_parallel_workers user-defined number of parallel workers
+ *	cost_scan	costing function for sequential scan
+ *	cost_samplescan costing function for sample scan
  *
  * Also, add information about the relation's foreign keys to root->fkey_list.
  *
@@ -443,6 +445,18 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 		rel->fdwroutine = NULL;
 	}
 
+	/* Get costing functions */
+	if (relation->rd_tableamroutine != NULL)
+	{
+		rel->cost_scan = relation->rd_tableamroutine->cost_scan;
+		rel->cost_samplescan = relation->rd_tableamroutine->cost_samplescan;
+	}
+	else
+	{
+		rel->cost_scan = NULL;
+		rel->cost_samplescan = NULL;
+	}
+
 	/* Collect info about relation's foreign keys, if relevant */
 	get_relation_foreign_keys(root, rel, relation, inhparent);
 
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index dba50178887..2f342c7fef1 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -17,6 +17,7 @@
 #include "access/genham.h"
 #include "nodes/lockoptions.h"
 #include "nodes/primnodes.h"
+#include "optimizer/cost.h"
 #include "storage/bufpage.h"
 #include "storage/lockdefs.h"
 #include "utils/relcache.h"
@@ -59,6 +60,13 @@ extern Relation heap_openrv_extended(const RangeVar *relation,
 
 #define heap_close(r,l)  relation_close(r,l)
 
+/* in heap/heamam_cost.c */
+extern void heapam_cost_scan(Path *path, PlannerInfo *root,
+							 RelOptInfo *baserel, ParamPathInfo *param_info);
+extern void heapam_cost_samplescan(Path *path, PlannerInfo *root,
+							 RelOptInfo *baserel, ParamPathInfo *param_info);
+
+
 /* struct definitions appear in relscan.h */
 typedef struct TableScanDescData *TableScanDesc;
 typedef struct HeapScanDescData *HeapScanDesc;
diff --git a/src/include/access/tableam.h b/src/include/access/tableam.h
index fb37a739918..beea954885a 100644
--- a/src/include/access/tableam.h
+++ b/src/include/access/tableam.h
@@ -22,6 +22,7 @@
 #include "executor/tuptable.h"
 #include "nodes/execnodes.h"
 #include "nodes/nodes.h"
+#include "optimizer/cost.h"
 #include "fmgr.h"
 #include "utils/guc.h"
 #include "utils/rel.h"
@@ -192,6 +193,8 @@ struct SampleScanState;
 typedef bool (*SampleScanNextBlock_function)(TableScanDesc scan, struct SampleScanState *scanstate);
 typedef bool (*SampleScanNextTuple_function)(TableScanDesc scan, struct SampleScanState *scanstate, TupleTableSlot *slot);
 
+typedef void (*TableScanCost_function)(Path *path, PlannerInfo *root, RelOptInfo *baserel, ParamPathInfo *param_info);
+
 /*
  * API struct for a table AM.  Note this must be allocated in a
  * server-lifetime manner, typically as a static const struct.
@@ -246,6 +249,10 @@ typedef struct TableAmRoutine
 
 	IndexBuildRangeScan_function index_build_range_scan;
 	IndexValidateScan_function index_validate_scan;
+
+	/* Costing functions */
+	TableScanCost_function cost_scan;
+	TableScanCost_function cost_samplescan;
 }			TableAmRoutine;
 
 static inline const TupleTableSlotOps*
diff --git a/src/include/access/zheap.h b/src/include/access/zheap.h
index c657c728ec3..583cf25f965 100644
--- a/src/include/access/zheap.h
+++ b/src/include/access/zheap.h
@@ -20,6 +20,7 @@
 #include "access/hio.h"
 #include "access/undoinsert.h"
 #include "access/zhtup.h"
+#include "optimizer/cost.h"
 #include "utils/rel.h"
 #include "utils/snapshot.h"
 
@@ -211,4 +212,11 @@ typedef struct ZHeapFreeOffsetRanges
 	int nranges;
 } ZHeapFreeOffsetRanges;
 
+/* Zheap costing functions */
+extern void zheapam_cost_scan(Path *path, PlannerInfo *root,
+							  RelOptInfo *baserel, ParamPathInfo *param_info);
+extern void zheapam_cost_samplescan(Path *path, PlannerInfo *root,
+							  RelOptInfo *baserel, ParamPathInfo *param_info);
+
+
 #endif   /* ZHEAP_H */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index adb42650479..6c51fe27460 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -704,6 +704,10 @@ typedef struct RelOptInfo
 	List	  **partexprs;		/* Non-nullable partition key expressions. */
 	List	  **nullable_partexprs; /* Nullable partition key expressions. */
 	List	   *partitioned_child_rels; /* List of RT indexes. */
+
+	/* Rather than include tableam.h here, we declare costing functions like this */
+	void		(*cost_scan) ();	/* sequential scan cost estimator */
+	void		(*cost_samplescan) ();	/* sample scan cost estimator */
 } RelOptInfo;
 
 /*
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index 77ca7ff8371..0af574c41fe 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -166,6 +166,9 @@ extern void cost_gather(GatherPath *path, PlannerInfo *root,
 extern void cost_subplan(PlannerInfo *root, SubPlan *subplan, Plan *plan);
 extern void cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root);
 extern void cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root);
+extern void get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel,
+						  ParamPathInfo *param_info,
+						  QualCost *qpqual_cost);
 extern void compute_semi_anti_join_factors(PlannerInfo *root,
 							   RelOptInfo *joinrel,
 							   RelOptInfo *outerrel,
@@ -198,6 +201,7 @@ extern void set_tablefunc_size_estimates(PlannerInfo *root, RelOptInfo *rel);
 extern void set_namedtuplestore_size_estimates(PlannerInfo *root, RelOptInfo *rel);
 extern void set_foreign_size_estimates(PlannerInfo *root, RelOptInfo *rel);
 extern PathTarget *set_pathtarget_cost_width(PlannerInfo *root, PathTarget *target);
+extern double get_parallel_divisor(Path *path);
 extern double compute_bitmap_pages(PlannerInfo *root, RelOptInfo *baserel,
 					 Path *bitmapqual, int loop_count, Cost *cost, double *tuple);
 
