@@ -414,6 +414,11 @@ static void add_paths_with_pathkeys_for_rel(PlannerInfo *root, RelOptInfo *rel,
414
414
static void add_foreign_grouping_paths (PlannerInfo * root ,
415
415
RelOptInfo * input_rel ,
416
416
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 );
417
422
418
423
419
424
/*
@@ -513,31 +518,8 @@ postgresGetForeignRelSize(PlannerInfo *root,
513
518
fpinfo -> shippable_extensions = NIL ;
514
519
fpinfo -> fetch_size = 100 ;
515
520
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 );
541
523
542
524
/*
543
525
* 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,
4114
4096
if (fpinfo_o -> local_conds || fpinfo_i -> local_conds )
4115
4097
return false;
4116
4098
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
+
4117
4108
/*
4118
4109
* Separate restrict list into join quals and pushed-down (other) quals.
4119
4110
*
@@ -4279,15 +4270,6 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
4279
4270
/* Mark that this join can be pushed down safely */
4280
4271
fpinfo -> pushdown_safe = true;
4281
4272
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
-
4291
4273
/* Get user mapping */
4292
4274
if (fpinfo -> use_remote_estimate )
4293
4275
{
@@ -4299,17 +4281,6 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
4299
4281
else
4300
4282
fpinfo -> user = NULL ;
4301
4283
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
-
4313
4284
/*
4314
4285
* Set cached relation costs to some negative value, so that we can detect
4315
4286
* 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,
4318
4289
fpinfo -> rel_startup_cost = -1 ;
4319
4290
fpinfo -> rel_total_cost = -1 ;
4320
4291
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
-
4330
4292
/*
4331
4293
* Set the string describing this join relation to be used in EXPLAIN
4332
4294
* output of corresponding ForeignScan.
@@ -4384,6 +4346,110 @@ add_paths_with_pathkeys_for_rel(PlannerInfo *root, RelOptInfo *rel,
4384
4346
}
4385
4347
}
4386
4348
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
+
4387
4453
/*
4388
4454
* postgresGetForeignJoinPaths
4389
4455
* Add possible ForeignPath to joinrel, if join is safe to push down.
@@ -4714,18 +4780,6 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel)
4714
4780
/* Safe to pushdown */
4715
4781
fpinfo -> pushdown_safe = true;
4716
4782
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
-
4729
4783
/*
4730
4784
* Set cached relation costs to some negative value, so that we can detect
4731
4785
* when they are set to some sensible costs, during one (usually the
@@ -4734,9 +4788,6 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel)
4734
4788
fpinfo -> rel_startup_cost = -1 ;
4735
4789
fpinfo -> rel_total_cost = -1 ;
4736
4790
4737
- /* Set fetch size same as that of underneath input rel's fpinfo */
4738
- fpinfo -> fetch_size = ofpinfo -> fetch_size ;
4739
-
4740
4791
/*
4741
4792
* Set the string describing this grouped relation to be used in EXPLAIN
4742
4793
* output of corresponding ForeignScan.
@@ -4812,13 +4863,13 @@ add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
4812
4863
fpinfo -> outerrel = input_rel ;
4813
4864
4814
4865
/*
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.
4817
4868
*/
4818
4869
fpinfo -> table = ifpinfo -> table ;
4819
4870
fpinfo -> server = ifpinfo -> server ;
4820
4871
fpinfo -> user = ifpinfo -> user ;
4821
- fpinfo -> shippable_extensions = ifpinfo -> shippable_extensions ;
4872
+ merge_fdw_options ( fpinfo , ifpinfo , NULL ) ;
4822
4873
4823
4874
/* Assess if it is safe to push down aggregation and grouping. */
4824
4875
if (!foreign_grouping_ok (root , grouped_rel ))
0 commit comments