Skip to content

Commit 88e902b

Browse files
committed
Simplify handling of remote-qual pass-forward in postgres_fdw.
Commit 0bf3ae8 encountered a need to pass the finally chosen remote qual conditions forward from postgresGetForeignPlan to postgresPlanDirectModify. It solved that by sticking them into the plan node's fdw_private list, which in hindsight was a pretty bad idea. In the first place, there's no use for those qual trees either in EXPLAIN or execution; indeed they could never safely be used for any post-planning purposes, because they would not get processed by setrefs.c. So they're just dead weight to carry around in the finished plan tree, plus being an attractive nuisance for somebody who might get the idea that they could be used that way. Secondly, because those qual trees (sometimes) contained RestrictInfos, they created a plan-transmission hazard for parallel query, which is how come we noticed a problem. We dealt with that symptom in commit 28b0478, but really a more straightforward and more efficient fix is to pass the data through in a new field of struct PgFdwRelationInfo. So do it that way. (There's no need to revert 28b0478, as it has sufficient reason to live anyway.) Per fuzz testing by Andreas Seltenreich. Discussion: https://postgr.es/m/87tw5x4vcu.fsf@credativ.de
1 parent 02af785 commit 88e902b

File tree

2 files changed

+29
-24
lines changed

2 files changed

+29
-24
lines changed

contrib/postgres_fdw/postgres_fdw.c

+21-23
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,6 @@ enum FdwScanPrivateIndex
6464
{
6565
/* SQL statement to execute remotely (as a String node) */
6666
FdwScanPrivateSelectSql,
67-
/* List of qual clauses that can be executed remotely */
68-
/* (DO NOT try to use these at runtime; see postgresGetForeignPlan) */
69-
FdwScanPrivateRemoteConds,
7067
/* Integer list of attribute numbers retrieved by the SELECT */
7168
FdwScanPrivateRetrievedAttrs,
7269
/* Integer representing the desired fetch_size */
@@ -1262,12 +1259,14 @@ postgresGetForeignPlan(PlannerInfo *root,
12621259
remote_exprs, best_path->path.pathkeys,
12631260
false, &retrieved_attrs, &params_list);
12641261

1262+
/* Remember remote_exprs for possible use by postgresPlanDirectModify */
1263+
fpinfo->final_remote_exprs = remote_exprs;
1264+
12651265
/*
12661266
* Build the fdw_private list that will be available to the executor.
12671267
* Items in the list must match order in enum FdwScanPrivateIndex.
12681268
*/
1269-
fdw_private = list_make4(makeString(sql.data),
1270-
remote_exprs,
1269+
fdw_private = list_make3(makeString(sql.data),
12711270
retrieved_attrs,
12721271
makeInteger(fpinfo->fetch_size));
12731272
if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
@@ -1280,13 +1279,6 @@ postgresGetForeignPlan(PlannerInfo *root,
12801279
* Note that the remote parameter expressions are stored in the fdw_exprs
12811280
* field of the finished plan node; we can't keep them in private state
12821281
* because then they wouldn't be subject to later planner processing.
1283-
*
1284-
* We have some foreign qual conditions hidden away within fdw_private's
1285-
* FdwScanPrivateRemoteConds item, which would be unsafe per the above
1286-
* consideration. But those will only be used by postgresPlanDirectModify,
1287-
* which may extract them to use in a rewritten plan. We assume that
1288-
* nothing will be done between here and there that would need to modify
1289-
* those expressions.
12901282
*/
12911283
return make_foreignscan(tlist,
12921284
local_exprs,
@@ -2130,13 +2122,15 @@ postgresPlanDirectModify(PlannerInfo *root,
21302122
int subplan_index)
21312123
{
21322124
CmdType operation = plan->operation;
2133-
Plan *subplan = (Plan *) list_nth(plan->plans, subplan_index);
2134-
RangeTblEntry *rte = planner_rt_fetch(resultRelation, root);
2125+
Plan *subplan;
2126+
RelOptInfo *foreignrel;
2127+
RangeTblEntry *rte;
2128+
PgFdwRelationInfo *fpinfo;
21352129
Relation rel;
21362130
StringInfoData sql;
21372131
ForeignScan *fscan;
21382132
List *targetAttrs = NIL;
2139-
List *remote_conds;
2133+
List *remote_exprs;
21402134
List *params_list = NIL;
21412135
List *returningList = NIL;
21422136
List *retrieved_attrs = NIL;
@@ -2155,8 +2149,10 @@ postgresPlanDirectModify(PlannerInfo *root,
21552149
* It's unsafe to modify a foreign table directly if there are any local
21562150
* joins needed.
21572151
*/
2152+
subplan = (Plan *) list_nth(plan->plans, subplan_index);
21582153
if (!IsA(subplan, ForeignScan))
21592154
return false;
2155+
fscan = (ForeignScan *) subplan;
21602156

21612157
/*
21622158
* It's unsafe to modify a foreign table directly if there are any quals
@@ -2168,17 +2164,20 @@ postgresPlanDirectModify(PlannerInfo *root,
21682164
/*
21692165
* We can't handle an UPDATE or DELETE on a foreign join for now.
21702166
*/
2171-
fscan = (ForeignScan *) subplan;
21722167
if (fscan->scan.scanrelid == 0)
21732168
return false;
21742169

2170+
/* Safe to fetch data about the target foreign rel */
2171+
foreignrel = root->simple_rel_array[resultRelation];
2172+
rte = root->simple_rte_array[resultRelation];
2173+
fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
2174+
21752175
/*
21762176
* It's unsafe to update a foreign table directly, if any expressions to
21772177
* assign to the target columns are unsafe to evaluate remotely.
21782178
*/
21792179
if (operation == CMD_UPDATE)
21802180
{
2181-
RelOptInfo *baserel = root->simple_rel_array[resultRelation];
21822181
int col;
21832182

21842183
/*
@@ -2201,7 +2200,7 @@ postgresPlanDirectModify(PlannerInfo *root,
22012200
elog(ERROR, "attribute number %d not found in subplan targetlist",
22022201
attno);
22032202

2204-
if (!is_foreign_expr(root, baserel, (Expr *) tle->expr))
2203+
if (!is_foreign_expr(root, foreignrel, (Expr *) tle->expr))
22052204
return false;
22062205

22072206
targetAttrs = lappend_int(targetAttrs, attno);
@@ -2220,12 +2219,11 @@ postgresPlanDirectModify(PlannerInfo *root,
22202219
rel = heap_open(rte->relid, NoLock);
22212220

22222221
/*
2223-
* Extract the qual clauses that can be evaluated remotely. (These are
2222+
* Recall the qual clauses that must be evaluated remotely. (These are
22242223
* bare clauses not RestrictInfos, but deparse.c's appendConditions()
22252224
* doesn't care.)
22262225
*/
2227-
remote_conds = (List *) list_nth(fscan->fdw_private,
2228-
FdwScanPrivateRemoteConds);
2226+
remote_exprs = fpinfo->final_remote_exprs;
22292227

22302228
/*
22312229
* Extract the relevant RETURNING list if any.
@@ -2242,12 +2240,12 @@ postgresPlanDirectModify(PlannerInfo *root,
22422240
deparseDirectUpdateSql(&sql, root, resultRelation, rel,
22432241
((Plan *) fscan)->targetlist,
22442242
targetAttrs,
2245-
remote_conds, &params_list,
2243+
remote_exprs, &params_list,
22462244
returningList, &retrieved_attrs);
22472245
break;
22482246
case CMD_DELETE:
22492247
deparseDirectDeleteSql(&sql, root, resultRelation, rel,
2250-
remote_conds, &params_list,
2248+
remote_exprs, &params_list,
22512249
returningList, &retrieved_attrs);
22522250
break;
22532251
default:

contrib/postgres_fdw/postgres_fdw.h

+8-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222

2323
/*
2424
* FDW-specific planner information kept in RelOptInfo.fdw_private for a
25-
* foreign table. This information is collected by postgresGetForeignRelSize.
25+
* postgres_fdw foreign table. For a baserel, this struct is created by
26+
* postgresGetForeignRelSize, although some fields are not filled till later.
27+
* postgresGetForeignJoinPaths creates it for a joinrel, and
28+
* postgresGetForeignUpperPaths creates it for an upperrel.
2629
*/
2730
typedef struct PgFdwRelationInfo
2831
{
@@ -40,6 +43,9 @@ typedef struct PgFdwRelationInfo
4043
List *remote_conds;
4144
List *local_conds;
4245

46+
/* Actual remote restriction clauses for scan (sans RestrictInfos) */
47+
List *final_remote_exprs;
48+
4349
/* Bitmap of attr numbers we need to fetch from the remote server. */
4450
Bitmapset *attrs_used;
4551

@@ -83,6 +89,7 @@ typedef struct PgFdwRelationInfo
8389
RelOptInfo *outerrel;
8490
RelOptInfo *innerrel;
8591
JoinType jointype;
92+
/* joinclauses contains only JOIN/ON conditions for an outer join */
8693
List *joinclauses; /* List of RestrictInfo */
8794

8895
/* Grouping information */

0 commit comments

Comments
 (0)