Skip to content

Commit 178f2d5

Browse files
committed
Include result relation info in direct modify ForeignScan nodes.
FDWs that can perform an UPDATE/DELETE remotely using the "direct modify" set of APIs need to access the ResultRelInfo of the target table. That's currently available in EState.es_result_relation_info, but the next commit will remove that field. This commit adds a new resultRelation field in ForeignScan, to store the target relation's RT index, and the corresponding ResultRelInfo in ForeignScanState. The FDW's PlanDirectModify callback is expected to set 'resultRelation' along with 'operation'. The core code doesn't need them for anything, they are for the convenience of FDW's Begin- and IterateDirectModify callbacks. Authors: Amit Langote, Etsuro Fujita Discussion: https://www.postgresql.org/message-id/CA%2BHiwqGEmiib8FLiHMhKB%2BCH5dRgHSLc5N5wnvc4kym%2BZYpQEQ%40mail.gmail.com
1 parent 39b4a95 commit 178f2d5

File tree

10 files changed

+45
-13
lines changed

10 files changed

+45
-13
lines changed

contrib/postgres_fdw/postgres_fdw.c

+9-7
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,7 @@ static void init_returning_filter(PgFdwDirectModifyState *dmstate,
451451
List *fdw_scan_tlist,
452452
Index rtindex);
453453
static TupleTableSlot *apply_returning_filter(PgFdwDirectModifyState *dmstate,
454+
ResultRelInfo *resultRelInfo,
454455
TupleTableSlot *slot,
455456
EState *estate);
456457
static void prepare_query_params(PlanState *node,
@@ -2287,9 +2288,10 @@ postgresPlanDirectModify(PlannerInfo *root,
22872288
}
22882289

22892290
/*
2290-
* Update the operation info.
2291+
* Update the operation and target relation info.
22912292
*/
22922293
fscan->operation = operation;
2294+
fscan->resultRelation = resultRelation;
22932295

22942296
/*
22952297
* Update the fdw_exprs list that will be available to the executor.
@@ -2355,7 +2357,7 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags)
23552357
* Identify which user to do the remote access as. This should match what
23562358
* ExecCheckRTEPerms() does.
23572359
*/
2358-
rtindex = estate->es_result_relation_info->ri_RangeTableIndex;
2360+
rtindex = node->resultRelInfo->ri_RangeTableIndex;
23592361
rte = exec_rt_fetch(rtindex, estate);
23602362
userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
23612363

@@ -2450,7 +2452,7 @@ postgresIterateDirectModify(ForeignScanState *node)
24502452
{
24512453
PgFdwDirectModifyState *dmstate = (PgFdwDirectModifyState *) node->fdw_state;
24522454
EState *estate = node->ss.ps.state;
2453-
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
2455+
ResultRelInfo *resultRelInfo = node->resultRelInfo;
24542456

24552457
/*
24562458
* If this is the first call after Begin, execute the statement.
@@ -4086,7 +4088,7 @@ get_returning_data(ForeignScanState *node)
40864088
{
40874089
PgFdwDirectModifyState *dmstate = (PgFdwDirectModifyState *) node->fdw_state;
40884090
EState *estate = node->ss.ps.state;
4089-
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
4091+
ResultRelInfo *resultRelInfo = node->resultRelInfo;
40904092
TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
40914093
TupleTableSlot *resultSlot;
40924094

@@ -4141,7 +4143,7 @@ get_returning_data(ForeignScanState *node)
41414143
if (dmstate->rel)
41424144
resultSlot = slot;
41434145
else
4144-
resultSlot = apply_returning_filter(dmstate, slot, estate);
4146+
resultSlot = apply_returning_filter(dmstate, resultRelInfo, slot, estate);
41454147
}
41464148
dmstate->next_tuple++;
41474149

@@ -4230,10 +4232,10 @@ init_returning_filter(PgFdwDirectModifyState *dmstate,
42304232
*/
42314233
static TupleTableSlot *
42324234
apply_returning_filter(PgFdwDirectModifyState *dmstate,
4235+
ResultRelInfo *resultRelInfo,
42334236
TupleTableSlot *slot,
42344237
EState *estate)
42354238
{
4236-
ResultRelInfo *relInfo = estate->es_result_relation_info;
42374239
TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
42384240
TupleTableSlot *resultSlot;
42394241
Datum *values;
@@ -4245,7 +4247,7 @@ apply_returning_filter(PgFdwDirectModifyState *dmstate,
42454247
/*
42464248
* Use the return tuple slot as a place to store the result tuple.
42474249
*/
4248-
resultSlot = ExecGetReturningSlot(estate, relInfo);
4250+
resultSlot = ExecGetReturningSlot(estate, resultRelInfo);
42494251

42504252
/*
42514253
* Extract all the values of the scan tuple.

doc/src/sgml/fdwhandler.sgml

+9-6
Original file line numberDiff line numberDiff line change
@@ -861,11 +861,15 @@ PlanDirectModify(PlannerInfo *root,
861861
To execute the direct modification on the remote server, this function
862862
must rewrite the target subplan with a <structname>ForeignScan</structname> plan
863863
node that executes the direct modification on the remote server. The
864-
<structfield>operation</structfield> field of the <structname>ForeignScan</structname> must
865-
be set to the <literal>CmdType</literal> enumeration appropriately; that is,
864+
<structfield>operation</structfield> and <structfield>resultRelation</structfield> fields
865+
of the <structname>ForeignScan</structname> must be set appropriately.
866+
<structfield>operation</structfield> must be set to the <literal>CmdType</literal>
867+
enumeration corresponding to the statement kind (that is,
866868
<literal>CMD_UPDATE</literal> for <command>UPDATE</command>,
867869
<literal>CMD_INSERT</literal> for <command>INSERT</command>, and
868-
<literal>CMD_DELETE</literal> for <command>DELETE</command>.
870+
<literal>CMD_DELETE</literal> for <command>DELETE</command>), and the
871+
<literal>resultRelation</literal> argument must be copied to the
872+
<structfield>resultRelation</structfield> field.
869873
</para>
870874

871875
<para>
@@ -925,9 +929,8 @@ IterateDirectModify(ForeignScanState *node);
925929
needed for the <literal>RETURNING</literal> calculation, returning it in a
926930
tuple table slot (the node's <structfield>ScanTupleSlot</structfield> should be
927931
used for this purpose). The data that was actually inserted, updated
928-
or deleted must be stored in the
929-
<literal>es_result_relation_info-&gt;ri_projectReturning-&gt;pi_exprContext-&gt;ecxt_scantuple</literal>
930-
of the node's <structname>EState</structname>.
932+
or deleted must be stored in
933+
<literal>node->resultRelInfo->ri_projectReturning-&gt;pi_exprContext-&gt;ecxt_scantuple</literal>.
931934
Return NULL if no more rows are available.
932935
Note that this is called in a short-lived memory context that will be
933936
reset between invocations. Create a memory context in

src/backend/executor/nodeForeignscan.c

+7
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,13 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
215215
scanstate->fdwroutine = fdwroutine;
216216
scanstate->fdw_state = NULL;
217217

218+
/*
219+
* For the FDW's convenience, look up the modification target relation's.
220+
* ResultRelInfo.
221+
*/
222+
if (node->resultRelation > 0)
223+
scanstate->resultRelInfo = estate->es_result_relations[node->resultRelation - 1];
224+
218225
/* Initialize any outer plan. */
219226
if (outerPlan(node))
220227
outerPlanState(scanstate) =

src/backend/nodes/copyfuncs.c

+1
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,7 @@ _copyForeignScan(const ForeignScan *from)
758758
COPY_NODE_FIELD(fdw_recheck_quals);
759759
COPY_BITMAPSET_FIELD(fs_relids);
760760
COPY_SCALAR_FIELD(fsSystemCol);
761+
COPY_SCALAR_FIELD(resultRelation);
761762

762763
return newnode;
763764
}

src/backend/nodes/outfuncs.c

+1
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node)
695695
WRITE_NODE_FIELD(fdw_recheck_quals);
696696
WRITE_BITMAPSET_FIELD(fs_relids);
697697
WRITE_BOOL_FIELD(fsSystemCol);
698+
WRITE_INT_FIELD(resultRelation);
698699
}
699700

700701
static void

src/backend/nodes/readfuncs.c

+1
Original file line numberDiff line numberDiff line change
@@ -2014,6 +2014,7 @@ _readForeignScan(void)
20142014
READ_NODE_FIELD(fdw_recheck_quals);
20152015
READ_BITMAPSET_FIELD(fs_relids);
20162016
READ_BOOL_FIELD(fsSystemCol);
2017+
READ_INT_FIELD(resultRelation);
20172018

20182019
READ_DONE();
20192020
}

src/backend/optimizer/plan/createplan.c

+4
Original file line numberDiff line numberDiff line change
@@ -5530,7 +5530,11 @@ make_foreignscan(List *qptlist,
55305530
plan->lefttree = outer_plan;
55315531
plan->righttree = NULL;
55325532
node->scan.scanrelid = scanrelid;
5533+
5534+
/* these may be overridden by the FDW's PlanDirectModify callback. */
55335535
node->operation = CMD_SELECT;
5536+
node->resultRelation = 0;
5537+
55345538
/* fs_server will be filled in by create_foreignscan_plan */
55355539
node->fs_server = InvalidOid;
55365540
node->fdw_exprs = fdw_exprs;

src/backend/optimizer/plan/setrefs.c

+4
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,10 @@ set_foreignscan_references(PlannerInfo *root,
13101310
}
13111311

13121312
fscan->fs_relids = offset_relid_set(fscan->fs_relids, rtoffset);
1313+
1314+
/* Adjust resultRelation if it's valid */
1315+
if (fscan->resultRelation > 0)
1316+
fscan->resultRelation += rtoffset;
13131317
}
13141318

13151319
/*

src/include/nodes/execnodes.h

+1
Original file line numberDiff line numberDiff line change
@@ -1777,6 +1777,7 @@ typedef struct ForeignScanState
17771777
ScanState ss; /* its first field is NodeTag */
17781778
ExprState *fdw_recheck_quals; /* original quals not in ss.ps.qual */
17791779
Size pscan_len; /* size of parallel coordination information */
1780+
ResultRelInfo *resultRelInfo; /* result rel info, if UPDATE or DELETE */
17801781
/* use struct pointer to avoid including fdwapi.h here */
17811782
struct FdwRoutine *fdwroutine;
17821783
void *fdw_state; /* foreign-data wrapper can keep state here */

src/include/nodes/plannodes.h

+8
Original file line numberDiff line numberDiff line change
@@ -599,12 +599,20 @@ typedef struct WorkTableScan
599599
* When the plan node represents a foreign join, scan.scanrelid is zero and
600600
* fs_relids must be consulted to identify the join relation. (fs_relids
601601
* is valid for simple scans as well, but will always match scan.scanrelid.)
602+
*
603+
* If the FDW's PlanDirectModify() callback decides to repurpose a ForeignScan
604+
* node to perform the UPDATE or DELETE operation directly in the remote
605+
* server, it sets 'operation' and 'resultRelation' to identify the operation
606+
* type and target relation. Note that these fields are only set if the
607+
* modification is performed *fully* remotely; otherwise, the modification is
608+
* driven by a local ModifyTable node and 'operation' is left to CMD_SELECT.
602609
* ----------------
603610
*/
604611
typedef struct ForeignScan
605612
{
606613
Scan scan;
607614
CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */
615+
Index resultRelation; /* direct modification target's RT index */
608616
Oid fs_server; /* OID of foreign server */
609617
List *fdw_exprs; /* expressions that FDW may evaluate */
610618
List *fdw_private; /* private data for FDW */

0 commit comments

Comments
 (0)