diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 5d270b9..bebf949 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -415,6 +415,9 @@ static void add_paths_with_pathkeys_for_rel(PlannerInfo *root, RelOptInfo *rel, 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 set_fdw_options_for_join_upper_rels(PgFdwRelationInfo *fpinfo); /* @@ -514,31 +517,9 @@ postgresGetForeignRelSize(PlannerInfo *root, 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); + apply_server_options(fpinfo); - 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_table_options(fpinfo); /* * If the table or the server is configured to use remote estimates, @@ -4090,6 +4071,17 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, joinclauses = NIL; } + /* + * Set the foreign server to which this join will be shipped if found safe + * to push-down. We need server specific options like extensions to decide + * push-down safety, so set FDW specific options. + */ + fpinfo->server = fpinfo_o->server; + fpinfo->outerrel = outerrel; + fpinfo->innerrel = innerrel; + fpinfo->jointype = jointype; + set_fdw_options_for_join_upper_rels(fpinfo); + /* Join quals must be safe to push down. */ foreach(lc, joinclauses) { @@ -4140,10 +4132,6 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, fpinfo->remote_conds = lappend(fpinfo->remote_conds, expr); } - fpinfo->outerrel = outerrel; - fpinfo->innerrel = innerrel; - fpinfo->jointype = jointype; - /* * Pull the other remote conditions from the joining relations into join * clauses or other remote clauses (remote_conds) of this relation @@ -4213,15 +4201,6 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, /* 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) { @@ -4233,17 +4212,6 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, 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 @@ -4253,15 +4221,6 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, 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. */ @@ -4309,6 +4268,118 @@ add_paths_with_pathkeys_for_rel(PlannerInfo *root, RelOptInfo *rel, } /* + * apply_server_options + * Parse options from foreign server any apply them to 'fpinfo' + * + * While adding code for a new option in this function, please remember to add + * code for that option in set_fdw_options_for_join_upper_rels(). + */ +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); + } +} + +/* + * apply_table_options + * Parse options from foreign table any apply them to 'fpinfo' + * + * While adding code for a new option in this function, please remember to add + * code for that option in set_fdw_options_for_join_upper_rels(). + */ +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); + } +} + +/* + * set_fdw_options_for_join_upper_rels + * Set FDW options for a join or an upper relation based on those of the + * input relations. + * + * For a join relation the inner and outer relations are set. For an upper + * relation the input relation is set as ouerrel. The both the types of + * relations values for the FDW options are copied from the outer relation. For + * a join relation, the values of table specific options are derived from the + * values of corresponding options from the joining tables. + */ +static void +set_fdw_options_for_join_upper_rels(PgFdwRelationInfo *fpinfo) +{ + RelOptInfo *outerrel = fpinfo->outerrel; + RelOptInfo *innerrel = fpinfo->innerrel; + PgFdwRelationInfo *fpinfo_o; + PgFdwRelationInfo *fpinfo_i; + ListCell *lc; + + Assert(outerrel && outerrel->fdw_private); + fpinfo_o = (PgFdwRelationInfo *) outerrel->fdw_private; + + /* Copy the server specific FDW options from the outer relation. */ + 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; + + /* + * For a join relation, derive the table specific options from the joining + * tables. + */ + if (innerrel) + { + fpinfo_i = (PgFdwRelationInfo *) innerrel->fdw_private; + + /* + * 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; + + /* + * 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; + } +} + +/* * postgresGetForeignJoinPaths * Add possible ForeignPath to joinrel, if join is safe to push down. */ @@ -4617,18 +4688,6 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel) 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 * first) of the calls to estimate_path_cost_size(). @@ -4636,9 +4695,6 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel) 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. @@ -4714,13 +4770,13 @@ add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, 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; + set_fdw_options_for_join_upper_rels(fpinfo); /* Assess if it is safe to push down aggregation and grouping. */ if (!foreign_grouping_ok(root, grouped_rel))