diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 2b6d885efe..71f7d85654 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -661,12 +661,13 @@ postgresGetForeignRelSize(PlannerInfo *root, cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root); /* - * Set cached relation costs to some negative value, so that we can detect - * when they are set to some sensible costs during one (usually the first) - * of the calls to estimate_path_cost_size(). + * Set cached relation costs and # of retrieved rows to some negative + * value, so that we can detect when they are set to some sensible values, + * during one (usually the first) of the calls to estimate_path_cost_size. */ fpinfo->rel_startup_cost = -1; fpinfo->rel_total_cost = -1; + fpinfo->retrieved_rows = -1; /* * If the table or the server is configured to use remote estimates, @@ -2616,7 +2617,6 @@ estimate_path_cost_size(PlannerInfo *root, int width; Cost startup_cost; Cost total_cost; - Cost cpu_per_tuple; /* Make sure the core code has set up the relation's reltarget */ Assert(foreignrel->reltarget); @@ -2729,26 +2729,20 @@ estimate_path_cost_size(PlannerInfo *root, */ Assert(param_join_conds == NIL); - /* - * Use rows/width estimates made by set_baserel_size_estimates() for - * base foreign relations and set_joinrel_size_estimates() for join - * between foreign relations. - */ - rows = foreignrel->rows; - width = foreignrel->reltarget->width; - - /* Back into an estimate of the number of retrieved rows. */ - retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel); - /* * We will come here again and again with different set of pathkeys or * additional post-scan/join-processing steps that caller wants to - * cost. We don't need to calculate the costs of the underlying scan, - * join, or grouping each time. Instead, use the costs if we have - * cached them already. + * cost. We don't need to calculate the cost/size estimates for the + * underlying scan, join, or grouping each time. Instead, use those + * estimates if we have cached them already. */ if (fpinfo->rel_startup_cost >= 0 && fpinfo->rel_total_cost >= 0) { + Assert(fpinfo->retrieved_rows >= 0); + + rows = fpinfo->rows; + retrieved_rows = fpinfo->retrieved_rows; + width = fpinfo->width; startup_cost = fpinfo->rel_startup_cost; run_cost = fpinfo->rel_total_cost - fpinfo->rel_startup_cost; @@ -2778,6 +2772,10 @@ estimate_path_cost_size(PlannerInfo *root, QualCost remote_conds_cost; double nrows; + /* Use rows/width estimates made by the core code. */ + rows = foreignrel->rows; + width = foreignrel->reltarget->width; + /* For join we expect inner and outer relations set */ Assert(fpinfo->innerrel && fpinfo->outerrel); @@ -2786,7 +2784,12 @@ estimate_path_cost_size(PlannerInfo *root, /* Estimate of number of rows in cross product */ nrows = fpinfo_i->rows * fpinfo_o->rows; - /* Clamp retrieved rows estimate to at most size of cross product */ + + /* + * Back into an estimate of the number of retrieved rows. Just in + * case this is nuts, clamp to at most nrow. + */ + retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel); retrieved_rows = Min(retrieved_rows, nrows); /* @@ -2864,9 +2867,8 @@ estimate_path_cost_size(PlannerInfo *root, ofpinfo = (PgFdwRelationInfo *) outerrel->fdw_private; - /* Get rows and width from input rel */ + /* Get rows from input rel */ input_rows = ofpinfo->rows; - width = ofpinfo->width; /* Collect statistics about aggregates for estimating costs. */ MemSet(&aggcosts, 0, sizeof(AggClauseCosts)); @@ -2913,6 +2915,9 @@ estimate_path_cost_size(PlannerInfo *root, rows = retrieved_rows = numGroups; } + /* Use width estimates made by the core code. */ + width = foreignrel->reltarget->width; + /*----- * Startup cost includes: * 1. Startup cost for underneath input relation, adjusted for @@ -2959,7 +2964,17 @@ estimate_path_cost_size(PlannerInfo *root, } else { - /* Clamp retrieved rows estimates to at most foreignrel->tuples. */ + Cost cpu_per_tuple; + + /* Use rows/width estimates made by set_baserel_size_estimates. */ + rows = foreignrel->rows; + width = foreignrel->reltarget->width; + + /* + * Back into an estimate of the number of retrieved rows. Just in + * case this is nuts, clamp to at most foreignrel->tuples. + */ + retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel); retrieved_rows = Min(retrieved_rows, foreignrel->tuples); /* @@ -3036,20 +3051,22 @@ estimate_path_cost_size(PlannerInfo *root, } /* - * Cache the costs for scans, joins, or groupings without any - * parameterization, pathkeys, or additional post-scan/join-processing - * steps, before adding the costs for transferring data from the foreign - * server. These costs are useful for costing remote joins involving this - * relation or costing other remote operations for this relation such as - * remote sorts and remote LIMIT restrictions, when the costs can not be - * obtained from the foreign server. This function will be called at - * least once for every foreign relation without any parameterization, - * pathkeys, or additional post-scan/join-processing steps. + * Cache the cost and retrieved rows estimates for scans, joins, or + * groupings without any parameterization, pathkeys, or additional + * post-scan/join-processing steps, before adding the costs for + * transferring data from the foreign server. These estimates are useful + * for costing remote joins involving this relation or costing other + * remote operations on this relation such as remote sorts and remote + * LIMIT restrictions, when the costs can not be obtained from the foreign + * server. This function will be called at least once for every foreign + * relation without any parameterization, pathkeys, or additional + * post-scan/join-processing steps. */ if (pathkeys == NIL && param_join_conds == NIL && fpextra == NULL) { fpinfo->rel_startup_cost = startup_cost; fpinfo->rel_total_cost = total_cost; + fpinfo->retrieved_rows = retrieved_rows; } /* @@ -5149,12 +5166,13 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, fpinfo->user = NULL; /* - * Set cached relation costs to some negative value, so that we can detect - * when they are set to some sensible costs, during one (usually the - * first) of the calls to estimate_path_cost_size(). + * Set cached relation costs and # of retrieved rows to some negative + * value, so that we can detect when they are set to some sensible values, + * during one (usually the first) of the calls to estimate_path_cost_size. */ fpinfo->rel_startup_cost = -1; fpinfo->rel_total_cost = -1; + fpinfo->retrieved_rows = -1; /* * Set the string describing this join relation to be used in EXPLAIN @@ -5700,12 +5718,13 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, fpinfo->pushdown_safe = true; /* - * Set cached relation costs to some negative value, so that we can detect - * when they are set to some sensible costs, during one (usually the - * first) of the calls to estimate_path_cost_size(). + * Set cached relation costs and # of retrieved rows to some negative + * value, so that we can detect when they are set to some sensible values, + * during one (usually the first) of the calls to estimate_path_cost_size. */ fpinfo->rel_startup_cost = -1; fpinfo->rel_total_cost = -1; + fpinfo->retrieved_rows = -1; /* * Set the string describing this grouped relation to be used in EXPLAIN @@ -5845,8 +5864,6 @@ add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, fpinfo->startup_cost = startup_cost; fpinfo->total_cost = total_cost; - grouped_rel->rows = fpinfo->rows; - /* Create and add foreign path to the grouping relation. */ grouppath = create_foreign_upper_path(root, grouped_rel, diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h index 3e4603d718..1d813457b0 100644 --- a/contrib/postgres_fdw/postgres_fdw.h +++ b/contrib/postgres_fdw/postgres_fdw.h @@ -59,14 +59,19 @@ typedef struct PgFdwRelationInfo /* Selectivity of join conditions */ Selectivity joinclause_sel; - /* Estimated size and cost for a scan or join. */ + /* Estimated size and cost for a scan, join, or grouping. */ double rows; int width; Cost startup_cost; Cost total_cost; - /* Costs excluding costs for transferring data from the foreign server */ + /* + * Costs excluding costs for transferring data from the foreign server, + * and estimated number of rows fetched from the foreign server. These + * are only used by estimate_path_cost_size(). + */ Cost rel_startup_cost; Cost rel_total_cost; + double retrieved_rows; /* Options extracted from catalogs. */ bool use_remote_estimate;