@@ -414,6 +414,11 @@ static void add_paths_with_pathkeys_for_rel(PlannerInfo *root, RelOptInfo *rel,
414414static void add_foreign_grouping_paths (PlannerInfo * root ,
415415 RelOptInfo * input_rel ,
416416 RelOptInfo * grouped_rel );
417+ static void apply_server_options (PgFdwRelationInfo * fpinfo );
418+ static void apply_table_options (PgFdwRelationInfo * fpinfo );
419+ static void merge_fdw_options (PgFdwRelationInfo * fpinfo ,
420+ const PgFdwRelationInfo * fpinfo_o ,
421+ const PgFdwRelationInfo * fpinfo_i );
417422
418423
419424/*
@@ -513,31 +518,8 @@ postgresGetForeignRelSize(PlannerInfo *root,
513518 fpinfo -> shippable_extensions = NIL ;
514519 fpinfo -> fetch_size = 100 ;
515520
516- foreach (lc , fpinfo -> server -> options )
517- {
518- DefElem * def = (DefElem * ) lfirst (lc );
519-
520- if (strcmp (def -> defname , "use_remote_estimate" ) == 0 )
521- fpinfo -> use_remote_estimate = defGetBoolean (def );
522- else if (strcmp (def -> defname , "fdw_startup_cost" ) == 0 )
523- fpinfo -> fdw_startup_cost = strtod (defGetString (def ), NULL );
524- else if (strcmp (def -> defname , "fdw_tuple_cost" ) == 0 )
525- fpinfo -> fdw_tuple_cost = strtod (defGetString (def ), NULL );
526- else if (strcmp (def -> defname , "extensions" ) == 0 )
527- fpinfo -> shippable_extensions =
528- ExtractExtensionList (defGetString (def ), false);
529- else if (strcmp (def -> defname , "fetch_size" ) == 0 )
530- fpinfo -> fetch_size = strtol (defGetString (def ), NULL , 10 );
531- }
532- foreach (lc , fpinfo -> table -> options )
533- {
534- DefElem * def = (DefElem * ) lfirst (lc );
535-
536- if (strcmp (def -> defname , "use_remote_estimate" ) == 0 )
537- fpinfo -> use_remote_estimate = defGetBoolean (def );
538- else if (strcmp (def -> defname , "fetch_size" ) == 0 )
539- fpinfo -> fetch_size = strtol (defGetString (def ), NULL , 10 );
540- }
521+ apply_server_options (fpinfo );
522+ apply_table_options (fpinfo );
541523
542524 /*
543525 * If the table or the server is configured to use remote estimates,
@@ -4114,6 +4096,15 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
41144096 if (fpinfo_o -> local_conds || fpinfo_i -> local_conds )
41154097 return false;
41164098
4099+ /*
4100+ * Merge FDW options. We might be tempted to do this after we have deemed
4101+ * the foreign join to be OK. But we must do this beforehand so that we
4102+ * know which quals can be evaluated on the foreign server, which might
4103+ * depend on shippable_extensions.
4104+ */
4105+ fpinfo -> server = fpinfo_o -> server ;
4106+ merge_fdw_options (fpinfo , fpinfo_o , fpinfo_i );
4107+
41174108 /*
41184109 * Separate restrict list into join quals and pushed-down (other) quals.
41194110 *
@@ -4279,15 +4270,6 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
42794270 /* Mark that this join can be pushed down safely */
42804271 fpinfo -> pushdown_safe = true;
42814272
4282- /*
4283- * If user is willing to estimate cost for a scan of either of the joining
4284- * relations using EXPLAIN, he intends to estimate scans on that relation
4285- * more accurately. Then, it makes sense to estimate the cost of the join
4286- * with that relation more accurately using EXPLAIN.
4287- */
4288- fpinfo -> use_remote_estimate = fpinfo_o -> use_remote_estimate ||
4289- fpinfo_i -> use_remote_estimate ;
4290-
42914273 /* Get user mapping */
42924274 if (fpinfo -> use_remote_estimate )
42934275 {
@@ -4299,17 +4281,6 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
42994281 else
43004282 fpinfo -> user = NULL ;
43014283
4302- /* Get foreign server */
4303- fpinfo -> server = fpinfo_o -> server ;
4304-
4305- /*
4306- * Since both the joining relations come from the same server, the server
4307- * level options should have same value for both the relations. Pick from
4308- * any side.
4309- */
4310- fpinfo -> fdw_startup_cost = fpinfo_o -> fdw_startup_cost ;
4311- fpinfo -> fdw_tuple_cost = fpinfo_o -> fdw_tuple_cost ;
4312-
43134284 /*
43144285 * Set cached relation costs to some negative value, so that we can detect
43154286 * when they are set to some sensible costs, during one (usually the
@@ -4318,15 +4289,6 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
43184289 fpinfo -> rel_startup_cost = -1 ;
43194290 fpinfo -> rel_total_cost = -1 ;
43204291
4321- /*
4322- * Set fetch size to maximum of the joining sides, since we are expecting
4323- * the rows returned by the join to be proportional to the relation sizes.
4324- */
4325- if (fpinfo_o -> fetch_size > fpinfo_i -> fetch_size )
4326- fpinfo -> fetch_size = fpinfo_o -> fetch_size ;
4327- else
4328- fpinfo -> fetch_size = fpinfo_i -> fetch_size ;
4329-
43304292 /*
43314293 * Set the string describing this join relation to be used in EXPLAIN
43324294 * output of corresponding ForeignScan.
@@ -4384,6 +4346,110 @@ add_paths_with_pathkeys_for_rel(PlannerInfo *root, RelOptInfo *rel,
43844346 }
43854347}
43864348
4349+ /*
4350+ * Parse options from foreign server and apply them to fpinfo.
4351+ *
4352+ * New options might also require tweaking merge_fdw_options().
4353+ */
4354+ static void
4355+ apply_server_options (PgFdwRelationInfo * fpinfo )
4356+ {
4357+ ListCell * lc ;
4358+
4359+ foreach (lc , fpinfo -> server -> options )
4360+ {
4361+ DefElem * def = (DefElem * ) lfirst (lc );
4362+
4363+ if (strcmp (def -> defname , "use_remote_estimate" ) == 0 )
4364+ fpinfo -> use_remote_estimate = defGetBoolean (def );
4365+ else if (strcmp (def -> defname , "fdw_startup_cost" ) == 0 )
4366+ fpinfo -> fdw_startup_cost = strtod (defGetString (def ), NULL );
4367+ else if (strcmp (def -> defname , "fdw_tuple_cost" ) == 0 )
4368+ fpinfo -> fdw_tuple_cost = strtod (defGetString (def ), NULL );
4369+ else if (strcmp (def -> defname , "extensions" ) == 0 )
4370+ fpinfo -> shippable_extensions =
4371+ ExtractExtensionList (defGetString (def ), false);
4372+ else if (strcmp (def -> defname , "fetch_size" ) == 0 )
4373+ fpinfo -> fetch_size = strtol (defGetString (def ), NULL , 10 );
4374+ }
4375+ }
4376+
4377+ /*
4378+ * Parse options from foreign table and apply them to fpinfo.
4379+ *
4380+ * New options might also require tweaking merge_fdw_options().
4381+ */
4382+ static void
4383+ apply_table_options (PgFdwRelationInfo * fpinfo )
4384+ {
4385+ ListCell * lc ;
4386+
4387+ foreach (lc , fpinfo -> table -> options )
4388+ {
4389+ DefElem * def = (DefElem * ) lfirst (lc );
4390+
4391+ if (strcmp (def -> defname , "use_remote_estimate" ) == 0 )
4392+ fpinfo -> use_remote_estimate = defGetBoolean (def );
4393+ else if (strcmp (def -> defname , "fetch_size" ) == 0 )
4394+ fpinfo -> fetch_size = strtol (defGetString (def ), NULL , 10 );
4395+ }
4396+ }
4397+
4398+ /*
4399+ * Merge FDW options from input relations into a new set of options for a join
4400+ * or an upper rel.
4401+ *
4402+ * For a join relation, FDW-specific information about the inner and outer
4403+ * relations is provided using fpinfo_i and fpinfo_o. For an upper relation,
4404+ * fpinfo_o provides the information for the input relation; fpinfo_i is
4405+ * expected to NULL.
4406+ */
4407+ static void
4408+ merge_fdw_options (PgFdwRelationInfo * fpinfo ,
4409+ const PgFdwRelationInfo * fpinfo_o ,
4410+ const PgFdwRelationInfo * fpinfo_i )
4411+ {
4412+ /* We must always have fpinfo_o. */
4413+ Assert (fpinfo_o );
4414+
4415+ /* fpinfo_i may be NULL, but if present the servers must both match. */
4416+ Assert (!fpinfo_i ||
4417+ fpinfo_i -> server -> serverid == fpinfo_o -> server -> serverid );
4418+
4419+ /*
4420+ * Copy the server specific FDW options. (For a join, both relations come
4421+ * from the same server, so the server options should have the same value
4422+ * for both relations.)
4423+ */
4424+ fpinfo -> fdw_startup_cost = fpinfo_o -> fdw_startup_cost ;
4425+ fpinfo -> fdw_tuple_cost = fpinfo_o -> fdw_tuple_cost ;
4426+ fpinfo -> shippable_extensions = fpinfo_o -> shippable_extensions ;
4427+ fpinfo -> use_remote_estimate = fpinfo_o -> use_remote_estimate ;
4428+ fpinfo -> fetch_size = fpinfo_o -> fetch_size ;
4429+
4430+ /* Merge the table level options from either side of the join. */
4431+ if (fpinfo_i )
4432+ {
4433+ /*
4434+ * We'll prefer to use remote estimates for this join if any table
4435+ * from either side of the join is using remote estimates. This is
4436+ * most likely going to be preferred since they're already willing to
4437+ * pay the price of a round trip to get the remote EXPLAIN. In any
4438+ * case it's not entirely clear how we might otherwise handle this
4439+ * best.
4440+ */
4441+ fpinfo -> use_remote_estimate = fpinfo_o -> use_remote_estimate ||
4442+ fpinfo_i -> use_remote_estimate ;
4443+
4444+ /*
4445+ * Set fetch size to maximum of the joining sides, since we are
4446+ * expecting the rows returned by the join to be proportional to the
4447+ * relation sizes.
4448+ */
4449+ fpinfo -> fetch_size = Max (fpinfo_o -> fetch_size , fpinfo_i -> fetch_size );
4450+ }
4451+ }
4452+
43874453/*
43884454 * postgresGetForeignJoinPaths
43894455 * Add possible ForeignPath to joinrel, if join is safe to push down.
@@ -4714,18 +4780,6 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel)
47144780 /* Safe to pushdown */
47154781 fpinfo -> pushdown_safe = true;
47164782
4717- /*
4718- * If user is willing to estimate cost for a scan using EXPLAIN, he
4719- * intends to estimate scans on that relation more accurately. Then, it
4720- * makes sense to estimate the cost of the grouping on that relation more
4721- * accurately using EXPLAIN.
4722- */
4723- fpinfo -> use_remote_estimate = ofpinfo -> use_remote_estimate ;
4724-
4725- /* Copy startup and tuple cost as is from underneath input rel's fpinfo */
4726- fpinfo -> fdw_startup_cost = ofpinfo -> fdw_startup_cost ;
4727- fpinfo -> fdw_tuple_cost = ofpinfo -> fdw_tuple_cost ;
4728-
47294783 /*
47304784 * Set cached relation costs to some negative value, so that we can detect
47314785 * when they are set to some sensible costs, during one (usually the
@@ -4734,9 +4788,6 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel)
47344788 fpinfo -> rel_startup_cost = -1 ;
47354789 fpinfo -> rel_total_cost = -1 ;
47364790
4737- /* Set fetch size same as that of underneath input rel's fpinfo */
4738- fpinfo -> fetch_size = ofpinfo -> fetch_size ;
4739-
47404791 /*
47414792 * Set the string describing this grouped relation to be used in EXPLAIN
47424793 * output of corresponding ForeignScan.
@@ -4812,13 +4863,13 @@ add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
48124863 fpinfo -> outerrel = input_rel ;
48134864
48144865 /*
4815- * Copy foreign table, foreign server, user mapping, shippable extensions
4816- * etc. details from the input relation's fpinfo.
4866+ * Copy foreign table, foreign server, user mapping, FDW options etc.
4867+ * details from the input relation's fpinfo.
48174868 */
48184869 fpinfo -> table = ifpinfo -> table ;
48194870 fpinfo -> server = ifpinfo -> server ;
48204871 fpinfo -> user = ifpinfo -> user ;
4821- fpinfo -> shippable_extensions = ifpinfo -> shippable_extensions ;
4872+ merge_fdw_options ( fpinfo , ifpinfo , NULL ) ;
48224873
48234874 /* Assess if it is safe to push down aggregation and grouping. */
48244875 if (!foreign_grouping_ok (root , grouped_rel ))
0 commit comments