On 2017/08/17 17:27, Etsuro Fujita wrote:
On 2017/07/11 6:56, Robert Haas wrote:
I have to admit that I'm a little bit fuzzy about why foreign insert
routing requires all of these changes.  I think this patch would
benefit from being accompanied by several paragraphs of explanation
outlining the rationale for each part of the patch.

Will do.

Here is an updated version of the patch.

* Query planning: the patch creates copies of Query/Plan with a foreign partition as target from the original Query/Plan for each foreign partition and invokes PlanForeignModify with those copies, to allow the FDW to do query planning for remote INSERT with the existing API. To make such Queries the similar way inheritance_planner does, I modified transformInsertStmt so that the inh flag for the partitioned table's RTE is set to true, which allows (1) expand_inherited_rtentry to build an RTE and AppendRelInfo for each partition in the partitioned table and (2) make_modifytable to build such Queries using adjust_appendrel_attrs and those AppendRelInfos.

* explain.c: I modified show_modifytable_info so that we can show remote queries for foreign partitions in EXPLAIN for INSERT into a partitioned table the same way as for inherited UPDATE/DELETE cases. Here is an example:

postgres=# explain verbose insert into pt values (1), (2);
                            QUERY PLAN
-------------------------------------------------------------------
 Insert on public.pt  (cost=0.00..0.03 rows=2 width=4)
   Foreign Insert on public.fp1
     Remote SQL: INSERT INTO public.t1(a) VALUES ($1)
   Foreign Insert on public.fp2
     Remote SQL: INSERT INTO public.t2(a) VALUES ($1)
   ->  Values Scan on "*VALUES*"  (cost=0.00..0.03 rows=2 width=4)
         Output: "*VALUES*".column1
(7 rows)

I think I should add more explanation about the patch, but I don't have time today, so I'll write additional explanation in the next email. Sorry about that.

Best regards,
Etsuro Fujita
*** a/contrib/file_fdw/output/file_fdw.source
--- b/contrib/file_fdw/output/file_fdw.source
***************
*** 315,321 **** SELECT tableoid::regclass, * FROM p2;
  (0 rows)
  
  COPY pt FROM '@abs_srcdir@/data/list2.bad' with (format 'csv', delimiter 
','); -- ERROR
! ERROR:  cannot route inserted tuples to a foreign table
  CONTEXT:  COPY pt, line 2: "1,qux"
  COPY pt FROM '@abs_srcdir@/data/list2.csv' with (format 'csv', delimiter ',');
  SELECT tableoid::regclass, * FROM pt;
--- 315,321 ----
  (0 rows)
  
  COPY pt FROM '@abs_srcdir@/data/list2.bad' with (format 'csv', delimiter 
','); -- ERROR
! ERROR:  cannot route copied tuples to a foreign table
  CONTEXT:  COPY pt, line 2: "1,qux"
  COPY pt FROM '@abs_srcdir@/data/list2.csv' with (format 'csv', delimiter ',');
  SELECT tableoid::regclass, * FROM pt;
***************
*** 342,348 **** SELECT tableoid::regclass, * FROM p2;
  (2 rows)
  
  INSERT INTO pt VALUES (1, 'xyzzy'); -- ERROR
! ERROR:  cannot route inserted tuples to a foreign table
  INSERT INTO pt VALUES (2, 'xyzzy');
  SELECT tableoid::regclass, * FROM pt;
   tableoid | a |   b   
--- 342,348 ----
  (2 rows)
  
  INSERT INTO pt VALUES (1, 'xyzzy'); -- ERROR
! ERROR:  cannot route inserted tuples to foreign table "p1"
  INSERT INTO pt VALUES (2, 'xyzzy');
  SELECT tableoid::regclass, * FROM pt;
   tableoid | a |   b   
*** a/contrib/postgres_fdw/expected/postgres_fdw.out
--- b/contrib/postgres_fdw/expected/postgres_fdw.out
***************
*** 7029,7034 **** NOTICE:  drop cascades to foreign table bar2
--- 7029,7172 ----
  drop table loct1;
  drop table loct2;
  -- ===================================================================
+ -- test tuple routing to foreign-table partitions
+ -- ===================================================================
+ create table pt (a int, b int) partition by list (a);
+ create table locp partition of pt for values in (1);
+ create table locfoo (a int check (a in (2)), b int);
+ create foreign table remp partition of pt for values in (2) server loopback 
options (table_name 'locfoo');
+ explain (verbose, costs off)
+ insert into pt values (1, 1), (2, 1);
+                            QUERY PLAN                            
+ -----------------------------------------------------------------
+  Insert on public.pt
+    Insert on public.locp
+    Foreign Insert on public.remp
+      Remote SQL: INSERT INTO public.locfoo(a, b) VALUES ($1, $2)
+    ->  Values Scan on "*VALUES*"
+          Output: "*VALUES*".column1, "*VALUES*".column2
+ (6 rows)
+ 
+ insert into pt values (1, 1), (2, 1);
+ select tableoid::regclass, * FROM pt;
+  tableoid | a | b 
+ ----------+---+---
+  locp     | 1 | 1
+  remp     | 2 | 1
+ (2 rows)
+ 
+ select tableoid::regclass, * FROM locp;
+  tableoid | a | b 
+ ----------+---+---
+  locp     | 1 | 1
+ (1 row)
+ 
+ select tableoid::regclass, * FROM remp;
+  tableoid | a | b 
+ ----------+---+---
+  remp     | 2 | 1
+ (1 row)
+ 
+ explain (verbose, costs off)
+ insert into pt values (1, 2), (2, 2) returning *;
+                                    QUERY PLAN                                 
  
+ 
--------------------------------------------------------------------------------
+  Insert on public.pt
+    Output: pt.a, pt.b
+    Insert on public.locp
+    Foreign Insert on public.remp
+      Remote SQL: INSERT INTO public.locfoo(a, b) VALUES ($1, $2) RETURNING a, 
b
+    ->  Values Scan on "*VALUES*"
+          Output: "*VALUES*".column1, "*VALUES*".column2
+ (7 rows)
+ 
+ insert into pt values (1, 2), (2, 2) returning *;
+  a | b 
+ ---+---
+  1 | 2
+  2 | 2
+ (2 rows)
+ 
+ select tableoid::regclass, * FROM pt;
+  tableoid | a | b 
+ ----------+---+---
+  locp     | 1 | 1
+  locp     | 1 | 2
+  remp     | 2 | 1
+  remp     | 2 | 2
+ (4 rows)
+ 
+ select tableoid::regclass, * FROM locp;
+  tableoid | a | b 
+ ----------+---+---
+  locp     | 1 | 1
+  locp     | 1 | 2
+ (2 rows)
+ 
+ select tableoid::regclass, * FROM remp;
+  tableoid | a | b 
+ ----------+---+---
+  remp     | 2 | 1
+  remp     | 2 | 2
+ (2 rows)
+ 
+ prepare q1 as insert into pt values (1, 3), (2, 3);
+ explain (verbose, costs off) execute q1;
+                            QUERY PLAN                            
+ -----------------------------------------------------------------
+  Insert on public.pt
+    Insert on public.locp
+    Foreign Insert on public.remp
+      Remote SQL: INSERT INTO public.locfoo(a, b) VALUES ($1, $2)
+    ->  Values Scan on "*VALUES*"
+          Output: "*VALUES*".column1, "*VALUES*".column2
+ (6 rows)
+ 
+ alter table locfoo rename to locbar;
+ alter foreign table remp options (set table_name 'locbar');
+ explain (verbose, costs off) execute q1;
+                            QUERY PLAN                            
+ -----------------------------------------------------------------
+  Insert on public.pt
+    Insert on public.locp
+    Foreign Insert on public.remp
+      Remote SQL: INSERT INTO public.locbar(a, b) VALUES ($1, $2)
+    ->  Values Scan on "*VALUES*"
+          Output: "*VALUES*".column1, "*VALUES*".column2
+ (6 rows)
+ 
+ execute q1;
+ select tableoid::regclass, * FROM pt;
+  tableoid | a | b 
+ ----------+---+---
+  locp     | 1 | 1
+  locp     | 1 | 2
+  locp     | 1 | 3
+  remp     | 2 | 1
+  remp     | 2 | 2
+  remp     | 2 | 3
+ (6 rows)
+ 
+ select tableoid::regclass, * FROM locp;
+  tableoid | a | b 
+ ----------+---+---
+  locp     | 1 | 1
+  locp     | 1 | 2
+  locp     | 1 | 3
+ (3 rows)
+ 
+ select tableoid::regclass, * FROM remp;
+  tableoid | a | b 
+ ----------+---+---
+  remp     | 2 | 1
+  remp     | 2 | 2
+  remp     | 2 | 3
+ (3 rows)
+ 
+ deallocate q1;
+ drop table pt;
+ drop table locbar;
+ -- ===================================================================
  -- test IMPORT FOREIGN SCHEMA
  -- ===================================================================
  CREATE SCHEMA import_source;
*** a/contrib/postgres_fdw/sql/postgres_fdw.sql
--- b/contrib/postgres_fdw/sql/postgres_fdw.sql
***************
*** 1662,1667 **** drop table loct1;
--- 1662,1707 ----
  drop table loct2;
  
  -- ===================================================================
+ -- test tuple routing to foreign-table partitions
+ -- ===================================================================
+ 
+ create table pt (a int, b int) partition by list (a);
+ create table locp partition of pt for values in (1);
+ create table locfoo (a int check (a in (2)), b int);
+ create foreign table remp partition of pt for values in (2) server loopback 
options (table_name 'locfoo');
+ 
+ explain (verbose, costs off)
+ insert into pt values (1, 1), (2, 1);
+ insert into pt values (1, 1), (2, 1);
+ 
+ select tableoid::regclass, * FROM pt;
+ select tableoid::regclass, * FROM locp;
+ select tableoid::regclass, * FROM remp;
+ 
+ explain (verbose, costs off)
+ insert into pt values (1, 2), (2, 2) returning *;
+ insert into pt values (1, 2), (2, 2) returning *;
+ 
+ select tableoid::regclass, * FROM pt;
+ select tableoid::regclass, * FROM locp;
+ select tableoid::regclass, * FROM remp;
+ 
+ prepare q1 as insert into pt values (1, 3), (2, 3);
+ explain (verbose, costs off) execute q1;
+ alter table locfoo rename to locbar;
+ alter foreign table remp options (set table_name 'locbar');
+ explain (verbose, costs off) execute q1;
+ execute q1;
+ 
+ select tableoid::regclass, * FROM pt;
+ select tableoid::regclass, * FROM locp;
+ select tableoid::regclass, * FROM remp;
+ 
+ deallocate q1;
+ drop table pt;
+ drop table locbar;
+ 
+ -- ===================================================================
  -- test IMPORT FOREIGN SCHEMA
  -- ===================================================================
  
*** a/src/backend/commands/copy.c
--- b/src/backend/commands/copy.c
***************
*** 2417,2422 **** CopyFrom(CopyState cstate)
--- 2417,2423 ----
                                          cstate->rel,
                                          1,            /* dummy rangetable 
index */
                                          NULL,
+                                         0,            /* dummy rangetable 
index */
                                          0);
  
        ExecOpenIndices(resultRelInfo, false);
***************
*** 2446,2461 **** CopyFrom(CopyState cstate)
        if (cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
        {
                PartitionDispatch *partition_dispatch_info;
                ResultRelInfo *partitions;
                TupleConversionMap **partition_tupconv_maps;
                TupleTableSlot *partition_tuple_slot;
                int                     num_parted,
                                        num_partitions;
  
                ExecSetupPartitionTupleRouting(cstate->rel,
-                                                                          1,
-                                                                          
estate,
                                                                           
&partition_dispatch_info,
                                                                           
&partitions,
                                                                           
&partition_tupconv_maps,
                                                                           
&partition_tuple_slot,
--- 2447,2465 ----
        if (cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
        {
                PartitionDispatch *partition_dispatch_info;
+               List       *partition_oids;
                ResultRelInfo *partitions;
                TupleConversionMap **partition_tupconv_maps;
                TupleTableSlot *partition_tuple_slot;
                int                     num_parted,
                                        num_partitions;
+               ResultRelInfo *partRelInfo;
+               int                     i;
+               ListCell   *l;
  
                ExecSetupPartitionTupleRouting(cstate->rel,
                                                                           
&partition_dispatch_info,
+                                                                          
&partition_oids,
                                                                           
&partitions,
                                                                           
&partition_tupconv_maps,
                                                                           
&partition_tuple_slot,
***************
*** 2467,2472 **** CopyFrom(CopyState cstate)
--- 2471,2508 ----
                cstate->partition_tupconv_maps = partition_tupconv_maps;
                cstate->partition_tuple_slot = partition_tuple_slot;
  
+               partRelInfo = partitions;
+               i = 0;
+               foreach(l, partition_oids)
+               {
+                       Oid                     partOid = lfirst_oid(l);
+                       Relation        partrel;
+ 
+                       /* Prepare map and ResultRelInfo for the partition */
+                       ExecInitPartition(estate,
+                                                         partOid,
+                                                         0,            /* 
dummy rangetable index */
+                                                         resultRelInfo,
+                                                         partRelInfo,
+                                                         
&partition_tupconv_maps[i]);
+ 
+                       /* Verify the partition is a valid target for COPY */
+                       partrel = partRelInfo->ri_RelationDesc;
+                       if (partrel->rd_rel->relkind == RELKIND_RELATION)
+                               partRelInfo->ri_PartitionIsValid = true;
+                       else
+                       {
+                               /* Should be foreign */
+                               Assert(partrel->rd_rel->relkind == 
RELKIND_FOREIGN_TABLE);
+ 
+                               /* We do not yet have a way to copy into a 
foreign partition */
+                               partRelInfo->ri_PartitionIsValid = false;
+                       }
+ 
+                       partRelInfo++;
+                       i++;
+               }
+ 
                /*
                 * If we are capturing transition tuples, they may need to be
                 * converted from partition format back to partitioned table 
format
***************
*** 2618,2628 **** CopyFrom(CopyState cstate)
                        saved_resultRelInfo = resultRelInfo;
                        resultRelInfo = cstate->partitions + leaf_part_index;
  
!                       /* We do not yet have a way to insert into a foreign 
partition */
!                       if (resultRelInfo->ri_FdwRoutine)
                                ereport(ERROR,
                                                
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
!                                                errmsg("cannot route inserted 
tuples to a foreign table")));
  
                        /*
                         * For ExecInsertIndexTuples() to work on the 
partition's indexes
--- 2654,2669 ----
                        saved_resultRelInfo = resultRelInfo;
                        resultRelInfo = cstate->partitions + leaf_part_index;
  
!                       /* We do not yet have a way to copy into a foreign 
partition */
!                       if (!resultRelInfo->ri_PartitionIsValid)
!                       {
!                               /* Should be foreign */
!                               Assert(resultRelInfo->ri_FdwRoutine);
! 
                                ereport(ERROR,
                                                
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
!                                                errmsg("cannot route copied 
tuples to a foreign table")));
!                       }
  
                        /*
                         * For ExecInsertIndexTuples() to work on the 
partition's indexes
*** a/src/backend/commands/explain.c
--- b/src/backend/commands/explain.c
***************
*** 116,121 **** static void ExplainModifyTarget(ModifyTable *plan, 
ExplainState *es);
--- 116,125 ----
  static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es);
  static void show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
                                          ExplainState *es);
+ static void show_actual_target(ModifyTableState *mtstate, ModifyTable *node,
+                                  ResultRelInfo *resultRelInfo, FdwRoutine 
*fdwroutine,
+                                  bool main_target, int subplan_index,
+                                  const char *operation, bool labeltarget, 
ExplainState *es);
  static void ExplainMemberNodes(List *plans, PlanState **planstates,
                                   List *ancestors, ExplainState *es);
  static void ExplainSubPlans(List *plans, List *ancestors,
***************
*** 834,839 **** ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
--- 838,854 ----
                        if (((ModifyTable *) plan)->exclRelRTI)
                                *rels_used = bms_add_member(*rels_used,
                                                                                
        ((ModifyTable *) plan)->exclRelRTI);
+                       if (((ModifyTable *) plan)->partition_rels)
+                       {
+                               ListCell   *lc;
+ 
+                               foreach(lc, ((ModifyTable *) 
plan)->partition_rels)
+                               {
+                                       Index           rti = lfirst_int(lc);
+ 
+                                       *rels_used = bms_add_member(*rels_used, 
rti);
+                               }
+                       }
                        break;
                default:
                        break;
***************
*** 2856,2916 **** show_modifytable_info(ModifyTableState *mtstate, List 
*ancestors,
        if (labeltargets)
                ExplainOpenGroup("Target Tables", "Target Tables", false, es);
  
        for (j = 0; j < mtstate->mt_nplans; j++)
        {
                ResultRelInfo *resultRelInfo = mtstate->resultRelInfo + j;
                FdwRoutine *fdwroutine = resultRelInfo->ri_FdwRoutine;
  
!               if (labeltargets)
!               {
!                       /* Open a group for this target */
!                       ExplainOpenGroup("Target Table", NULL, true, es);
! 
!                       /*
!                        * In text mode, decorate each target with operation 
type, so that
!                        * ExplainTargetRel's output of " on foo" will read 
nicely.
!                        */
!                       if (es->format == EXPLAIN_FORMAT_TEXT)
!                       {
!                               appendStringInfoSpaces(es->str, es->indent * 2);
!                               appendStringInfoString(es->str,
!                                                                          
fdwroutine ? foperation : operation);
!                       }
! 
!                       /* Identify target */
!                       ExplainTargetRel((Plan *) node,
!                                                        
resultRelInfo->ri_RangeTableIndex,
!                                                        es);
  
!                       if (es->format == EXPLAIN_FORMAT_TEXT)
!                       {
!                               appendStringInfoChar(es->str, '\n');
!                               es->indent++;
!                       }
!               }
  
!               /* Give FDW a chance if needed */
!               if (!resultRelInfo->ri_usesFdwDirectModify &&
!                       fdwroutine != NULL &&
!                       fdwroutine->ExplainForeignModify != NULL)
                {
!                       List       *fdw_private = (List *) 
list_nth(node->fdwPrivLists, j);
  
!                       fdwroutine->ExplainForeignModify(mtstate,
!                                                                               
         resultRelInfo,
!                                                                               
         fdw_private,
!                                                                               
         j,
!                                                                               
         es);
!               }
  
!               if (labeltargets)
!               {
!                       /* Undo the indentation we added in text format */
!                       if (es->format == EXPLAIN_FORMAT_TEXT)
!                               es->indent--;
  
!                       /* Close the group */
!                       ExplainCloseGroup("Target Table", NULL, true, es);
                }
        }
  
--- 2871,2919 ----
        if (labeltargets)
                ExplainOpenGroup("Target Tables", "Target Tables", false, es);
  
+       /* Print main target(s) */
        for (j = 0; j < mtstate->mt_nplans; j++)
        {
                ResultRelInfo *resultRelInfo = mtstate->resultRelInfo + j;
                FdwRoutine *fdwroutine = resultRelInfo->ri_FdwRoutine;
  
!               show_actual_target(mtstate, node, resultRelInfo, fdwroutine, 
true, j,
!                                                  fdwroutine ? foperation : 
operation, labeltargets,
!                                                  es);
!       }
  
!       /* If this is an INSERT into a partitioned table, print partitions */
!       for (j = 0; j < mtstate->mt_num_partitions; j++)
!       {
!               ResultRelInfo *resultRelInfo = mtstate->mt_partitions + j;
!               FdwRoutine *fdwroutine = resultRelInfo->ri_FdwRoutine;
  
!               if (resultRelInfo->ri_PartitionIsValid)
                {
!                       Oid                     partOid = 
RelationGetRelid(resultRelInfo->ri_RelationDesc);
!                       int                     k;
!                       ListCell   *cell;
!                       bool            found;
  
!                       /* First, find the subplan index for the partition */
!                       found = false;
!                       k = 0;
!                       foreach(cell, node->partition_rels)
!                       {
!                               Index           partRTindex = lfirst_int(cell);
  
!                               if (getrelid(partRTindex, es->rtable) == 
partOid)
!                               {
!                                       found = true;
!                                       break;
!                               }
!                               k++;
!                       }
!                       if (!found)
!                               elog(ERROR, "failed to find subplan index for 
relation %u", partOid);
  
!                       show_actual_target(mtstate, node, resultRelInfo, 
fdwroutine, false, k,
!                                                          fdwroutine ? 
foperation : operation, true, es);
                }
        }
  
***************
*** 2968,2973 **** show_modifytable_info(ModifyTableState *mtstate, List 
*ancestors,
--- 2971,3042 ----
  }
  
  /*
+  * Show an actual target relation
+  */
+ static void
+ show_actual_target(ModifyTableState *mtstate, ModifyTable *node,
+                                  ResultRelInfo *resultRelInfo, FdwRoutine 
*fdwroutine,
+                                  bool main_target, int subplan_index,
+                                  const char *operation, bool labeltarget, 
ExplainState *es)
+ {
+       if (labeltarget)
+       {
+               /* Open a group for this target */
+               ExplainOpenGroup("Target Table", NULL, true, es);
+ 
+               /*
+                * In text mode, decorate each target with operation type, so 
that
+                * ExplainTargetRel's output of " on foo" will read nicely.
+                */
+               if (es->format == EXPLAIN_FORMAT_TEXT)
+               {
+                       appendStringInfoSpaces(es->str, es->indent * 2);
+                       appendStringInfoString(es->str, operation);
+               }
+ 
+               /* Identify target */
+               ExplainTargetRel((Plan *) node,
+                                                
resultRelInfo->ri_RangeTableIndex,
+                                                es);
+ 
+               if (es->format == EXPLAIN_FORMAT_TEXT)
+               {
+                       appendStringInfoChar(es->str, '\n');
+                       es->indent++;
+               }
+       }
+ 
+       /* Give FDW a chance if needed */
+       if (fdwroutine != NULL &&
+               fdwroutine->ExplainForeignModify != NULL &&
+               !resultRelInfo->ri_usesFdwDirectModify)
+       {
+               List       *fdw_private;
+ 
+               if (main_target)
+                       fdw_private = (List *) list_nth(node->fdwPrivLists, 
subplan_index);
+               else
+                       fdw_private = (List *) 
list_nth(node->fdwPartitionPrivLists, subplan_index);
+ 
+               fdwroutine->ExplainForeignModify(mtstate,
+                                                                               
 resultRelInfo,
+                                                                               
 fdw_private,
+                                                                               
 main_target ? subplan_index : 0,
+                                                                               
 es);
+       }
+ 
+       if (labeltarget)
+       {
+               /* Undo the indentation we added in text format */
+               if (es->format == EXPLAIN_FORMAT_TEXT)
+                       es->indent--;
+ 
+               /* Close the group */
+               ExplainCloseGroup("Target Table", NULL, true, es);
+       }
+ }
+ 
+ /*
   * Explain the constituent plans of a ModifyTable, Append, MergeAppend,
   * BitmapAnd, or BitmapOr node.
   *
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 1403,1408 **** ExecuteTruncate(TruncateStmt *stmt)
--- 1403,1409 ----
                                                  rel,
                                                  0,    /* dummy rangetable 
index */
                                                  NULL,
+                                                 0,    /* dummy rangetable 
index */
                                                  0);
                resultRelInfo++;
        }
*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
***************
*** 114,120 **** static void ExecPartitionCheck(ResultRelInfo *resultRelInfo,
   * to be changed, however.
   */
  #define GetInsertedColumns(relinfo, estate) \
!       (rt_fetch((relinfo)->ri_RangeTableIndex, 
(estate)->es_range_table)->insertedCols)
  #define GetUpdatedColumns(relinfo, estate) \
        (rt_fetch((relinfo)->ri_RangeTableIndex, 
(estate)->es_range_table)->updatedCols)
  
--- 114,123 ----
   * to be changed, however.
   */
  #define GetInsertedColumns(relinfo, estate) \
!       (rt_fetch((relinfo)->ri_PartitionRoot ? \
!                         (relinfo)->ri_PartitionRootRTindex : \
!                         (relinfo)->ri_RangeTableIndex, \
!                         (estate)->es_range_table)->insertedCols)
  #define GetUpdatedColumns(relinfo, estate) \
        (rt_fetch((relinfo)->ri_RangeTableIndex, 
(estate)->es_range_table)->updatedCols)
  
***************
*** 854,859 **** InitPlan(QueryDesc *queryDesc, int eflags)
--- 857,863 ----
                                                          resultRelation,
                                                          resultRelationIndex,
                                                          NULL,
+                                                         0,            /* 
dummy rangetable index */
                                                          
estate->es_instrument);
                        resultRelInfo++;
                }
***************
*** 893,898 **** InitPlan(QueryDesc *queryDesc, int eflags)
--- 897,903 ----
                                                                  resultRelDesc,
                                                                  lfirst_int(l),
                                                                  NULL,
+                                                                 0,            
/* dummy rangetable index */
                                                                  
estate->es_instrument);
                                resultRelInfo++;
                        }
***************
*** 1102,1113 **** CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType 
operation)
--- 1107,1121 ----
        Relation        resultRel = resultRelInfo->ri_RelationDesc;
        TriggerDesc *trigDesc = resultRel->trigdesc;
        FdwRoutine *fdwroutine;
+       bool            is_valid;
  
        switch (resultRel->rd_rel->relkind)
        {
                case RELKIND_RELATION:
                case RELKIND_PARTITIONED_TABLE:
                        CheckCmdReplicaIdentity(resultRel, operation);
+                       if (resultRelInfo->ri_PartitionRoot && operation == 
CMD_INSERT)
+                               resultRelInfo->ri_PartitionIsValid = true;
                        break;
                case RELKIND_SEQUENCE:
                        ereport(ERROR,
***************
*** 1174,1196 **** CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType 
operation)
                        switch (operation)
                        {
                                case CMD_INSERT:
!                                       /*
!                                        * If foreign partition to do 
tuple-routing for, skip the
!                                        * check; it's disallowed elsewhere.
!                                        */
!                                       if (resultRelInfo->ri_PartitionRoot)
!                                               break;
                                        if (fdwroutine->ExecForeignInsert == 
NULL)
!                                               ereport(ERROR,
!                                                               
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
!                                                                errmsg("cannot 
insert into foreign table \"%s\"",
!                                                                               
RelationGetRelationName(resultRel))));
                                        if (fdwroutine->IsForeignRelUpdatable 
!= NULL &&
                                                
(fdwroutine->IsForeignRelUpdatable(resultRel) & (1 << CMD_INSERT)) == 0)
!                                               ereport(ERROR,
!                                                               
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
!                                                                
errmsg("foreign table \"%s\" does not allow inserts",
!                                                                               
RelationGetRelationName(resultRel))));
                                        break;
                                case CMD_UPDATE:
                                        if (fdwroutine->ExecForeignUpdate == 
NULL)
--- 1182,1209 ----
                        switch (operation)
                        {
                                case CMD_INSERT:
!                                       is_valid = true;
                                        if (fdwroutine->ExecForeignInsert == 
NULL)
!                                       {
!                                               if 
(!resultRelInfo->ri_PartitionRoot)
!                                                       ereport(ERROR,
!                                                                       
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
!                                                                        
errmsg("cannot insert into foreign table \"%s\"",
!                                                                               
        RelationGetRelationName(resultRel))));
!                                               is_valid = false;
!                                       }
                                        if (fdwroutine->IsForeignRelUpdatable 
!= NULL &&
                                                
(fdwroutine->IsForeignRelUpdatable(resultRel) & (1 << CMD_INSERT)) == 0)
!                                       {
!                                               if 
(!resultRelInfo->ri_PartitionRoot)
!                                                       ereport(ERROR,
!                                                                       
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
!                                                                        
errmsg("foreign table \"%s\" does not allow inserts",
!                                                                               
        RelationGetRelationName(resultRel))));
!                                               is_valid = false;
!                                       }
!                                       if (resultRelInfo->ri_PartitionRoot)
!                                               
resultRelInfo->ri_PartitionIsValid = is_valid;
                                        break;
                                case CMD_UPDATE:
                                        if (fdwroutine->ExecForeignUpdate == 
NULL)
***************
*** 1308,1313 **** InitResultRelInfo(ResultRelInfo *resultRelInfo,
--- 1321,1327 ----
                                  Relation resultRelationDesc,
                                  Index resultRelationIndex,
                                  Relation partition_root,
+                                 Index partition_root_rtindex,
                                  int instrument_options)
  {
        List       *partition_check = NIL;
***************
*** 1365,1370 **** InitResultRelInfo(ResultRelInfo *resultRelInfo,
--- 1379,1386 ----
  
        resultRelInfo->ri_PartitionCheck = partition_check;
        resultRelInfo->ri_PartitionRoot = partition_root;
+       resultRelInfo->ri_PartitionRootRTindex = partition_root_rtindex;
+       resultRelInfo->ri_PartitionIsValid = false;
  }
  
  /*
***************
*** 1447,1452 **** ExecGetTriggerResultRel(EState *estate, Oid relid)
--- 1463,1469 ----
                                          rel,
                                          0,            /* dummy rangetable 
index */
                                          NULL,
+                                         0,            /* dummy rangetable 
index */
                                          estate->es_instrument);
        estate->es_trig_target_relations =
                lappend(estate->es_trig_target_relations, rInfo);
***************
*** 3245,3250 **** EvalPlanQualEnd(EPQState *epqstate)
--- 3262,3269 ----
   * Output arguments:
   * 'pd' receives an array of PartitionDispatch objects with one entry for
   *            every partitioned table in the partition tree
+  * 'leaf_parts' receives a list of relation OIDs with one entry for every leaf
+  *            partition in the partition tree
   * 'partitions' receives an array of ResultRelInfo objects with one entry for
   *            every leaf partition in the partition tree
   * 'tup_conv_maps' receives an array of TupleConversionMap objects with one
***************
*** 3265,3291 **** EvalPlanQualEnd(EPQState *epqstate)
   */
  void
  ExecSetupPartitionTupleRouting(Relation rel,
-                                                          Index resultRTindex,
-                                                          EState *estate,
                                                           PartitionDispatch 
**pd,
                                                           ResultRelInfo 
**partitions,
                                                           TupleConversionMap 
***tup_conv_maps,
                                                           TupleTableSlot 
**partition_tuple_slot,
                                                           int *num_parted, int 
*num_partitions)
  {
-       TupleDesc       tupDesc = RelationGetDescr(rel);
-       List       *leaf_parts;
-       ListCell   *cell;
-       int                     i;
-       ResultRelInfo *leaf_part_rri;
- 
        /*
         * Get the information about the partition tree after locking all the
         * partitions.
         */
        (void) find_all_inheritors(RelationGetRelid(rel), RowExclusiveLock, 
NULL);
!       *pd = RelationGetPartitionDispatchInfo(rel, num_parted, &leaf_parts);
!       *num_partitions = list_length(leaf_parts);
        *partitions = (ResultRelInfo *) palloc(*num_partitions *
                                                                                
   sizeof(ResultRelInfo));
        *tup_conv_maps = (TupleConversionMap **) palloc0(*num_partitions *
--- 3284,3303 ----
   */
  void
  ExecSetupPartitionTupleRouting(Relation rel,
                                                           PartitionDispatch 
**pd,
+                                                          List **leaf_parts,
                                                           ResultRelInfo 
**partitions,
                                                           TupleConversionMap 
***tup_conv_maps,
                                                           TupleTableSlot 
**partition_tuple_slot,
                                                           int *num_parted, int 
*num_partitions)
  {
        /*
         * Get the information about the partition tree after locking all the
         * partitions.
         */
        (void) find_all_inheritors(RelationGetRelid(rel), RowExclusiveLock, 
NULL);
!       *pd = RelationGetPartitionDispatchInfo(rel, num_parted, leaf_parts);
!       *num_partitions = list_length(*leaf_parts);
        *partitions = (ResultRelInfo *) palloc(*num_partitions *
                                                                                
   sizeof(ResultRelInfo));
        *tup_conv_maps = (TupleConversionMap **) palloc0(*num_partitions *
***************
*** 3298,3352 **** ExecSetupPartitionTupleRouting(Relation rel,
         * processing.
         */
        *partition_tuple_slot = MakeTupleTableSlot();
  
!       leaf_part_rri = *partitions;
!       i = 0;
!       foreach(cell, leaf_parts)
!       {
!               Relation        partrel;
!               TupleDesc       part_tupdesc;
! 
!               /*
!                * We locked all the partitions above including the leaf 
partitions.
!                * Note that each of the relations in *partitions are eventually
!                * closed by the caller.
!                */
!               partrel = heap_open(lfirst_oid(cell), NoLock);
!               part_tupdesc = RelationGetDescr(partrel);
! 
!               /*
!                * Save a tuple conversion map to convert a tuple routed to this
!                * partition from the parent's type to the partition's.
!                */
!               (*tup_conv_maps)[i] = convert_tuples_by_name(tupDesc, 
part_tupdesc,
!                                                                               
                         gettext_noop("could not convert row type"));
  
!               InitResultRelInfo(leaf_part_rri,
!                                                 partrel,
!                                                 resultRTindex,
!                                                 rel,
!                                                 estate->es_instrument);
  
!               /*
!                * Verify result relation is a valid target for INSERT.
!                */
!               CheckValidResultRel(leaf_part_rri, CMD_INSERT);
  
!               /*
!                * Open partition indices (remember we do not support ON 
CONFLICT in
!                * case of partitioned tables, so we do not need support 
information
!                * for speculative insertion)
!                */
!               if (leaf_part_rri->ri_RelationDesc->rd_rel->relhasindex &&
!                       leaf_part_rri->ri_IndexRelationDescs == NULL)
!                       ExecOpenIndices(leaf_part_rri, false);
  
!               estate->es_leaf_result_relations =
!                       lappend(estate->es_leaf_result_relations, 
leaf_part_rri);
  
!               leaf_part_rri++;
!               i++;
!       }
  }
  
  /*
--- 3310,3366 ----
         * processing.
         */
        *partition_tuple_slot = MakeTupleTableSlot();
+ }
  
! /*
!  * ExecInitPartition -- Prepare tuple conversion map and ResultRelInfo for
!  * the partition with OID 'partOid'
!  */
! void
! ExecInitPartition(EState *estate,
!                                 Oid partOid,
!                                 Index partRTindex,
!                                 ResultRelInfo *rootRelInfo,
!                                 ResultRelInfo *partRelInfo,
!                                 TupleConversionMap **partTupConvMap)
! {
!       Relation        rootrel = rootRelInfo->ri_RelationDesc;
!       Relation        partrel;
  
!       /*
!        * We assume that ExecSetupPartitionTupleRouting() already locked the
!        * partition, so we need no lock here.  The partition must be closed
!        * by the caller.
!        */
!       partrel = heap_open(partOid, NoLock);
  
!       /*
!        * Save a tuple conversion map to convert a tuple routed to the 
partition
!        * from the parent's type to the partition's.
!        */
!       *partTupConvMap = convert_tuples_by_name(RelationGetDescr(rootrel),
!                                                                               
         RelationGetDescr(partrel),
!                                                                               
         gettext_noop("could not convert row type"));
  
!       /* Save a ResultRelInfo data for the partition. */
!       InitResultRelInfo(partRelInfo,
!                                         partrel,
!                                         partRTindex,
!                                         rootrel,
!                                         rootRelInfo->ri_RangeTableIndex,
!                                         estate->es_instrument);
  
!       /*
!        * Open partition indices (remember we do not support ON CONFLICT in
!        * case of partitioned tables, so we do not need support information
!        * for speculative insertion)
!        */
!       if (partRelInfo->ri_RelationDesc->rd_rel->relhasindex &&
!               partRelInfo->ri_IndexRelationDescs == NULL)
!               ExecOpenIndices(partRelInfo, false);
  
!       estate->es_leaf_result_relations =
!               lappend(estate->es_leaf_result_relations, partRelInfo);
  }
  
  /*
*** a/src/backend/executor/nodeModifyTable.c
--- b/src/backend/executor/nodeModifyTable.c
***************
*** 305,315 **** ExecInsert(ModifyTableState *mtstate,
                saved_resultRelInfo = resultRelInfo;
                resultRelInfo = mtstate->mt_partitions + leaf_part_index;
  
!               /* We do not yet have a way to insert into a foreign partition 
*/
!               if (resultRelInfo->ri_FdwRoutine)
                        ereport(ERROR,
                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
!                                        errmsg("cannot route inserted tuples 
to a foreign table")));
  
                /* For ExecInsertIndexTuples() to work on the partition's 
indexes */
                estate->es_result_relation_info = resultRelInfo;
--- 305,321 ----
                saved_resultRelInfo = resultRelInfo;
                resultRelInfo = mtstate->mt_partitions + leaf_part_index;
  
!               if (!resultRelInfo->ri_PartitionIsValid)
!               {
!                       /* Should be foreign */
!                       Assert(resultRelInfo->ri_FdwRoutine);
! 
!                       /* We cannot insert into this foreign partition */
                        ereport(ERROR,
                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
!                                        errmsg("cannot route inserted tuples 
to foreign table \"%s\"",
!                                                       
RelationGetRelationName(resultRelInfo->ri_RelationDesc))));
!               }
  
                /* For ExecInsertIndexTuples() to work on the partition's 
indexes */
                estate->es_result_relation_info = resultRelInfo;
***************
*** 1912,1927 **** ExecInitModifyTable(ModifyTable *node, EState *estate, int 
eflags)
                rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
        {
                PartitionDispatch *partition_dispatch_info;
                ResultRelInfo *partitions;
                TupleConversionMap **partition_tupconv_maps;
                TupleTableSlot *partition_tuple_slot;
                int                     num_parted,
                                        num_partitions;
  
                ExecSetupPartitionTupleRouting(rel,
-                                                                          
node->nominalRelation,
-                                                                          
estate,
                                                                           
&partition_dispatch_info,
                                                                           
&partitions,
                                                                           
&partition_tupconv_maps,
                                                                           
&partition_tuple_slot,
--- 1918,1935 ----
                rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
        {
                PartitionDispatch *partition_dispatch_info;
+               List       *partition_oids;
                ResultRelInfo *partitions;
                TupleConversionMap **partition_tupconv_maps;
                TupleTableSlot *partition_tuple_slot;
                int                     num_parted,
                                        num_partitions;
+               Index           rootRTindex;
+               ResultRelInfo *partRelInfo;
  
                ExecSetupPartitionTupleRouting(rel,
                                                                           
&partition_dispatch_info,
+                                                                          
&partition_oids,
                                                                           
&partitions,
                                                                           
&partition_tupconv_maps,
                                                                           
&partition_tuple_slot,
***************
*** 1932,1937 **** ExecInitModifyTable(ModifyTable *node, EState *estate, int 
eflags)
--- 1940,2002 ----
                mtstate->mt_num_partitions = num_partitions;
                mtstate->mt_partition_tupconv_maps = partition_tupconv_maps;
                mtstate->mt_partition_tuple_slot = partition_tuple_slot;
+ 
+               rootRTindex = mtstate->resultRelInfo->ri_RangeTableIndex;
+               partRelInfo = partitions;
+               i = 0;
+               foreach(l, partition_oids)
+               {
+                       Oid                     partOid = lfirst_oid(l);
+                       Index           partRTindex;
+                       int                     j;
+                       ListCell   *cell;
+                       bool            found;
+ 
+                       /* First, find the RT index for the partition */
+                       found = false;
+                       j = 0;
+                       foreach(cell, node->partition_rels)
+                       {
+                               partRTindex = lfirst_int(cell);
+ 
+                               if (getrelid(partRTindex, 
estate->es_range_table) == partOid)
+                               {
+                                       found = true;
+                                       break;
+                               }
+                               j++;
+                       }
+                       if (!found)
+                               elog(ERROR, "failed to find range table index 
for relation %u", partOid);
+ 
+                       /* Prepare map and ResultRelInfo for the partition */
+                       ExecInitPartition(estate,
+                                                         partOid,
+                                                         partRTindex,
+                                                         
mtstate->resultRelInfo,
+                                                         partRelInfo,
+                                                         
&partition_tupconv_maps[i]);
+ 
+                       /* Verify the partition is a valid target for INSERT */
+                       CheckValidResultRel(partRelInfo, CMD_INSERT);
+ 
+                       /* If so, let the FDW init itself for the partition */
+                       if (partRelInfo->ri_PartitionIsValid &&
+                               partRelInfo->ri_FdwRoutine != NULL &&
+                               partRelInfo->ri_FdwRoutine->BeginForeignModify 
!= NULL)
+                       {
+                               List       *fdw_private = (List *) 
list_nth(node->fdwPartitionPrivLists, j);
+ 
+                               
partRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate,
+                                                                               
                                           partRelInfo,
+                                                                               
                                           fdw_private,
+                                                                               
                                           0,
+                                                                               
                                           eflags);
+                       }
+ 
+                       partRelInfo++;
+                       i++;
+               }
        }
  
        /* Build state for collecting transition tuples */
***************
*** 2361,2366 **** ExecEndModifyTable(ModifyTableState *node)
--- 2426,2436 ----
        {
                ResultRelInfo *resultRelInfo = node->mt_partitions + i;
  
+               if (resultRelInfo->ri_FdwRoutine != NULL &&
+                       resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
+                       
resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
+                                                                               
                                   resultRelInfo);
+ 
                ExecCloseIndices(resultRelInfo);
                heap_close(resultRelInfo->ri_RelationDesc, NoLock);
        }
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 204,209 **** _copyModifyTable(const ModifyTable *from)
--- 204,210 ----
        COPY_SCALAR_FIELD(canSetTag);
        COPY_SCALAR_FIELD(nominalRelation);
        COPY_NODE_FIELD(partitioned_rels);
+       COPY_NODE_FIELD(partition_rels);
        COPY_NODE_FIELD(resultRelations);
        COPY_SCALAR_FIELD(resultRelIndex);
        COPY_SCALAR_FIELD(rootResultRelIndex);
***************
*** 220,225 **** _copyModifyTable(const ModifyTable *from)
--- 221,227 ----
        COPY_NODE_FIELD(onConflictWhere);
        COPY_SCALAR_FIELD(exclRelRTI);
        COPY_NODE_FIELD(exclRelTlist);
+       COPY_NODE_FIELD(fdwPartitionPrivLists);
  
        return newnode;
  }
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
***************
*** 367,372 **** _outModifyTable(StringInfo str, const ModifyTable *node)
--- 367,373 ----
        WRITE_BOOL_FIELD(canSetTag);
        WRITE_UINT_FIELD(nominalRelation);
        WRITE_NODE_FIELD(partitioned_rels);
+       WRITE_NODE_FIELD(partition_rels);
        WRITE_NODE_FIELD(resultRelations);
        WRITE_INT_FIELD(resultRelIndex);
        WRITE_INT_FIELD(rootResultRelIndex);
***************
*** 383,388 **** _outModifyTable(StringInfo str, const ModifyTable *node)
--- 384,390 ----
        WRITE_NODE_FIELD(onConflictWhere);
        WRITE_UINT_FIELD(exclRelRTI);
        WRITE_NODE_FIELD(exclRelTlist);
+       WRITE_NODE_FIELD(fdwPartitionPrivLists);
  }
  
  static void
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
***************
*** 1562,1567 **** _readModifyTable(void)
--- 1562,1568 ----
        READ_BOOL_FIELD(canSetTag);
        READ_UINT_FIELD(nominalRelation);
        READ_NODE_FIELD(partitioned_rels);
+       READ_NODE_FIELD(partition_rels);
        READ_NODE_FIELD(resultRelations);
        READ_INT_FIELD(resultRelIndex);
        READ_INT_FIELD(rootResultRelIndex);
***************
*** 1578,1583 **** _readModifyTable(void)
--- 1579,1585 ----
        READ_NODE_FIELD(onConflictWhere);
        READ_UINT_FIELD(exclRelRTI);
        READ_NODE_FIELD(exclRelTlist);
+       READ_NODE_FIELD(fdwPartitionPrivLists);
  
        READ_DONE();
  }
*** a/src/backend/optimizer/plan/createplan.c
--- b/src/backend/optimizer/plan/createplan.c
***************
*** 35,40 ****
--- 35,41 ----
  #include "optimizer/planmain.h"
  #include "optimizer/planner.h"
  #include "optimizer/predtest.h"
+ #include "optimizer/prep.h"
  #include "optimizer/restrictinfo.h"
  #include "optimizer/subselect.h"
  #include "optimizer/tlist.h"
***************
*** 6412,6417 **** make_modifytable(PlannerInfo *root,
--- 6413,6419 ----
        ModifyTable *node = makeNode(ModifyTable);
        List       *fdw_private_list;
        Bitmapset  *direct_modify_plans;
+       List       *partition_rels;
        ListCell   *lc;
        int                     i;
  
***************
*** 6535,6540 **** make_modifytable(PlannerInfo *root,
--- 6537,6637 ----
        node->fdwPrivLists = fdw_private_list;
        node->fdwDirectModifyPlans = direct_modify_plans;
  
+       /*
+        * Also, if this is an INSERT into a partitioned table, build a list of
+        * RT indexes of partitions, and for each partition that is a foreign 
table,
+        * allow the FDW to construct private plan data and accumulate it all 
into
+        * another list.
+        */
+       partition_rels = NIL;
+       fdw_private_list = NIL;
+       if (operation == CMD_INSERT)
+       {
+               Index           rti = linitial_int(resultRelations);
+ 
+               if (planner_rt_fetch(rti, root)->relkind == 
RELKIND_PARTITIONED_TABLE)
+               {
+                       Query      *saved_query = root->parse;
+                       Index           saved_nominalRelation = 
node->nominalRelation;
+                       List       *saved_resultRelations = 
node->resultRelations;
+                       List       *saved_withCheckOptionLists = 
node->withCheckOptionLists;
+                       List       *saved_returningLists = node->returningLists;
+                       Plan       *subplan = (Plan *) linitial(node->plans);
+                       List       *saved_tlist = subplan->targetlist;
+ 
+                       foreach(lc, root->append_rel_list)
+                       {
+                               AppendRelInfo *appinfo = (AppendRelInfo *) 
lfirst(lc);
+                               Index           part_rti = appinfo->child_relid;
+                               RangeTblEntry *part_rte;
+                               FdwRoutine *fdwroutine = NULL;
+                               List       *fdw_private = NIL;
+ 
+                               /* append_rel_list contains all append rels; 
ignore others */
+                               if (appinfo->parent_relid != rti)
+                                       continue;
+ 
+                               part_rte = planner_rt_fetch(part_rti, root);
+                               Assert(part_rte->rtekind == RTE_RELATION);
+ 
+                               if (part_rte->relkind == RELKIND_FOREIGN_TABLE)
+                                       fdwroutine = 
GetFdwRoutineByRelId(part_rte->relid);
+ 
+                               if (fdwroutine != NULL &&
+                                       fdwroutine->PlanForeignModify != NULL)
+                               {
+                                       List       *tlist;
+ 
+                                       /*
+                                        * Replace the Query node with the 
modified one that has
+                                        * this partition as target.
+                                        */
+                                       root->parse = (Query *)
+                                               adjust_appendrel_attrs(root,
+                                                                               
           (Node *) root->parse,
+                                                                               
           1, &appinfo);
+ 
+                                       /*
+                                        * Likewise for the ModifyTable node.
+                                        */
+                                       node->nominalRelation = part_rti;
+                                       node->resultRelations = 
list_make1_int(part_rti);
+                                       node->withCheckOptionLists =
+                                               
list_make1(root->parse->withCheckOptions);
+                                       node->returningLists =
+                                               
list_make1(root->parse->returningList);
+ 
+                                       /*
+                                        * Adjust the subplan's tlist because 
the column list of
+                                        * this partition might have a 
different column order
+                                        * and/or a different set of dropped 
columns than the
+                                        * partitioned table root.
+                                        */
+                                       tlist = preprocess_targetlist(root,
+                                                                               
                  root->parse->targetList);
+                                       subplan->targetlist = tlist;
+ 
+                                       fdw_private = 
fdwroutine->PlanForeignModify(root,
+                                                                               
                                                node,
+                                                                               
                                                part_rti,
+                                                                               
                                                0);
+                               }
+ 
+                               partition_rels = lappend_int(partition_rels, 
part_rti);
+                               fdw_private_list = lappend(fdw_private_list, 
fdw_private);
+                       }
+ 
+                       root->parse = saved_query;
+                       node->nominalRelation = saved_nominalRelation;
+                       node->resultRelations = saved_resultRelations;
+                       node->withCheckOptionLists = saved_withCheckOptionLists;
+                       node->returningLists = saved_returningLists;
+                       subplan->targetlist = saved_tlist;
+               }
+       }
+       node->partition_rels = partition_rels;
+       node->fdwPartitionPrivLists = fdw_private_list;
+ 
        return node;
  }
  
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
***************
*** 840,848 **** subquery_planner(PlannerGlobal *glob, Query *parse,
  
        /*
         * Do the main planning.  If we have an inherited target relation, that
!        * needs special processing, else go straight to grouping_planner.
         */
!       if (parse->resultRelation &&
                rt_fetch(parse->resultRelation, parse->rtable)->inh)
                inheritance_planner(root);
        else
--- 840,850 ----
  
        /*
         * Do the main planning.  If we have an inherited target relation, that
!        * needs special processing except for INSERT cases, else go straight to
!        * grouping_planner.
         */
!       if ((parse->commandType == CMD_UPDATE ||
!                parse->commandType == CMD_DELETE) &&
                rt_fetch(parse->resultRelation, parse->rtable)->inh)
                inheritance_planner(root);
        else
*** a/src/backend/optimizer/plan/setrefs.c
--- b/src/backend/optimizer/plan/setrefs.c
***************
*** 850,855 **** set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
--- 850,859 ----
                                {
                                        lfirst_int(l) += rtoffset;
                                }
+                               foreach(l, splan->partition_rels)
+                               {
+                                       lfirst_int(l) += rtoffset;
+                               }
                                foreach(l, splan->resultRelations)
                                {
                                        lfirst_int(l) += rtoffset;
*** a/src/backend/optimizer/prep/preptlist.c
--- b/src/backend/optimizer/prep/preptlist.c
***************
*** 22,30 ****
   * The fact that rewriteTargetListIU sorts non-resjunk tlist entries by column
   * position, which expand_targetlist depends on, violates the above comment
   * because the sorting is only valid for the parent relation.  In inherited
!  * UPDATE cases, adjust_inherited_tlist runs in between to take care of fixing
!  * the tlists for child tables to keep expand_targetlist happy.  We do it like
!  * that because it's faster in typical non-inherited cases.
   *
   *
   * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
--- 22,30 ----
   * The fact that rewriteTargetListIU sorts non-resjunk tlist entries by column
   * position, which expand_targetlist depends on, violates the above comment
   * because the sorting is only valid for the parent relation.  In inherited
!  * INSERT/UPDATE cases, adjust_inherited_tlist runs in between to take care of
!  * fixing the tlists for child tables to keep expand_targetlist happy.  We do
!  * it like that because it's faster in typical non-inherited cases.
   *
   *
   * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
*** a/src/backend/optimizer/prep/prepunion.c
--- b/src/backend/optimizer/prep/prepunion.c
***************
*** 1929,1936 **** adjust_appendrel_attrs(PlannerInfo *root, Node *node, int 
nappinfos,
                        if (newnode->resultRelation == appinfo->parent_relid)
                        {
                                newnode->resultRelation = appinfo->child_relid;
!                               /* Fix tlist resnos too, if it's inherited 
UPDATE */
!                               if (newnode->commandType == CMD_UPDATE)
                                        newnode->targetList =
                                                
adjust_inherited_tlist(newnode->targetList,
                                                                                
           appinfo);
--- 1929,1937 ----
                        if (newnode->resultRelation == appinfo->parent_relid)
                        {
                                newnode->resultRelation = appinfo->child_relid;
!                               /* Fix tlist resnos too, if it's inherited 
INSERT/UPDATE */
!                               if (newnode->commandType == CMD_INSERT ||
!                                       newnode->commandType == CMD_UPDATE)
                                        newnode->targetList =
                                                
adjust_inherited_tlist(newnode->targetList,
                                                                                
           appinfo);
***************
*** 2227,2233 **** adjust_child_relids(Relids relids, int nappinfos, 
AppendRelInfo **appinfos)
  }
  
  /*
!  * Adjust the targetlist entries of an inherited UPDATE operation
   *
   * The expressions have already been fixed, but we have to make sure that
   * the target resnos match the child table (they may not, in the case of
--- 2228,2234 ----
  }
  
  /*
!  * Adjust the targetlist entries of an inherited INSERT/UPDATE operation
   *
   * The expressions have already been fixed, but we have to make sure that
   * the target resnos match the child table (they may not, in the case of
***************
*** 2239,2246 **** adjust_child_relids(Relids relids, int nappinfos, 
AppendRelInfo **appinfos)
   * The given tlist has already been through expression_tree_mutator;
   * therefore the TargetEntry nodes are fresh copies that it's okay to
   * scribble on.
-  *
-  * Note that this is not needed for INSERT because INSERT isn't inheritable.
   */
  static List *
  adjust_inherited_tlist(List *tlist, AppendRelInfo *context)
--- 2240,2245 ----
*** a/src/backend/parser/analyze.c
--- b/src/backend/parser/analyze.c
***************
*** 542,547 **** transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
--- 542,554 ----
        qry->resultRelation = setTargetTable(pstate, stmt->relation,
                                                                                
 false, false, targetPerms);
  
+       /*
+        * If the target table is a partitioned table, reset the inh flag to 
true.
+        */
+       rte = pstate->p_target_rangetblentry;
+       if (rte->relkind == RELKIND_PARTITIONED_TABLE)
+               rte->inh = true;
+ 
        /* Validate stmt->cols list, or build default list if no list given */
        icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos);
        Assert(list_length(icolumns) == list_length(attrnos));
*** a/src/backend/replication/logical/worker.c
--- b/src/backend/replication/logical/worker.c
***************
*** 198,204 **** create_estate_for_relation(LogicalRepRelMapEntry *rel)
        estate->es_range_table = list_make1(rte);
  
        resultRelInfo = makeNode(ResultRelInfo);
!       InitResultRelInfo(resultRelInfo, rel->localrel, 1, NULL, 0);
  
        estate->es_result_relations = resultRelInfo;
        estate->es_num_result_relations = 1;
--- 198,204 ----
        estate->es_range_table = list_make1(rte);
  
        resultRelInfo = makeNode(ResultRelInfo);
!       InitResultRelInfo(resultRelInfo, rel->localrel, 1, NULL, 0, 0);
  
        estate->es_result_relations = resultRelInfo;
        estate->es_num_result_relations = 1;
*** a/src/backend/rewrite/rewriteHandler.c
--- b/src/backend/rewrite/rewriteHandler.c
***************
*** 2890,2902 **** rewriteTargetView(Query *parsetree, Relation view)
        new_rt_index = list_length(parsetree->rtable);
  
        /*
-        * INSERTs never inherit.  For UPDATE/DELETE, we use the view query's
-        * inheritance flag for the base relation.
-        */
-       if (parsetree->commandType == CMD_INSERT)
-               new_rte->inh = false;
- 
-       /*
         * Adjust the view's targetlist Vars to reference the new target RTE, ie
         * make their varnos be new_rt_index instead of base_rt_index.  There 
can
         * be no Vars for other rels in the tlist, so this is sufficient to pull
--- 2890,2895 ----
*** a/src/include/executor/executor.h
--- b/src/include/executor/executor.h
***************
*** 182,187 **** extern void InitResultRelInfo(ResultRelInfo *resultRelInfo,
--- 182,188 ----
                                  Relation resultRelationDesc,
                                  Index resultRelationIndex,
                                  Relation partition_root,
+                                 Index partition_root_rtindex,
                                  int instrument_options);
  extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid);
  extern void ExecCleanUpTriggerState(EState *estate);
***************
*** 207,219 **** extern void EvalPlanQualSetTuple(EPQState *epqstate, Index rti,
                                         HeapTuple tuple);
  extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
  extern void ExecSetupPartitionTupleRouting(Relation rel,
-                                                          Index resultRTindex,
-                                                          EState *estate,
                                                           PartitionDispatch 
**pd,
                                                           ResultRelInfo 
**partitions,
                                                           TupleConversionMap 
***tup_conv_maps,
                                                           TupleTableSlot 
**partition_tuple_slot,
                                                           int *num_parted, int 
*num_partitions);
  extern int ExecFindPartition(ResultRelInfo *resultRelInfo,
                                  PartitionDispatch *pd,
                                  TupleTableSlot *slot,
--- 208,225 ----
                                         HeapTuple tuple);
  extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
  extern void ExecSetupPartitionTupleRouting(Relation rel,
                                                           PartitionDispatch 
**pd,
+                                                          List **leaf_parts,
                                                           ResultRelInfo 
**partitions,
                                                           TupleConversionMap 
***tup_conv_maps,
                                                           TupleTableSlot 
**partition_tuple_slot,
                                                           int *num_parted, int 
*num_partitions);
+ extern void ExecInitPartition(EState *estate,
+                                 Oid partOid,
+                                 Index partRTindex,
+                                 ResultRelInfo *rootRelInfo,
+                                 ResultRelInfo *partRelInfo,
+                                 TupleConversionMap **partTupConvMap);
  extern int ExecFindPartition(ResultRelInfo *resultRelInfo,
                                  PartitionDispatch *pd,
                                  TupleTableSlot *slot,
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
***************
*** 412,417 **** typedef struct ResultRelInfo
--- 412,423 ----
  
        /* relation descriptor for root partitioned table */
        Relation        ri_PartitionRoot;
+ 
+       /* range table index for root partitioned table */
+       Index           ri_PartitionRootRTindex;
+ 
+       /* true when partition is legal for tuple-routing */
+       bool            ri_PartitionIsValid;
  } ResultRelInfo;
  
  /* ----------------
*** a/src/include/nodes/plannodes.h
--- b/src/include/nodes/plannodes.h
***************
*** 219,224 **** typedef struct ModifyTable
--- 219,226 ----
        Index           nominalRelation;        /* Parent RT index for use of 
EXPLAIN */
        /* RT indexes of non-leaf tables in a partition tree */
        List       *partitioned_rels;
+       List       *partition_rels;     /* RT indexes of leaf tables in a 
partition
+                                                                * tree */
        List       *resultRelations;    /* integer list of RT indexes */
        int                     resultRelIndex; /* index of first resultRel in 
plan's list */
        int                     rootResultRelIndex; /* index of the partitioned 
table root */
***************
*** 235,240 **** typedef struct ModifyTable
--- 237,244 ----
        Node       *onConflictWhere;    /* WHERE for ON CONFLICT UPDATE */
        Index           exclRelRTI;             /* RTI of the EXCLUDED pseudo 
relation */
        List       *exclRelTlist;       /* tlist of the EXCLUDED pseudo 
relation */
+       List       *fdwPartitionPrivLists;      /* per-partition FDW private 
data
+                                                                               
 * lists */
  } ModifyTable;
  
  /* ----------------
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to