aboutsummaryrefslogtreecommitdiff
path: root/contrib/postgres_fdw/postgres_fdw.c
diff options
context:
space:
mode:
authorRobert Haas <rhaas@postgresql.org>2016-03-09 10:51:49 -0500
committerRobert Haas <rhaas@postgresql.org>2016-03-09 10:51:49 -0500
commitaa09cd242fa7e3a694a31f8aed521e80d1e626a4 (patch)
treef59e3056c8fc5d461c89e906a5aa74eb167ff926 /contrib/postgres_fdw/postgres_fdw.c
parentd31f20e2b5a246f276c73134b610ac7a2f34e274 (diff)
downloadpostgresql-aa09cd242fa7e3a694a31f8aed521e80d1e626a4.tar.gz
postgresql-aa09cd242fa7e3a694a31f8aed521e80d1e626a4.zip
postgres_fdw: Consider foreign joining and foreign sorting together.
Commit ccd8f97922944566d26c7d90eb67ab7848ee9905 gave us the ability to request that the remote side sort the data, and, later, commit e4106b2528727c4b48639c0e12bf2f70a766b910 gave us the ability to request that the remote side perform the join for us rather than doing it locally. But we could not do both things at the same time: a remote SQL query that had an ORDER BY clause would never be a join. This commit adds that capability. Ashutosh Bapat, reviewed by me.
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