static void add_foreign_grouping_paths(PlannerInfo *root,
RelOptInfo *input_rel,
RelOptInfo *grouped_rel);
+static void apply_server_options(PgFdwRelationInfo *fpinfo);
+static void apply_table_options(PgFdwRelationInfo *fpinfo);
+static void merge_fdw_options(PgFdwRelationInfo *fpinfo,
+ const PgFdwRelationInfo *fpinfo_o,
+ const PgFdwRelationInfo *fpinfo_i);
/*
fpinfo->shippable_extensions = NIL;
fpinfo->fetch_size = 100;
- foreach(lc, fpinfo->server->options)
- {
- DefElem *def = (DefElem *) lfirst(lc);
-
- if (strcmp(def->defname, "use_remote_estimate") == 0)
- fpinfo->use_remote_estimate = defGetBoolean(def);
- else if (strcmp(def->defname, "fdw_startup_cost") == 0)
- fpinfo->fdw_startup_cost = strtod(defGetString(def), NULL);
- else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
- fpinfo->fdw_tuple_cost = strtod(defGetString(def), NULL);
- else if (strcmp(def->defname, "extensions") == 0)
- fpinfo->shippable_extensions =
- ExtractExtensionList(defGetString(def), false);
- else if (strcmp(def->defname, "fetch_size") == 0)
- fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
- }
- foreach(lc, fpinfo->table->options)
- {
- DefElem *def = (DefElem *) lfirst(lc);
-
- if (strcmp(def->defname, "use_remote_estimate") == 0)
- fpinfo->use_remote_estimate = defGetBoolean(def);
- else if (strcmp(def->defname, "fetch_size") == 0)
- fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
- }
+ apply_server_options(fpinfo);
+ apply_table_options(fpinfo);
/*
* If the table or the server is configured to use remote estimates,
if (fpinfo_o->local_conds || fpinfo_i->local_conds)
return false;
+ /*
+ * Merge FDW options. We might be tempted to do this after we have deemed
+ * the foreign join to be OK. But we must do this beforehand so that we
+ * know which quals can be evaluated on the foreign server, which might
+ * depend on shippable_extensions.
+ */
+ fpinfo->server = fpinfo_o->server;
+ merge_fdw_options(fpinfo, fpinfo_o, fpinfo_i);
+
/*
* Separate restrict list into join quals and pushed-down (other) quals.
*
/* Mark that this join can be pushed down safely */
fpinfo->pushdown_safe = true;
- /*
- * If user is willing to estimate cost for a scan of either of the joining
- * relations using EXPLAIN, he intends to estimate scans on that relation
- * more accurately. Then, it makes sense to estimate the cost of the join
- * with that relation more accurately using EXPLAIN.
- */
- fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate ||
- fpinfo_i->use_remote_estimate;
-
/* Get user mapping */
if (fpinfo->use_remote_estimate)
{
else
fpinfo->user = NULL;
- /* Get foreign server */
- fpinfo->server = fpinfo_o->server;
-
- /*
- * Since both the joining relations come from the same server, the server
- * level options should have same value for both the relations. Pick from
- * any side.
- */
- 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
fpinfo->rel_startup_cost = -1;
fpinfo->rel_total_cost = -1;
- /*
- * Set fetch size to maximum of the joining sides, since we are expecting
- * the rows returned by the join to be proportional to the relation sizes.
- */
- if (fpinfo_o->fetch_size > fpinfo_i->fetch_size)
- fpinfo->fetch_size = fpinfo_o->fetch_size;
- else
- fpinfo->fetch_size = fpinfo_i->fetch_size;
-
/*
* Set the string describing this join relation to be used in EXPLAIN
* output of corresponding ForeignScan.
}
}
+/*
+ * Parse options from foreign server and apply them to fpinfo.
+ *
+ * New options might also require tweaking merge_fdw_options().
+ */
+static void
+apply_server_options(PgFdwRelationInfo *fpinfo)
+{
+ ListCell *lc;
+
+ foreach(lc, fpinfo->server->options)
+ {
+ DefElem *def = (DefElem *) lfirst(lc);
+
+ if (strcmp(def->defname, "use_remote_estimate") == 0)
+ fpinfo->use_remote_estimate = defGetBoolean(def);
+ else if (strcmp(def->defname, "fdw_startup_cost") == 0)
+ fpinfo->fdw_startup_cost = strtod(defGetString(def), NULL);
+ else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
+ fpinfo->fdw_tuple_cost = strtod(defGetString(def), NULL);
+ else if (strcmp(def->defname, "extensions") == 0)
+ fpinfo->shippable_extensions =
+ ExtractExtensionList(defGetString(def), false);
+ else if (strcmp(def->defname, "fetch_size") == 0)
+ fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
+ }
+}
+
+/*
+ * Parse options from foreign table and apply them to fpinfo.
+ *
+ * New options might also require tweaking merge_fdw_options().
+ */
+static void
+apply_table_options(PgFdwRelationInfo *fpinfo)
+{
+ ListCell *lc;
+
+ foreach(lc, fpinfo->table->options)
+ {
+ DefElem *def = (DefElem *) lfirst(lc);
+
+ if (strcmp(def->defname, "use_remote_estimate") == 0)
+ fpinfo->use_remote_estimate = defGetBoolean(def);
+ else if (strcmp(def->defname, "fetch_size") == 0)
+ fpinfo->fetch_size = strtol(defGetString(def), NULL, 10);
+ }
+}
+
+/*
+ * Merge FDW options from input relations into a new set of options for a join
+ * or an upper rel.
+ *
+ * For a join relation, FDW-specific information about the inner and outer
+ * relations is provided using fpinfo_i and fpinfo_o. For an upper relation,
+ * fpinfo_o provides the information for the input relation; fpinfo_i is
+ * expected to NULL.
+ */
+static void
+merge_fdw_options(PgFdwRelationInfo *fpinfo,
+ const PgFdwRelationInfo *fpinfo_o,
+ const PgFdwRelationInfo *fpinfo_i)
+{
+ /* We must always have fpinfo_o. */
+ Assert(fpinfo_o);
+
+ /* fpinfo_i may be NULL, but if present the servers must both match. */
+ Assert(!fpinfo_i ||
+ fpinfo_i->server->serverid == fpinfo_o->server->serverid);
+
+ /*
+ * Copy the server specific FDW options. (For a join, both relations come
+ * from the same server, so the server options should have the same value
+ * for both relations.)
+ */
+ fpinfo->fdw_startup_cost = fpinfo_o->fdw_startup_cost;
+ fpinfo->fdw_tuple_cost = fpinfo_o->fdw_tuple_cost;
+ fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
+ fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
+ fpinfo->fetch_size = fpinfo_o->fetch_size;
+
+ /* Merge the table level options from either side of the join. */
+ if (fpinfo_i)
+ {
+ /*
+ * We'll prefer to use remote estimates for this join if any table
+ * from either side of the join is using remote estimates. This is
+ * most likely going to be preferred since they're already willing to
+ * pay the price of a round trip to get the remote EXPLAIN. In any
+ * case it's not entirely clear how we might otherwise handle this
+ * best.
+ */
+ fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate ||
+ fpinfo_i->use_remote_estimate;
+
+ /*
+ * Set fetch size to maximum of the joining sides, since we are
+ * expecting the rows returned by the join to be proportional to the
+ * relation sizes.
+ */
+ fpinfo->fetch_size = Max(fpinfo_o->fetch_size, fpinfo_i->fetch_size);
+ }
+}
+
/*
* postgresGetForeignJoinPaths
* Add possible ForeignPath to joinrel, if join is safe to push down.
/* Safe to pushdown */
fpinfo->pushdown_safe = true;
- /*
- * If user is willing to estimate cost for a scan using EXPLAIN, he
- * intends to estimate scans on that relation more accurately. Then, it
- * makes sense to estimate the cost of the grouping on that relation more
- * accurately using EXPLAIN.
- */
- fpinfo->use_remote_estimate = ofpinfo->use_remote_estimate;
-
- /* Copy startup and tuple cost as is from underneath input rel's fpinfo */
- fpinfo->fdw_startup_cost = ofpinfo->fdw_startup_cost;
- fpinfo->fdw_tuple_cost = ofpinfo->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
fpinfo->rel_startup_cost = -1;
fpinfo->rel_total_cost = -1;
- /* Set fetch size same as that of underneath input rel's fpinfo */
- fpinfo->fetch_size = ofpinfo->fetch_size;
-
/*
* Set the string describing this grouped relation to be used in EXPLAIN
* output of corresponding ForeignScan.
fpinfo->outerrel = input_rel;
/*
- * Copy foreign table, foreign server, user mapping, shippable extensions
- * etc. details from the input relation's fpinfo.
+ * Copy foreign table, foreign server, user mapping, FDW options etc.
+ * details from the input relation's fpinfo.
*/
fpinfo->table = ifpinfo->table;
fpinfo->server = ifpinfo->server;
fpinfo->user = ifpinfo->user;
- fpinfo->shippable_extensions = ifpinfo->shippable_extensions;
+ merge_fdw_options(fpinfo, ifpinfo , NULL);
/* Assess if it is safe to push down aggregation and grouping. */
if (!foreign_grouping_ok(root, grouped_rel))