diff options
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/path/allpaths.c | 49 | ||||
-rw-r--r-- | src/backend/optimizer/path/costsize.c | 67 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 69 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 15 | ||||
-rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 11 | ||||
-rw-r--r-- | src/backend/optimizer/plan/subselect.c | 1 | ||||
-rw-r--r-- | src/backend/optimizer/util/pathnode.c | 22 |
7 files changed, 233 insertions, 1 deletions
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 9caca94f64b..4cd1bf65e74 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -71,6 +71,10 @@ static void set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte); static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte); +static void set_tablesample_rel_size(PlannerInfo *root, RelOptInfo *rel, + RangeTblEntry *rte); +static void set_tablesample_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, + RangeTblEntry *rte); static void set_foreign_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte); static void set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, @@ -265,6 +269,11 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel, /* Foreign table */ set_foreign_size(root, rel, rte); } + else if (rte->tablesample != NULL) + { + /* Sampled relation */ + set_tablesample_rel_size(root, rel, rte); + } else { /* Plain relation */ @@ -332,6 +341,11 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, /* Foreign table */ set_foreign_pathlist(root, rel, rte); } + else if (rte->tablesample != NULL) + { + /* Build sample scan on relation */ + set_tablesample_rel_pathlist(root, rel, rte); + } else { /* Plain relation */ @@ -418,6 +432,41 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) } /* + * set_tablesample_rel_size + * Set size estimates for a sampled relation. + */ +static void +set_tablesample_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) +{ + /* Mark rel with estimated output rows, width, etc */ + set_baserel_size_estimates(root, rel); +} + +/* + * set_tablesample_rel_pathlist + * Build access paths for a sampled relation + * + * There is only one possible path - sampling scan + */ +static void +set_tablesample_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) +{ + Relids required_outer; + Path *path; + + /* + * We don't support pushing join clauses into the quals of a seqscan, but + * it could still have required parameterization due to LATERAL refs in + * its tlist. + */ + required_outer = rel->lateral_relids; + + /* We only do sample scan if it was requested */ + path = create_samplescan_path(root, rel, required_outer); + rel->pathlist = list_make1(path); +} + +/* * set_foreign_size * Set size estimates for a foreign table RTE */ diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 1a0d358c5f7..c2b2b7622a6 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -220,6 +220,73 @@ cost_seqscan(Path *path, PlannerInfo *root, } /* + * cost_samplescan + * Determines and returns the cost of scanning a relation using sampling. + * + * From planner/optimizer perspective, we don't care all that much about cost + * itself since there is always only one scan path to consider when sampling + * scan is present, but number of rows estimation is still important. + * + * 'baserel' is the relation to be scanned + * 'param_info' is the ParamPathInfo if this is a parameterized path, else NULL + */ +void +cost_samplescan(Path *path, PlannerInfo *root, RelOptInfo *baserel) +{ + Cost startup_cost = 0; + Cost run_cost = 0; + double spc_seq_page_cost, + spc_random_page_cost, + spc_page_cost; + QualCost qpqual_cost; + Cost cpu_per_tuple; + BlockNumber pages; + double tuples; + RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); + TableSampleClause *tablesample = rte->tablesample; + + /* 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 (path->param_info) + path->rows = path->param_info->ppi_rows; + else + path->rows = baserel->rows; + + /* Call the sampling method's costing function. */ + OidFunctionCall6(tablesample->tsmcost, PointerGetDatum(root), + PointerGetDatum(path), PointerGetDatum(baserel), + PointerGetDatum(tablesample->args), + PointerGetDatum(&pages), PointerGetDatum(&tuples)); + + /* fetch estimated page cost for tablespace containing table */ + get_tablespace_page_costs(baserel->reltablespace, + &spc_random_page_cost, + &spc_seq_page_cost); + + + spc_page_cost = tablesample->tsmseqscan ? spc_seq_page_cost : + spc_random_page_cost; + + /* + * disk costs + */ + run_cost += spc_page_cost * pages; + + /* CPU costs */ + get_restriction_qual_cost(root, baserel, path->param_info, &qpqual_cost); + + startup_cost += qpqual_cost.startup; + cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple; + run_cost += cpu_per_tuple * tuples; + + path->startup_cost = startup_cost; + path->total_cost = startup_cost + run_cost; +} + +/* * cost_index * Determines and returns the cost of scanning a relation using an index. * diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 783e34b4fbc..c6095167e80 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -59,6 +59,8 @@ static Material *create_material_plan(PlannerInfo *root, MaterialPath *best_path static Plan *create_unique_plan(PlannerInfo *root, UniquePath *best_path); static SeqScan *create_seqscan_plan(PlannerInfo *root, Path *best_path, List *tlist, List *scan_clauses); +static SampleScan *create_samplescan_plan(PlannerInfo *root, Path *best_path, + List *tlist, List *scan_clauses); static Scan *create_indexscan_plan(PlannerInfo *root, IndexPath *best_path, List *tlist, List *scan_clauses, bool indexonly); static BitmapHeapScan *create_bitmap_scan_plan(PlannerInfo *root, @@ -101,6 +103,7 @@ static List *order_qual_clauses(PlannerInfo *root, List *clauses); static void copy_path_costsize(Plan *dest, Path *src); static void copy_plan_costsize(Plan *dest, Plan *src); static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid); +static SampleScan *make_samplescan(List *qptlist, List *qpqual, Index scanrelid); static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid, Oid indexid, List *indexqual, List *indexqualorig, List *indexorderby, List *indexorderbyorig, Oid *indexorderbyops, @@ -229,6 +232,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path) switch (best_path->pathtype) { case T_SeqScan: + case T_SampleScan: case T_IndexScan: case T_IndexOnlyScan: case T_BitmapHeapScan: @@ -344,6 +348,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path) scan_clauses); break; + case T_SampleScan: + plan = (Plan *) create_samplescan_plan(root, + best_path, + tlist, + scan_clauses); + break; + case T_IndexScan: plan = (Plan *) create_indexscan_plan(root, (IndexPath *) best_path, @@ -547,6 +558,7 @@ disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path) switch (path->pathtype) { case T_SeqScan: + case T_SampleScan: case T_IndexScan: case T_IndexOnlyScan: case T_BitmapHeapScan: @@ -1134,6 +1146,45 @@ create_seqscan_plan(PlannerInfo *root, Path *best_path, } /* + * create_samplescan_plan + * Returns a samplecan plan for the base relation scanned by 'best_path' + * with restriction clauses 'scan_clauses' and targetlist 'tlist'. + */ +static SampleScan * +create_samplescan_plan(PlannerInfo *root, Path *best_path, + List *tlist, List *scan_clauses) +{ + SampleScan *scan_plan; + Index scan_relid = best_path->parent->relid; + + /* it should be a base rel with tablesample clause... */ + Assert(scan_relid > 0); + Assert(best_path->parent->rtekind == RTE_RELATION); + Assert(best_path->pathtype == T_SampleScan); + + /* Sort clauses into best execution order */ + scan_clauses = order_qual_clauses(root, scan_clauses); + + /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */ + scan_clauses = extract_actual_clauses(scan_clauses, false); + + /* Replace any outer-relation variables with nestloop params */ + if (best_path->param_info) + { + scan_clauses = (List *) + replace_nestloop_params(root, (Node *) scan_clauses); + } + + scan_plan = make_samplescan(tlist, + scan_clauses, + scan_relid); + + copy_path_costsize(&scan_plan->plan, best_path); + + return scan_plan; +} + +/* * create_indexscan_plan * Returns an indexscan plan for the base relation scanned by 'best_path' * with restriction clauses 'scan_clauses' and targetlist 'tlist'. @@ -3378,6 +3429,24 @@ make_seqscan(List *qptlist, return node; } +static SampleScan * +make_samplescan(List *qptlist, + List *qpqual, + Index scanrelid) +{ + SampleScan *node = makeNode(SampleScan); + Plan *plan = &node->plan; + + /* cost should be inserted by caller */ + plan->targetlist = qptlist; + plan->qual = qpqual; + plan->lefttree = NULL; + plan->righttree = NULL; + node->scanrelid = scanrelid; + + return node; +} + static IndexScan * make_indexscan(List *qptlist, List *qpqual, diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 8de57c8e6bb..9ba10516bb2 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -60,6 +60,7 @@ planner_hook_type planner_hook = NULL; #define EXPRKIND_LIMIT 6 #define EXPRKIND_APPINFO 7 #define EXPRKIND_PHV 8 +#define EXPRKIND_TABLESAMPLE 9 /* Passthrough data for standard_qp_callback */ typedef struct @@ -486,7 +487,19 @@ subquery_planner(PlannerGlobal *glob, Query *parse, RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); int kind; - if (rte->rtekind == RTE_SUBQUERY) + if (rte->rtekind == RTE_RELATION) + { + if (rte->tablesample) + { + rte->tablesample->args = (List *) + preprocess_expression(root, (Node *) rte->tablesample->args, + EXPRKIND_TABLESAMPLE); + rte->tablesample->repeatable = (Node *) + preprocess_expression(root, rte->tablesample->repeatable, + EXPRKIND_TABLESAMPLE); + } + } + else if (rte->rtekind == RTE_SUBQUERY) { /* * We don't want to do all preprocessing yet on the subquery's diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 517409d28a0..73b19888369 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -451,6 +451,17 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) fix_scan_list(root, splan->plan.qual, rtoffset); } break; + case T_SampleScan: + { + SampleScan *splan = (SampleScan *) plan; + + splan->scanrelid += rtoffset; + splan->plan.targetlist = + fix_scan_list(root, splan->plan.targetlist, rtoffset); + splan->plan.qual = + fix_scan_list(root, splan->plan.qual, rtoffset); + } + break; case T_IndexScan: { IndexScan *splan = (IndexScan *) plan; diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index afccee53acf..2f7f5c0df0e 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -2167,6 +2167,7 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, break; case T_SeqScan: + case T_SampleScan: context.paramids = bms_add_members(context.paramids, scan_params); break; diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index faca30b322d..ea7a47bdf45 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -706,6 +706,26 @@ create_seqscan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer) } /* + * create_samplescan_path + * Like seqscan but uses sampling function while scanning. + */ +Path * +create_samplescan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer) +{ + Path *pathnode = makeNode(Path); + + pathnode->pathtype = T_SampleScan; + pathnode->parent = rel; + pathnode->param_info = get_baserel_parampathinfo(root, rel, + required_outer); + pathnode->pathkeys = NIL; /* samplescan has unordered result */ + + cost_samplescan(pathnode, root, rel); + + return pathnode; +} + +/* * create_index_path * Creates a path node for an index scan. * @@ -1778,6 +1798,8 @@ reparameterize_path(PlannerInfo *root, Path *path, case T_SubqueryScan: return create_subqueryscan_path(root, rel, path->pathkeys, required_outer); + case T_SampleScan: + return (Path *) create_samplescan_path(root, rel, required_outer); default: break; } |