aboutsummaryrefslogtreecommitdiff
path: root/contrib/postgres_fdw/postgres_fdw.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/postgres_fdw/postgres_fdw.c')
-rw-r--r--contrib/postgres_fdw/postgres_fdw.c120
1 files changed, 83 insertions, 37 deletions
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 76d0e158519..40bffd6f6cb 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -283,9 +283,6 @@ static void postgresGetForeignJoinPaths(PlannerInfo *root,
JoinPathExtraData *extra);
static bool postgresRecheckForeignScan(ForeignScanState *node,
TupleTableSlot *slot);
-static List *get_useful_pathkeys_for_relation(PlannerInfo *root,
- RelOptInfo *rel);
-static List *get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel);
/*
* Helper functions
@@ -331,6 +328,11 @@ static void conversion_error_callback(void *arg);
static bool foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel,
JoinType jointype, RelOptInfo *outerrel, RelOptInfo *innerrel,
JoinPathExtraData *extra);
+static List *get_useful_pathkeys_for_relation(PlannerInfo *root,
+ RelOptInfo *rel);
+static List *get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel);
+static void add_paths_with_pathkeys_for_rel(PlannerInfo *root, RelOptInfo *rel,
+ Path *epq_path);
/*
@@ -503,6 +505,14 @@ 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().
+ */
+ fpinfo->rel_startup_cost = -1;
+ fpinfo->rel_total_cost = -1;
+
+ /*
* If the table or the server is configured to use remote estimates,
* connect to the foreign server and execute EXPLAIN to estimate the
* number of rows selected by the restriction clauses, as well as the
@@ -774,7 +784,6 @@ postgresGetForeignPaths(PlannerInfo *root,
ForeignPath *path;
List *ppi_list;
ListCell *lc;
- List *useful_pathkeys_list = NIL; /* List of all pathkeys */
/*
* Create simplest ForeignScan path node and add it to baserel. This path
@@ -793,30 +802,8 @@ postgresGetForeignPaths(PlannerInfo *root,
NIL); /* no fdw_private list */
add_path(baserel, (Path *) path);
- useful_pathkeys_list = get_useful_pathkeys_for_relation(root, baserel);
-
- /* Create one path for each set of pathkeys we found above. */
- foreach(lc, useful_pathkeys_list)
- {
- double rows;
- int width;
- Cost startup_cost;
- Cost total_cost;
- List *useful_pathkeys = lfirst(lc);
-
- estimate_path_cost_size(root, baserel, NIL, useful_pathkeys,
- &rows, &width, &startup_cost, &total_cost);
-
- add_path(baserel, (Path *)
- create_foreignscan_path(root, baserel,
- rows,
- startup_cost,
- total_cost,
- useful_pathkeys,
- NULL,
- NULL,
- NIL));
- }
+ /* Add paths with pathkeys */
+ add_paths_with_pathkeys_for_rel(root, baserel, NULL);
/*
* If we're not using remote estimates, stop here. We have no way to
@@ -2182,7 +2169,18 @@ estimate_path_cost_size(PlannerInfo *root,
/* Back into an estimate of the number of retrieved rows. */
retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
- if (foreignrel->reloptkind != RELOPT_JOINREL)
+ /*
+ * We will come here again and again with different set of pathkeys
+ * that caller wants to cost. We don't need to calculate the cost of
+ * bare scan each time. Instead, use the costs if we have cached them
+ * already.
+ */
+ if (fpinfo->rel_startup_cost > 0 && fpinfo->rel_total_cost > 0)
+ {
+ startup_cost = fpinfo->rel_startup_cost;
+ run_cost = fpinfo->rel_total_cost - fpinfo->rel_startup_cost;
+ }
+ else if (foreignrel->reloptkind != RELOPT_JOINREL)
{
/* Clamp retrieved rows estimates to at most foreignrel->tuples. */
retrieved_rows = Min(retrieved_rows, foreignrel->tuples);
@@ -2284,13 +2282,19 @@ estimate_path_cost_size(PlannerInfo *root,
}
/*
- * Cache the costs prior to adding the costs for transferring data from
- * the foreign server. These costs are useful for costing the join between
- * this relation and another foreign relation, when the cost of join can
- * not be obtained from the foreign server.
+ * Cache the costs for scans without any pathkeys or parameterization
+ * before adding the costs for transferring data from the foreign server.
+ * These costs are useful for costing the join between this relation and
+ * another foreign relation or to calculate the costs of paths with
+ * pathkeys for this relation, when the costs can not be obtained from the
+ * foreign server. This function will be called at least once for every
+ * foreign relation without pathkeys and parameterization.
*/
- fpinfo->rel_startup_cost = startup_cost;
- fpinfo->rel_total_cost = total_cost;
+ if (pathkeys == NIL && param_join_conds == NIL)
+ {
+ fpinfo->rel_startup_cost = startup_cost;
+ fpinfo->rel_total_cost = total_cost;
+ }
/*
* Add some additional cost factors to account for connection overhead
@@ -3458,6 +3462,14 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
fpinfo->fdw_startup_cost = fpinfo_o->fdw_startup_cost;
fpinfo->fdw_tuple_cost = fpinfo_o->fdw_tuple_cost;
+ /*
+ * 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().
+ */
+ fpinfo->rel_startup_cost = -1;
+ fpinfo->rel_total_cost = -1;
+
/* Mark that this join can be pushed down safely */
fpinfo->pushdown_safe = true;
@@ -3532,6 +3544,39 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
return true;
}
+static void
+add_paths_with_pathkeys_for_rel(PlannerInfo *root, RelOptInfo *rel,
+ Path *epq_path)
+{
+ List *useful_pathkeys_list = NIL; /* List of all pathkeys */
+ ListCell *lc;
+
+ useful_pathkeys_list = get_useful_pathkeys_for_relation(root, rel);
+
+ /* Create one path for each set of pathkeys we found above. */
+ foreach(lc, useful_pathkeys_list)
+ {
+ double rows;
+ int width;
+ Cost startup_cost;
+ Cost total_cost;
+ List *useful_pathkeys = lfirst(lc);
+
+ estimate_path_cost_size(root, rel, NIL, useful_pathkeys,
+ &rows, &width, &startup_cost, &total_cost);
+
+ add_path(rel, (Path *)
+ create_foreignscan_path(root, rel,
+ rows,
+ startup_cost,
+ total_cost,
+ useful_pathkeys,
+ NULL,
+ epq_path,
+ NIL));
+ }
+}
+
/*
* postgresGetForeignJoinPaths
* Add possible ForeignPath to joinrel, if join is safe to push down.
@@ -3670,7 +3715,8 @@ postgresGetForeignJoinPaths(PlannerInfo *root,
/* Add generated path into joinrel by add_path(). */
add_path(joinrel, (Path *) joinpath);
- /* XXX Consider pathkeys for the join relation */
+ /* Consider pathkeys for the join relation */
+ add_paths_with_pathkeys_for_rel(root, joinrel, epq_path);
/* XXX Consider parameterized paths for the join relation */
}
@@ -3877,7 +3923,7 @@ find_em_expr_for_rel(EquivalenceClass *ec, RelOptInfo *rel)
{
EquivalenceMember *em = lfirst(lc_em);
- if (bms_equal(em->em_relids, rel->relids))
+ if (bms_is_subset(em->em_relids, rel->relids))
{
/*
* If there is more than one equivalence member whose Vars are