From 178f2d560dde05b356ab2f586b8bc62514f454aa Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Wed, 14 Oct 2020 10:58:38 +0300 Subject: [PATCH] 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 --- contrib/postgres_fdw/postgres_fdw.c | 16 +++++++++------- doc/src/sgml/fdwhandler.sgml | 15 +++++++++------ src/backend/executor/nodeForeignscan.c | 7 +++++++ src/backend/nodes/copyfuncs.c | 1 + src/backend/nodes/outfuncs.c | 1 + src/backend/nodes/readfuncs.c | 1 + src/backend/optimizer/plan/createplan.c | 4 ++++ src/backend/optimizer/plan/setrefs.c | 4 ++++ src/include/nodes/execnodes.h | 1 + src/include/nodes/plannodes.h | 8 ++++++++ 10 files changed, 45 insertions(+), 13 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index a31abce7c9..78facb8ebf 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -451,6 +451,7 @@ static void init_returning_filter(PgFdwDirectModifyState *dmstate, List *fdw_scan_tlist, Index rtindex); static TupleTableSlot *apply_returning_filter(PgFdwDirectModifyState *dmstate, + ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate); static void prepare_query_params(PlanState *node, @@ -2287,9 +2288,10 @@ postgresPlanDirectModify(PlannerInfo *root, } /* - * Update the operation info. + * Update the operation and target relation info. */ fscan->operation = operation; + fscan->resultRelation = resultRelation; /* * Update the fdw_exprs list that will be available to the executor. @@ -2355,7 +2357,7 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) * Identify which user to do the remote access as. This should match what * ExecCheckRTEPerms() does. */ - rtindex = estate->es_result_relation_info->ri_RangeTableIndex; + rtindex = node->resultRelInfo->ri_RangeTableIndex; rte = exec_rt_fetch(rtindex, estate); userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); @@ -2450,7 +2452,7 @@ postgresIterateDirectModify(ForeignScanState *node) { PgFdwDirectModifyState *dmstate = (PgFdwDirectModifyState *) node->fdw_state; EState *estate = node->ss.ps.state; - ResultRelInfo *resultRelInfo = estate->es_result_relation_info; + ResultRelInfo *resultRelInfo = node->resultRelInfo; /* * If this is the first call after Begin, execute the statement. @@ -4086,7 +4088,7 @@ get_returning_data(ForeignScanState *node) { PgFdwDirectModifyState *dmstate = (PgFdwDirectModifyState *) node->fdw_state; EState *estate = node->ss.ps.state; - ResultRelInfo *resultRelInfo = estate->es_result_relation_info; + ResultRelInfo *resultRelInfo = node->resultRelInfo; TupleTableSlot *slot = node->ss.ss_ScanTupleSlot; TupleTableSlot *resultSlot; @@ -4141,7 +4143,7 @@ get_returning_data(ForeignScanState *node) if (dmstate->rel) resultSlot = slot; else - resultSlot = apply_returning_filter(dmstate, slot, estate); + resultSlot = apply_returning_filter(dmstate, resultRelInfo, slot, estate); } dmstate->next_tuple++; @@ -4230,10 +4232,10 @@ init_returning_filter(PgFdwDirectModifyState *dmstate, */ static TupleTableSlot * apply_returning_filter(PgFdwDirectModifyState *dmstate, + ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate) { - ResultRelInfo *relInfo = estate->es_result_relation_info; TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel); TupleTableSlot *resultSlot; Datum *values; @@ -4245,7 +4247,7 @@ apply_returning_filter(PgFdwDirectModifyState *dmstate, /* * Use the return tuple slot as a place to store the result tuple. */ - resultSlot = ExecGetReturningSlot(estate, relInfo); + resultSlot = ExecGetReturningSlot(estate, resultRelInfo); /* * Extract all the values of the scan tuple. diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml index 72fa127212..9c9293414c 100644 --- a/doc/src/sgml/fdwhandler.sgml +++ b/doc/src/sgml/fdwhandler.sgml @@ -861,11 +861,15 @@ PlanDirectModify(PlannerInfo *root, To execute the direct modification on the remote server, this function must rewrite the target subplan with a ForeignScan plan node that executes the direct modification on the remote server. The - operation field of the ForeignScan must - be set to the CmdType enumeration appropriately; that is, + operation and resultRelation fields + of the ForeignScan must be set appropriately. + operation must be set to the CmdType + enumeration corresponding to the statement kind (that is, CMD_UPDATE for UPDATE, CMD_INSERT for INSERT, and - CMD_DELETE for DELETE. + CMD_DELETE for DELETE), and the + resultRelation argument must be copied to the + resultRelation field. @@ -925,9 +929,8 @@ IterateDirectModify(ForeignScanState *node); needed for the RETURNING calculation, returning it in a tuple table slot (the node's ScanTupleSlot should be used for this purpose). The data that was actually inserted, updated - or deleted must be stored in the - es_result_relation_info->ri_projectReturning->pi_exprContext->ecxt_scantuple - of the node's EState. + or deleted must be stored in + node->resultRelInfo->ri_projectReturning->pi_exprContext->ecxt_scantuple. Return NULL if no more rows are available. Note that this is called in a short-lived memory context that will be reset between invocations. Create a memory context in diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index 513471ab9b..0b20f94035 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -215,6 +215,13 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) scanstate->fdwroutine = fdwroutine; scanstate->fdw_state = NULL; + /* + * For the FDW's convenience, look up the modification target relation's. + * ResultRelInfo. + */ + if (node->resultRelation > 0) + scanstate->resultRelInfo = estate->es_result_relations[node->resultRelation - 1]; + /* Initialize any outer plan. */ if (outerPlan(node)) outerPlanState(scanstate) = diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 4d79f70950..2b4d7654cc 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -758,6 +758,7 @@ _copyForeignScan(const ForeignScan *from) COPY_NODE_FIELD(fdw_recheck_quals); COPY_BITMAPSET_FIELD(fs_relids); COPY_SCALAR_FIELD(fsSystemCol); + COPY_SCALAR_FIELD(resultRelation); return newnode; } diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index f441ae3c51..08a049232e 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -695,6 +695,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node) WRITE_NODE_FIELD(fdw_recheck_quals); WRITE_BITMAPSET_FIELD(fs_relids); WRITE_BOOL_FIELD(fsSystemCol); + WRITE_INT_FIELD(resultRelation); } static void diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 3a54765f5c..ab7b535caa 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -2014,6 +2014,7 @@ _readForeignScan(void) READ_NODE_FIELD(fdw_recheck_quals); READ_BITMAPSET_FIELD(fs_relids); READ_BOOL_FIELD(fsSystemCol); + READ_INT_FIELD(resultRelation); READ_DONE(); } diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 881eaf4813..94280a730c 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -5530,7 +5530,11 @@ make_foreignscan(List *qptlist, plan->lefttree = outer_plan; plan->righttree = NULL; node->scan.scanrelid = scanrelid; + + /* these may be overridden by the FDW's PlanDirectModify callback. */ node->operation = CMD_SELECT; + node->resultRelation = 0; + /* fs_server will be filled in by create_foreignscan_plan */ node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 6847ff6f44..8b43371425 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -1310,6 +1310,10 @@ set_foreignscan_references(PlannerInfo *root, } fscan->fs_relids = offset_relid_set(fscan->fs_relids, rtoffset); + + /* Adjust resultRelation if it's valid */ + if (fscan->resultRelation > 0) + fscan->resultRelation += rtoffset; } /* diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index a926ff1711..d9b09c5920 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1777,6 +1777,7 @@ typedef struct ForeignScanState ScanState ss; /* its first field is NodeTag */ ExprState *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ + ResultRelInfo *resultRelInfo; /* result rel info, if UPDATE or DELETE */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; void *fdw_state; /* foreign-data wrapper can keep state here */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index a7bdf3497e..7e6b10f86b 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -599,12 +599,20 @@ typedef struct WorkTableScan * When the plan node represents a foreign join, scan.scanrelid is zero and * fs_relids must be consulted to identify the join relation. (fs_relids * is valid for simple scans as well, but will always match scan.scanrelid.) + * + * If the FDW's PlanDirectModify() callback decides to repurpose a ForeignScan + * node to perform the UPDATE or DELETE operation directly in the remote + * server, it sets 'operation' and 'resultRelation' to identify the operation + * type and target relation. Note that these fields are only set if the + * modification is performed *fully* remotely; otherwise, the modification is + * driven by a local ModifyTable node and 'operation' is left to CMD_SELECT. * ---------------- */ typedef struct ForeignScan { Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ + Index resultRelation; /* direct modification target's RT index */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */