Include result relation info in direct modify ForeignScan nodes.
authorHeikki Linnakangas <[email protected]>
Wed, 14 Oct 2020 07:58:38 +0000 (10:58 +0300)
committerHeikki Linnakangas <[email protected]>
Wed, 14 Oct 2020 07:58:38 +0000 (10:58 +0300)
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
doc/src/sgml/fdwhandler.sgml
src/backend/executor/nodeForeignscan.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/setrefs.c
src/include/nodes/execnodes.h
src/include/nodes/plannodes.h

index a31abce7c99604cff1606adf75e6e40dd7efae1e..78facb8ebfa49728a996cf2737560da9b5c621f0 100644 (file)
@@ -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.
index 72fa1272120d84e9be53d3881adb323ebee52a26..9c9293414c5815529c1d7f81ad394f7798a39003 100644 (file)
@@ -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 <structname>ForeignScan</structname> plan
      node that executes the direct modification on the remote server.  The
-     <structfield>operation</structfield> field of the <structname>ForeignScan</structname> must
-     be set to the <literal>CmdType</literal> enumeration appropriately; that is,
+     <structfield>operation</structfield> and <structfield>resultRelation</structfield> fields
+     of the <structname>ForeignScan</structname> must be set appropriately.
+     <structfield>operation</structfield> must be set to the <literal>CmdType</literal>
+     enumeration corresponding to the statement kind (that is,
      <literal>CMD_UPDATE</literal> for <command>UPDATE</command>,
      <literal>CMD_INSERT</literal> for <command>INSERT</command>, and
-     <literal>CMD_DELETE</literal> for <command>DELETE</command>.
+     <literal>CMD_DELETE</literal> for <command>DELETE</command>), and the
+     <literal>resultRelation</literal> argument must be copied to the
+     <structfield>resultRelation</structfield> field.
     </para>
 
     <para>
@@ -925,9 +929,8 @@ IterateDirectModify(ForeignScanState *node);
      needed for the <literal>RETURNING</literal> calculation, returning it in a
      tuple table slot (the node's <structfield>ScanTupleSlot</structfield> should be
      used for this purpose).  The data that was actually inserted, updated
-     or deleted must be stored in the
-     <literal>es_result_relation_info-&gt;ri_projectReturning-&gt;pi_exprContext-&gt;ecxt_scantuple</literal>
-     of the node's <structname>EState</structname>.
+     or deleted must be stored in
+     <literal>node->resultRelInfo->ri_projectReturning-&gt;pi_exprContext-&gt;ecxt_scantuple</literal>.
      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
index 513471ab9b907e4600486e887d20810e91219ca4..0b20f94035edd85eba887dc0f04c8187d9d9a86c 100644 (file)
@@ -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) =
index 4d79f70950b7613cb8bae1b0c27dac1002588597..2b4d7654cc7154d1e166f5744aa7505410979868 100644 (file)
@@ -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;
 }
index f441ae3c5192822118911d6b063f405686acace6..08a049232e0a794e20d288e6f304a74d16e3b03f 100644 (file)
@@ -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
index 3a54765f5ca16bcdebf74a86f846eefac9a28647..ab7b535caaeaa2f9abd61f27467120c70bbb3230 100644 (file)
@@ -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();
 }
index 881eaf481339a4d857066a8c999b0146636dad2d..94280a730c4d9abb2143416fca8e74e76dada042 100644 (file)
@@ -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;
index 6847ff6f44738c2f1546b3866f8b1ec32ca9be73..8b4337142594042f6cd17dd76def97d72ca62e95 100644 (file)
@@ -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;
 }
 
 /*
index a926ff17118bf12fe5e0cba8616e26f096f910fc..d9b09c592068610612a361d2923f9bcc10983b64 100644 (file)
@@ -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 */
index a7bdf3497e1385ee2189c5eca42bc54ecff43d4b..7e6b10f86b97866d7fc7c948bb8dded084565262 100644 (file)
@@ -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 */