Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 12cb7ea

Browse files
committed
Repair bogus EPQ plans generated for postgres_fdw foreign joins.
postgres_fdw's postgresGetForeignPlan() assumes without checking that the outer_plan it's given for a join relation must have a NestLoop, MergeJoin, or HashJoin node at the top. That's been wrong at least since commit 4bbf6ed (which could cause insertion of a Sort node on top) and it seems like a pretty unsafe thing to Just Assume even without that. Through blind good fortune, this doesn't seem to have any worse consequences today than strange EXPLAIN output, but it's clearly trouble waiting to happen. To fix, test the node type explicitly before touching Join-specific fields, and avoid jamming the new tlist into a node type that can't do projection. Export a new support function from createplan.c to avoid building low-level knowledge about the latter into FDWs. Back-patch to 9.6 where the faulty coding was added. Note that the associated regression test cases don't show any changes before v11, apparently because the tests back-patched with 4bbf6ed don't actually exercise the problem case before then (there's no top-level Sort in those plans). Discussion: https://postgr.es/m/8946.1544644803@sss.pgh.pa.us
1 parent 10bad8c commit 12cb7ea

File tree

3 files changed

+65
-24
lines changed

3 files changed

+65
-24
lines changed

contrib/postgres_fdw/postgres_fdw.c

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,33 +1181,50 @@ postgresGetForeignPlan(PlannerInfo *root,
11811181

11821182
/*
11831183
* Ensure that the outer plan produces a tuple whose descriptor
1184-
* matches our scan tuple slot. This is safe because all scans and
1185-
* joins support projection, so we never need to insert a Result node.
1186-
* Also, remove the local conditions from outer plan's quals, lest
1187-
* they will be evaluated twice, once by the local plan and once by
1188-
* the scan.
1184+
* matches our scan tuple slot. Also, remove the local conditions
1185+
* from outer plan's quals, lest they be evaluated twice, once by the
1186+
* local plan and once by the scan.
11891187
*/
11901188
if (outer_plan)
11911189
{
11921190
ListCell *lc;
11931191

1194-
outer_plan->targetlist = fdw_scan_tlist;
1195-
1192+
/*
1193+
* First, update the plan's qual list if possible. In some cases
1194+
* the quals might be enforced below the topmost plan level, in
1195+
* which case we'll fail to remove them; it's not worth working
1196+
* harder than this.
1197+
*/
11961198
foreach(lc, local_exprs)
11971199
{
1198-
Join *join_plan = (Join *) outer_plan;
11991200
Node *qual = lfirst(lc);
12001201

12011202
outer_plan->qual = list_delete(outer_plan->qual, qual);
12021203

12031204
/*
12041205
* For an inner join the local conditions of foreign scan plan
1205-
* can be part of the joinquals as well.
1206+
* can be part of the joinquals as well. (They might also be
1207+
* in the mergequals or hashquals, but we can't touch those
1208+
* without breaking the plan.)
12061209
*/
1207-
if (join_plan->jointype == JOIN_INNER)
1208-
join_plan->joinqual = list_delete(join_plan->joinqual,
1209-
qual);
1210+
if (IsA(outer_plan, NestLoop) ||
1211+
IsA(outer_plan, MergeJoin) ||
1212+
IsA(outer_plan, HashJoin))
1213+
{
1214+
Join *join_plan = (Join *) outer_plan;
1215+
1216+
if (join_plan->jointype == JOIN_INNER)
1217+
join_plan->joinqual = list_delete(join_plan->joinqual,
1218+
qual);
1219+
}
12101220
}
1221+
1222+
/*
1223+
* Now fix the subplan's tlist --- this might result in inserting
1224+
* a Result node atop the plan tree.
1225+
*/
1226+
outer_plan = change_plan_targetlist(outer_plan, fdw_scan_tlist,
1227+
best_path->path.parallel_safe);
12111228
}
12121229
}
12131230

src/backend/optimizer/plan/createplan.c

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,19 +1252,10 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
12521252
}
12531253
}
12541254

1255+
/* Use change_plan_targetlist in case we need to insert a Result node */
12551256
if (newitems || best_path->umethod == UNIQUE_PATH_SORT)
1256-
{
1257-
/*
1258-
* If the top plan node can't do projections and its existing target
1259-
* list isn't already what we need, we need to add a Result node to
1260-
* help it along.
1261-
*/
1262-
if (!is_projection_capable_plan(subplan) &&
1263-
!tlist_same_exprs(newtlist, subplan->targetlist))
1264-
subplan = inject_projection_plan(subplan, newtlist);
1265-
else
1266-
subplan->targetlist = newtlist;
1267-
}
1257+
subplan = change_plan_targetlist(subplan, newtlist,
1258+
best_path->path.parallel_safe);
12681259

12691260
/*
12701261
* Build control information showing which subplan output columns are to
@@ -1505,6 +1496,37 @@ inject_projection_plan(Plan *subplan, List *tlist)
15051496
return plan;
15061497
}
15071498

1499+
/*
1500+
* change_plan_targetlist
1501+
* Externally available wrapper for inject_projection_plan.
1502+
*
1503+
* This is meant for use by FDW plan-generation functions, which might
1504+
* want to adjust the tlist computed by some subplan tree. In general,
1505+
* a Result node is needed to compute the new tlist, but we can optimize
1506+
* some cases.
1507+
*
1508+
* In most cases, tlist_parallel_safe can just be passed as the parallel_safe
1509+
* flag of the FDW's own Path node. (It's not actually used in this branch.)
1510+
*/
1511+
Plan *
1512+
change_plan_targetlist(Plan *subplan, List *tlist, bool tlist_parallel_safe)
1513+
{
1514+
/*
1515+
* If the top plan node can't do projections and its existing target list
1516+
* isn't already what we need, we need to add a Result node to help it
1517+
* along.
1518+
*/
1519+
if (!is_projection_capable_plan(subplan) &&
1520+
!tlist_same_exprs(tlist, subplan->targetlist))
1521+
subplan = inject_projection_plan(subplan, tlist);
1522+
else
1523+
{
1524+
/* Else we can just replace the plan node's tlist */
1525+
subplan->targetlist = tlist;
1526+
}
1527+
return subplan;
1528+
}
1529+
15081530
/*
15091531
* create_sort_plan
15101532
*

src/include/optimizer/planmain.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ extern ForeignScan *make_foreignscan(List *qptlist, List *qpqual,
5252
Index scanrelid, List *fdw_exprs, List *fdw_private,
5353
List *fdw_scan_tlist, List *fdw_recheck_quals,
5454
Plan *outer_plan);
55+
extern Plan *change_plan_targetlist(Plan *subplan, List *tlist,
56+
bool tlist_parallel_safe);
5557
extern Plan *materialize_finished_plan(Plan *subplan);
5658
extern bool is_projection_capable_path(Path *path);
5759
extern bool is_projection_capable_plan(Plan *plan);

0 commit comments

Comments
 (0)