Hi Maksim,

On 2017/10/02 21:37, Maksim Milyutin wrote:
On 11.09.2017 16:01, Etsuro Fujita wrote:
* 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.

Could you update your patch, it isn't applied on the actual state of master. Namely conflict in src/backend/executor/execMain.c

Attached is an updated version.

* As mentioned in "Query planning", the patch builds an RTE for each partition so that the FDW can make reference to that RTE in eg, PlanForeignModify. set_plan_references also uses such RTEs to record plan dependencies for plancache.c to invalidate cached plans. See an example for that added to the regression tests in postgres_fdw.

* As mentioned in "explain.c", the EXPLAIN shows all partitions beneath the ModifyTable node. One merit of that is we can show remote queries for foreign partitions in the output as shown above. Another one I can think of is when reporting execution stats for triggers. Here is an example for that:

postgres=# explain analyze insert into list_parted values (1, 'hi there'), (2, 'hi there');
                                                  QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Insert on list_parted (cost=0.00..0.03 rows=2 width=36) (actual time=0.375..0.375 rows=0 loops=1)
   Insert on part1
   Insert on part2
-> Values Scan on "*VALUES*" (cost=0.00..0.03 rows=2 width=36) (actual time=0.004..0.007 rows=2 loops=1)
 Planning time: 0.089 ms
 Trigger part1brtrig on part1: time=0.059 calls=1
 Trigger part2brtrig on part2: time=0.021 calls=1
 Execution time: 0.422 ms
(8 rows)

This would allow the user to understand easily that "part1" and "part2" in the trigger lines are the partitions of list_parted. So, I think it's useful to show partition info in the ModifyTable node even in the case where a partitioned table only contains plain tables.

* The patch modifies make_modifytable and ExecInitModifyTable so that the former can allow the FDW to construct private plan data for each foreign partition and accumulate it all into a list, and that the latter can perform BeginForeignModify for each partition using that plan data stored in the list passed from make_modifytable. Other changes I made to the executor are: (1) currently, we set the RT index for the root partitioned table to ri_RangeTableIndex of partitions' ResultRelInfos, but the proposed EXPLAIN requires that the partition's ri_RangeTableIndex is set to the RT index for that partition's RTE, to show that partition info in the output. So, I made that change. Because of that, ExecPartitionCheck, ExecConstraints, and ExecWithCheckOptions are adjusted accordingly. (2) I added a new member to ResultRelInfo (ie, ri_PartitionIsValid), and modified CheckValidResultRel so that it checks a given partition without throwing an error and save the result in that flag so that ExecInsert determines using that flag whether a partition chosen by ExecFindPartition is valid for tuple-routing as proposed before.

* copy.c: I still don't think it's a good idea to implement COPY tuple-routing for foreign partitions using PlanForeignModify. (I plan to propose new FDW APIs for bulkload as discussed before, to implement this feature.) So, I kept that as-is. Two things I changed there are: (1) currently, ExecSetupPartitionTupleRouting verifies partitions using CheckValidResultRel, but I don't think we need the CheckValidResultRel check in the COPY case. So, I refactored that function and checked partitions directly. (2) I think it'd be better to distinguish the error message "cannot route inserted tuples to a foreign partition" in the COPY case from the INSERT case, I changed it to "cannot route copied tuples to a foreign partition".

* Fixed some bugs, revised comments, added a bit more regression tests, and rebased the patch.

Comments are welcome!

My apologies for the very late reply.

Best regards,
Etsuro Fujita
*** a/contrib/file_fdw/input/file_fdw.source
--- b/contrib/file_fdw/input/file_fdw.source
***************
*** 176,182 **** COPY pt FROM '@abs_srcdir@/data/list2.csv' with (format 'csv', 
delimiter ',');
--- 176,188 ----
  SELECT tableoid::regclass, * FROM pt;
  SELECT tableoid::regclass, * FROM p1;
  SELECT tableoid::regclass, * FROM p2;
+ \t on
+ EXPLAIN (VERBOSE, COSTS FALSE) INSERT INTO pt VALUES (1, 'xyzzy');
+ \t off
  INSERT INTO pt VALUES (1, 'xyzzy'); -- ERROR
+ \t on
+ EXPLAIN (VERBOSE, COSTS FALSE) INSERT INTO pt VALUES (2, 'xyzzy');
+ \t off
  INSERT INTO pt VALUES (2, 'xyzzy');
  SELECT tableoid::regclass, * FROM pt;
  SELECT tableoid::regclass, * FROM p1;
*** 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;
***************
*** 341,348 **** SELECT tableoid::regclass, * FROM p2;
   p2       | 2 | qux
  (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   
--- 341,366 ----
   p2       | 2 | qux
  (2 rows)
  
+ \t on
+ EXPLAIN (VERBOSE, COSTS FALSE) INSERT INTO pt VALUES (1, 'xyzzy');
+  Insert on public.pt
+    Foreign Insert on public.p1
+    Insert on public.p2
+    ->  Result
+          Output: 1, 'xyzzy'::text
+ 
+ \t off
  INSERT INTO pt VALUES (1, 'xyzzy'); -- ERROR
! ERROR:  cannot route inserted tuples to foreign table "p1"
! \t on
! EXPLAIN (VERBOSE, COSTS FALSE) INSERT INTO pt VALUES (2, 'xyzzy');
!  Insert on public.pt
!    Foreign Insert on public.p1
!    Insert on public.p2
!    ->  Result
!          Output: 2, 'xyzzy'::text
! 
! \t off
  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,7236 ----
  drop table loct1;
  drop table loct2;
  -- ===================================================================
+ -- test tuple routing for 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;
+ -- Check INSERT into a multi-level partitioned table
+ create table mlpt (a int, b int, c varchar) partition by range (a);
+ create table mlptp1 partition of mlpt for values from (100) to (200) 
partition by range(b);
+ create table mlptp2 partition of mlpt for values from (200) to (300);
+ create table locfoo (a int check (a >= 100 and a < 200), b int check (b >= 
100 and b < 200), c varchar);
+ create table locbar (a int check (a >= 100 and a < 200), b int check (b >= 
200 and b < 300), c varchar);
+ create foreign table mlptp1p1 partition of mlptp1 for values from (100) to 
(200) server loopback options (table_name 'locfoo');
+ create foreign table mlptp1p2 partition of mlptp1 for values from (200) to 
(300) server loopback options (table_name 'locbar');
+ explain (verbose, costs off)
+ insert into mlpt values (101, 101, 'x'), (101, 201, 'y') returning *;
+                                         QUERY PLAN                            
            
+ 
------------------------------------------------------------------------------------------
+  Insert on public.mlpt
+    Output: mlpt.a, mlpt.b, mlpt.c
+    Foreign Insert on public.mlptp1p1
+      Remote SQL: INSERT INTO public.locfoo(a, b, c) VALUES ($1, $2, $3) 
RETURNING a, b, c
+    Foreign Insert on public.mlptp1p2
+      Remote SQL: INSERT INTO public.locbar(a, b, c) VALUES ($1, $2, $3) 
RETURNING a, b, c
+    Insert on public.mlptp2
+    ->  Values Scan on "*VALUES*"
+          Output: "*VALUES*".column1, "*VALUES*".column2, "*VALUES*".column3
+ (9 rows)
+ 
+ insert into mlpt values (101, 101, 'x'), (101, 201, 'y') returning *;
+   a  |  b  | c 
+ -----+-----+---
+  101 | 101 | x
+  101 | 201 | y
+ (2 rows)
+ 
+ select tableoid::regclass, * FROM mlpt;
+  tableoid |  a  |  b  | c 
+ ----------+-----+-----+---
+  mlptp1p1 | 101 | 101 | x
+  mlptp1p2 | 101 | 201 | y
+ (2 rows)
+ 
+ select tableoid::regclass, * FROM mlptp1;
+  tableoid |  a  |  b  | c 
+ ----------+-----+-----+---
+  mlptp1p1 | 101 | 101 | x
+  mlptp1p2 | 101 | 201 | y
+ (2 rows)
+ 
+ select tableoid::regclass, * FROM mlptp2;
+  tableoid | a | b | c 
+ ----------+---+---+---
+ (0 rows)
+ 
+ select tableoid::regclass, * FROM mlptp1p1;
+  tableoid |  a  |  b  | c 
+ ----------+-----+-----+---
+  mlptp1p1 | 101 | 101 | x
+ (1 row)
+ 
+ select tableoid::regclass, * FROM mlptp1p2;
+  tableoid |  a  |  b  | c 
+ ----------+-----+-----+---
+  mlptp1p2 | 101 | 201 | y
+ (1 row)
+ 
+ drop table mlpt;
+ drop table locfoo;
+ 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,1730 ----
  drop table loct2;
  
  -- ===================================================================
+ -- test tuple routing for 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;
+ 
+ -- Check INSERT into a multi-level partitioned table
+ create table mlpt (a int, b int, c varchar) partition by range (a);
+ create table mlptp1 partition of mlpt for values from (100) to (200) 
partition by range(b);
+ create table mlptp2 partition of mlpt for values from (200) to (300);
+ create table locfoo (a int check (a >= 100 and a < 200), b int check (b >= 
100 and b < 200), c varchar);
+ create table locbar (a int check (a >= 100 and a < 200), b int check (b >= 
200 and b < 300), c varchar);
+ create foreign table mlptp1p1 partition of mlptp1 for values from (100) to 
(200) server loopback options (table_name 'locfoo');
+ create foreign table mlptp1p2 partition of mlptp1 for values from (200) to 
(300) server loopback options (table_name 'locbar');
+ 
+ explain (verbose, costs off)
+ insert into mlpt values (101, 101, 'x'), (101, 201, 'y') returning *;
+ insert into mlpt values (101, 101, 'x'), (101, 201, 'y') returning *;
+ 
+ select tableoid::regclass, * FROM mlpt;
+ select tableoid::regclass, * FROM mlptp1;
+ select tableoid::regclass, * FROM mlptp2;
+ select tableoid::regclass, * FROM mlptp1p1;
+ select tableoid::regclass, * FROM mlptp1p2;
+ 
+ drop table mlpt;
+ drop table locfoo;
+ drop table locbar;
+ 
+ -- ===================================================================
  -- test IMPORT FOREIGN SCHEMA
  -- ===================================================================
  
*** a/src/backend/commands/copy.c
--- b/src/backend/commands/copy.c
***************
*** 2459,2474 **** 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,
--- 2459,2477 ----
        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,
***************
*** 2480,2485 **** CopyFrom(CopyState cstate)
--- 2483,2522 ----
                cstate->partition_tupconv_maps = partition_tupconv_maps;
                cstate->partition_tuple_slot = partition_tuple_slot;
  
+               partRelInfo = (ResultRelInfo *) palloc0(num_partitions *
+                                                                               
                sizeof(ResultRelInfo));
+               i = 0;
+               foreach(l, partition_oids)
+               {
+                       Oid                     partOid = lfirst_oid(l);
+                       Relation        partrel;
+ 
+                       /* Prepare ResultRelInfo and map for the partition */
+                       ExecInitPartition(estate,
+                                                         resultRelInfo,
+                                                         partOid,
+                                                         0,            /* 
dummy rangetable index */
+                                                         partRelInfo,
+                                                         
&partition_tupconv_maps[i]);
+                       partitions[i] = partRelInfo;
+ 
+                       /* 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
+                       {
+                               /* The partition 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
***************
*** 2628,2638 **** 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
--- 2665,2680 ----
                        saved_resultRelInfo = resultRelInfo;
                        resultRelInfo = cstate->partitions[leaf_part_index];
  
!                       if (!resultRelInfo->ri_PartitionIsValid)
!                       {
!                               /* The partition should be foreign */
!                               Assert(resultRelInfo->ri_FdwRoutine);
! 
!                               /* We do not yet have a way to copy into a 
foreign partition */
                                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,
+                                  const char *operation, bool labeltarget,
+                                  bool main_target, int subplan_index, 
ExplainState *es);
  static void ExplainMemberNodes(List *plans, PlanState **planstates,
                                   List *ancestors, ExplainState *es);
  static void ExplainSubPlans(List *plans, List *ancestors,
***************
*** 828,833 **** ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
--- 832,848 ----
                        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;
***************
*** 2855,2911 **** show_modifytable_info(ModifyTableState *mtstate, List 
*ancestors,
                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);
!               }
        }
  
        /* Gather names of ON CONFLICT arbiter indexes */
--- 2870,2896 ----
                ResultRelInfo *resultRelInfo = mtstate->resultRelInfo + j;
                FdwRoutine *fdwroutine = resultRelInfo->ri_FdwRoutine;
  
!               show_actual_target(mtstate, node, resultRelInfo, fdwroutine,
!                                                  fdwroutine ? foperation : 
operation,
!                                                  labeltargets, true, j, es);
!       }
  
!       /* Print partition tables if needed */
!       if (mtstate->mt_num_partitions > 0)
!       {
!               ExplainOpenGroup("Partition Tables", "Partition Tables", false, 
es);
  
!               for (j = 0; j < mtstate->mt_num_partitions; j++)
                {
!                       ResultRelInfo *resultRelInfo = 
mtstate->mt_partitions[j];
!                       FdwRoutine *fdwroutine = resultRelInfo->ri_FdwRoutine;
  
!                       show_actual_target(mtstate, node, resultRelInfo, 
fdwroutine,
!                                                          fdwroutine ? 
foperation : operation,
!                                                          true, false, j, es);
                }
  
!               ExplainCloseGroup("Partition Tables", "Partition Tables", 
false, es);
        }
  
        /* Gather names of ON CONFLICT arbiter indexes */
***************
*** 2962,2967 **** show_modifytable_info(ModifyTableState *mtstate, List 
*ancestors,
--- 2947,3024 ----
  }
  
  /*
+  * Show an actual target relation
+  */
+ static void
+ show_actual_target(ModifyTableState *mtstate, ModifyTable *node,
+                                  ResultRelInfo *resultRelInfo, FdwRoutine 
*fdwroutine,
+                                  const char *operation, bool labeltarget,
+                                  bool main_target, int subplan_index, 
ExplainState *es)
+ {
+       if (labeltarget)
+       {
+               /* Open a group for this target */
+               if (main_target)
+                       ExplainOpenGroup("Target Table", NULL, true, es);
+               else
+                       ExplainOpenGroup("Partition 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 */
+               if (main_target)
+                       ExplainCloseGroup("Target Table", NULL, true, es);
+               else
+                       ExplainCloseGroup("Partition Table", NULL, true, es);
+       }
+ }
+ 
+ /*
   * Explain the constituent plans of a ModifyTable, Append, MergeAppend,
   * BitmapAnd, or BitmapOr node.
   *
*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
***************
*** 1102,1113 **** CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType 
operation)
--- 1102,1116 ----
        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,1197 **** 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)
--- 1177,1204 ----
                        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,1314 **** void
  InitResultRelInfo(ResultRelInfo *resultRelInfo,
                                  Relation resultRelationDesc,
                                  Index resultRelationIndex,
!                                 Relation partition_root,
                                  int instrument_options)
  {
        List       *partition_check = NIL;
--- 1315,1321 ----
  InitResultRelInfo(ResultRelInfo *resultRelInfo,
                                  Relation resultRelationDesc,
                                  Index resultRelationIndex,
!                                 ResultRelInfo *partition_root,
                                  int instrument_options)
  {
        List       *partition_check = NIL;
***************
*** 1364,1371 **** InitResultRelInfo(ResultRelInfo *resultRelInfo,
         */
        partition_check = RelationGetPartitionQual(resultRelationDesc);
  
-       resultRelInfo->ri_PartitionCheck = partition_check;
        resultRelInfo->ri_PartitionRoot = partition_root;
  }
  
  /*
--- 1371,1379 ----
         */
        partition_check = RelationGetPartitionQual(resultRelationDesc);
  
        resultRelInfo->ri_PartitionRoot = partition_root;
+       resultRelInfo->ri_PartitionCheck = partition_check;
+       resultRelInfo->ri_PartitionIsValid = false;
  }
  
  /*
***************
*** 1889,1903 **** ExecPartitionCheck(ResultRelInfo *resultRelInfo, 
TupleTableSlot *slot,
        {
                char       *val_desc;
                Relation        orig_rel = rel;
  
                /* See the comment above. */
!               if (resultRelInfo->ri_PartitionRoot)
                {
                        HeapTuple       tuple = ExecFetchSlotTuple(slot);
                        TupleDesc       old_tupdesc = RelationGetDescr(rel);
                        TupleConversionMap *map;
  
!                       rel = resultRelInfo->ri_PartitionRoot;
                        tupdesc = RelationGetDescr(rel);
                        /* a reverse map */
                        map = convert_tuples_by_name(old_tupdesc, tupdesc,
--- 1897,1912 ----
        {
                char       *val_desc;
                Relation        orig_rel = rel;
+               ResultRelInfo *rootRelInfo = resultRelInfo->ri_PartitionRoot;
  
                /* See the comment above. */
!               if (rootRelInfo)
                {
                        HeapTuple       tuple = ExecFetchSlotTuple(slot);
                        TupleDesc       old_tupdesc = RelationGetDescr(rel);
                        TupleConversionMap *map;
  
!                       rel = rootRelInfo->ri_RelationDesc;
                        tupdesc = RelationGetDescr(rel);
                        /* a reverse map */
                        map = convert_tuples_by_name(old_tupdesc, tupdesc,
***************
*** 1908,1917 **** ExecPartitionCheck(ResultRelInfo *resultRelInfo, 
TupleTableSlot *slot,
                                ExecSetSlotDescriptor(slot, tupdesc);
                                ExecStoreTuple(tuple, slot, InvalidBuffer, 
false);
                        }
-               }
  
!               insertedCols = GetInsertedColumns(resultRelInfo, estate);
!               updatedCols = GetUpdatedColumns(resultRelInfo, estate);
                modifiedCols = bms_union(insertedCols, updatedCols);
                val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel),
                                                                                
                 slot,
--- 1917,1931 ----
                                ExecSetSlotDescriptor(slot, tupdesc);
                                ExecStoreTuple(tuple, slot, InvalidBuffer, 
false);
                        }
  
!                       insertedCols = GetInsertedColumns(rootRelInfo, estate);
!                       updatedCols = GetUpdatedColumns(rootRelInfo, estate);
!               }
!               else
!               {
!                       insertedCols = GetInsertedColumns(resultRelInfo, 
estate);
!                       updatedCols = GetUpdatedColumns(resultRelInfo, estate);
!               }
                modifiedCols = bms_union(insertedCols, updatedCols);
                val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel),
                                                                                
                 slot,
***************
*** 1963,1968 **** ExecConstraints(ResultRelInfo *resultRelInfo,
--- 1977,1983 ----
                                char       *val_desc;
                                Relation        orig_rel = rel;
                                TupleDesc       orig_tupdesc = 
RelationGetDescr(rel);
+                               ResultRelInfo *rootRelInfo = 
resultRelInfo->ri_PartitionRoot;
  
                                /*
                                 * If the tuple has been routed, it's been 
converted to the
***************
*** 1971,1982 **** ExecConstraints(ResultRelInfo *resultRelInfo,
                                 * rowtype so that val_desc shown error message 
matches the
                                 * input tuple.
                                 */
!                               if (resultRelInfo->ri_PartitionRoot)
                                {
                                        HeapTuple       tuple = 
ExecFetchSlotTuple(slot);
                                        TupleConversionMap *map;
  
!                                       rel = resultRelInfo->ri_PartitionRoot;
                                        tupdesc = RelationGetDescr(rel);
                                        /* a reverse map */
                                        map = 
convert_tuples_by_name(orig_tupdesc, tupdesc,
--- 1986,1997 ----
                                 * rowtype so that val_desc shown error message 
matches the
                                 * input tuple.
                                 */
!                               if (rootRelInfo)
                                {
                                        HeapTuple       tuple = 
ExecFetchSlotTuple(slot);
                                        TupleConversionMap *map;
  
!                                       rel = rootRelInfo->ri_RelationDesc;
                                        tupdesc = RelationGetDescr(rel);
                                        /* a reverse map */
                                        map = 
convert_tuples_by_name(orig_tupdesc, tupdesc,
***************
*** 1987,1996 **** ExecConstraints(ResultRelInfo *resultRelInfo,
                                                ExecSetSlotDescriptor(slot, 
tupdesc);
                                                ExecStoreTuple(tuple, slot, 
InvalidBuffer, false);
                                        }
-                               }
  
!                               insertedCols = 
GetInsertedColumns(resultRelInfo, estate);
!                               updatedCols = GetUpdatedColumns(resultRelInfo, 
estate);
                                modifiedCols = bms_union(insertedCols, 
updatedCols);
                                val_desc = 
ExecBuildSlotValueDescription(RelationGetRelid(rel),
                                                                                
                                 slot,
--- 2002,2016 ----
                                                ExecSetSlotDescriptor(slot, 
tupdesc);
                                                ExecStoreTuple(tuple, slot, 
InvalidBuffer, false);
                                        }
  
!                                       insertedCols = 
GetInsertedColumns(rootRelInfo, estate);
!                                       updatedCols = 
GetUpdatedColumns(rootRelInfo, estate);
!                               }
!                               else
!                               {
!                                       insertedCols = 
GetInsertedColumns(resultRelInfo, estate);
!                                       updatedCols = 
GetUpdatedColumns(resultRelInfo, estate);
!                               }
                                modifiedCols = bms_union(insertedCols, 
updatedCols);
                                val_desc = 
ExecBuildSlotValueDescription(RelationGetRelid(rel),
                                                                                
                                 slot,
***************
*** 2016,2030 **** ExecConstraints(ResultRelInfo *resultRelInfo,
                {
                        char       *val_desc;
                        Relation        orig_rel = rel;
  
                        /* See the comment above. */
!                       if (resultRelInfo->ri_PartitionRoot)
                        {
                                HeapTuple       tuple = 
ExecFetchSlotTuple(slot);
                                TupleDesc       old_tupdesc = 
RelationGetDescr(rel);
                                TupleConversionMap *map;
  
!                               rel = resultRelInfo->ri_PartitionRoot;
                                tupdesc = RelationGetDescr(rel);
                                /* a reverse map */
                                map = convert_tuples_by_name(old_tupdesc, 
tupdesc,
--- 2036,2051 ----
                {
                        char       *val_desc;
                        Relation        orig_rel = rel;
+                       ResultRelInfo *rootRelInfo = 
resultRelInfo->ri_PartitionRoot;
  
                        /* See the comment above. */
!                       if (rootRelInfo)
                        {
                                HeapTuple       tuple = 
ExecFetchSlotTuple(slot);
                                TupleDesc       old_tupdesc = 
RelationGetDescr(rel);
                                TupleConversionMap *map;
  
!                               rel = rootRelInfo->ri_RelationDesc;
                                tupdesc = RelationGetDescr(rel);
                                /* a reverse map */
                                map = convert_tuples_by_name(old_tupdesc, 
tupdesc,
***************
*** 2035,2044 **** ExecConstraints(ResultRelInfo *resultRelInfo,
                                        ExecSetSlotDescriptor(slot, tupdesc);
                                        ExecStoreTuple(tuple, slot, 
InvalidBuffer, false);
                                }
-                       }
  
!                       insertedCols = GetInsertedColumns(resultRelInfo, 
estate);
!                       updatedCols = GetUpdatedColumns(resultRelInfo, estate);
                        modifiedCols = bms_union(insertedCols, updatedCols);
                        val_desc = 
ExecBuildSlotValueDescription(RelationGetRelid(rel),
                                                                                
                         slot,
--- 2056,2070 ----
                                        ExecSetSlotDescriptor(slot, tupdesc);
                                        ExecStoreTuple(tuple, slot, 
InvalidBuffer, false);
                                }
  
!                               insertedCols = GetInsertedColumns(rootRelInfo, 
estate);
!                               updatedCols = GetUpdatedColumns(rootRelInfo, 
estate);
!                       }
!                       else
!                       {
!                               insertedCols = 
GetInsertedColumns(resultRelInfo, estate);
!                               updatedCols = GetUpdatedColumns(resultRelInfo, 
estate);
!                       }
                        modifiedCols = bms_union(insertedCols, updatedCols);
                        val_desc = 
ExecBuildSlotValueDescription(RelationGetRelid(rel),
                                                                                
                         slot,
***************
*** 2110,2115 **** ExecWithCheckOptions(WCOKind kind, ResultRelInfo 
*resultRelInfo,
--- 2136,2142 ----
                 */
                if (!ExecQual(wcoExpr, econtext))
                {
+                       ResultRelInfo *rootRelInfo = 
resultRelInfo->ri_PartitionRoot;
                        char       *val_desc;
                        Bitmapset  *modifiedCols;
                        Bitmapset  *insertedCols;
***************
*** 2128,2140 **** ExecWithCheckOptions(WCOKind kind, ResultRelInfo 
*resultRelInfo,
                                         */
                                case WCO_VIEW_CHECK:
                                        /* See the comment in 
ExecConstraints(). */
!                                       if (resultRelInfo->ri_PartitionRoot)
                                        {
                                                HeapTuple       tuple = 
ExecFetchSlotTuple(slot);
                                                TupleDesc       old_tupdesc = 
RelationGetDescr(rel);
                                                TupleConversionMap *map;
  
!                                               rel = 
resultRelInfo->ri_PartitionRoot;
                                                tupdesc = RelationGetDescr(rel);
                                                /* a reverse map */
                                                map = 
convert_tuples_by_name(old_tupdesc, tupdesc,
--- 2155,2167 ----
                                         */
                                case WCO_VIEW_CHECK:
                                        /* See the comment in 
ExecConstraints(). */
!                                       if (rootRelInfo)
                                        {
                                                HeapTuple       tuple = 
ExecFetchSlotTuple(slot);
                                                TupleDesc       old_tupdesc = 
RelationGetDescr(rel);
                                                TupleConversionMap *map;
  
!                                               rel = 
rootRelInfo->ri_RelationDesc;
                                                tupdesc = RelationGetDescr(rel);
                                                /* a reverse map */
                                                map = 
convert_tuples_by_name(old_tupdesc, tupdesc,
***************
*** 2145,2154 **** ExecWithCheckOptions(WCOKind kind, ResultRelInfo 
*resultRelInfo,
                                                        
ExecSetSlotDescriptor(slot, tupdesc);
                                                        ExecStoreTuple(tuple, 
slot, InvalidBuffer, false);
                                                }
-                                       }
  
!                                       insertedCols = 
GetInsertedColumns(resultRelInfo, estate);
!                                       updatedCols = 
GetUpdatedColumns(resultRelInfo, estate);
                                        modifiedCols = bms_union(insertedCols, 
updatedCols);
                                        val_desc = 
ExecBuildSlotValueDescription(RelationGetRelid(rel),
                                                                                
                                         slot,
--- 2172,2186 ----
                                                        
ExecSetSlotDescriptor(slot, tupdesc);
                                                        ExecStoreTuple(tuple, 
slot, InvalidBuffer, false);
                                                }
  
!                                               insertedCols = 
GetInsertedColumns(rootRelInfo, estate);
!                                               updatedCols = 
GetUpdatedColumns(rootRelInfo, estate);
!                                       }
!                                       else
!                                       {
!                                               insertedCols = 
GetInsertedColumns(resultRelInfo, estate);
!                                               updatedCols = 
GetUpdatedColumns(resultRelInfo, estate);
!                                       }
                                        modifiedCols = bms_union(insertedCols, 
updatedCols);
                                        val_desc = 
ExecBuildSlotValueDescription(RelationGetRelid(rel),
                                                                                
                                         slot,
***************
*** 3242,3247 **** EvalPlanQualEnd(EPQState *epqstate)
--- 3274,3281 ----
   * 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
***************
*** 3262,3288 **** 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 *
--- 3296,3315 ----
   */
  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 *
***************
*** 3295,3350 **** ExecSetupPartitionTupleRouting(Relation rel,
         * processing.
         */
        *partition_tuple_slot = MakeTupleTableSlot();
  
!       leaf_part_rri = (ResultRelInfo *) palloc0(*num_partitions *
!                                                                               
          sizeof(ResultRelInfo));
!       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);
  
!               (*partitions)[i] = leaf_part_rri++;
!               i++;
!       }
  }
  
  /*
--- 3322,3378 ----
         * processing.
         */
        *partition_tuple_slot = MakeTupleTableSlot();
+ }
  
! /*
!  * ExecInitPartition -- Prepare ResultRelInfo and tuple conversion map for
!  * the partition with OID 'partOid'
!  */
! void
! ExecInitPartition(EState *estate,
!                                 ResultRelInfo *rootRelInfo,
!                                 Oid partOid,
!                                 Index partRTindex,
!                                 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 ResultRelInfo data for the partition. */
!       InitResultRelInfo(partRelInfo,
!                                         partrel,
!                                         partRTindex,
!                                         rootRelInfo,
!                                         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);
  
!       /* Store ResultRelInfo in *estate. */
!       estate->es_leaf_result_relations =
!               lappend(estate->es_leaf_result_relations, partRelInfo);
  
!       /*
!        * Save 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"));
  }
  
  /*
*** 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)
!               {
!                       /* The partition 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;
***************
*** 1945,1960 **** 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,
--- 1951,1967 ----
                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;
  
                ExecSetupPartitionTupleRouting(rel,
                                                                           
&partition_dispatch_info,
+                                                                          
&partition_oids,
                                                                           
&partitions,
                                                                           
&partition_tupconv_maps,
                                                                           
&partition_tuple_slot,
***************
*** 1965,1970 **** ExecInitModifyTable(ModifyTable *node, EState *estate, int 
eflags)
--- 1972,2015 ----
                mtstate->mt_num_partitions = num_partitions;
                mtstate->mt_partition_tupconv_maps = partition_tupconv_maps;
                mtstate->mt_partition_tuple_slot = partition_tuple_slot;
+ 
+               partRelInfo = (ResultRelInfo *) palloc0(num_partitions *
+                                                                               
                sizeof(ResultRelInfo));
+               i = 0;
+               foreach(l, partition_oids)
+               {
+                       Oid                     partOid = lfirst_oid(l);
+                       Index           partRTindex = 
list_nth_int(node->partition_rels, i);
+ 
+                       /* Prepare ResultRelInfo and map for the partition */
+                       ExecInitPartition(estate,
+                                                         
mtstate->resultRelInfo,
+                                                         partOid,
+                                                         partRTindex,
+                                                         partRelInfo,
+                                                         
&partition_tupconv_maps[i]);
+                       partitions[i] = partRelInfo;
+ 
+                       /* Verify the partition is a valid target for INSERT */
+                       CheckValidResultRel(partRelInfo, CMD_INSERT);
+ 
+                       /* If so, allow the FDW to 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, i);
+ 
+                               
partRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate,
+                                                                               
                                           partRelInfo,
+                                                                               
                                           fdw_private,
+                                                                               
                                           0,
+                                                                               
                                           eflags);
+                       }
+ 
+                       partRelInfo++;
+                       i++;
+               }
        }
  
        /*
***************
*** 2390,2395 **** ExecEndModifyTable(ModifyTableState *node)
--- 2435,2445 ----
        {
                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
***************
*** 372,377 **** _outModifyTable(StringInfo str, const ModifyTable *node)
--- 372,378 ----
        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);
***************
*** 388,393 **** _outModifyTable(StringInfo str, const ModifyTable *node)
--- 389,395 ----
        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
***************
*** 1568,1573 **** _readModifyTable(void)
--- 1568,1574 ----
        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);
***************
*** 1584,1589 **** _readModifyTable(void)
--- 1585,1591 ----
        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"
***************
*** 6425,6430 **** make_modifytable(PlannerInfo *root,
--- 6426,6432 ----
        ModifyTable *node = makeNode(ModifyTable);
        List       *fdw_private_list;
        Bitmapset  *direct_modify_plans;
+       List       *partition_rels;
        ListCell   *lc;
        int                     i;
  
***************
*** 6548,6553 **** make_modifytable(PlannerInfo *root,
--- 6550,6682 ----
        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 foreign partition, allow the 
FDW
+        * to construct private plan data and accumulate it all into another 
list.
+        *
+        * Note: ExecSetupPartitionTupleRouting will expand partitions in the 
same
+        * order as these lists.
+        */
+       partition_rels = NIL;
+       fdw_private_list = NIL;
+       if (operation == CMD_INSERT &&
+               planner_rt_fetch(nominalRelation, root)->relkind == 
RELKIND_PARTITIONED_TABLE)
+       {
+               List       *saved_withCheckOptionLists = 
node->withCheckOptionLists;
+               List       *saved_returningLists = node->returningLists;
+               Plan       *subplan = (Plan *) linitial(node->plans);
+               List       *saved_tlist = subplan->targetlist;
+               Query      **parent_parses;
+               Query      *parent_parse;
+               Bitmapset  *parent_relids;
+ 
+               /*
+                * Similarly to inheritance_planner(), we generate a modified 
query
+                * with each child as target by applying 
adjust_appendrel_attrs() to
+                * the query for its immediate parent; build an array to save 
in the
+                * query for each parent.
+                */
+               parent_parses = (Query **)
+                       palloc0((list_length(root->parse->rtable) + 1) * 
sizeof(Query *));
+ 
+               parent_parses[nominalRelation] = root->parse;
+               parent_relids = bms_make_singleton(nominalRelation);
+ 
+               foreach(lc, root->append_rel_list)
+               {
+                       AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);
+                       Index           parent_rti = appinfo->parent_relid;
+                       Index           child_rti = appinfo->child_relid;
+                       RangeTblEntry *child_rte;
+                       Query      *child_parse;
+                       FdwRoutine *fdwroutine = NULL;
+                       List       *fdw_private = NIL;
+ 
+                       /* append_rel_list contains all append rels; ignore 
others */
+                       if (!bms_is_member(parent_rti, parent_relids))
+                               continue;
+ 
+                       child_rte = planner_rt_fetch(child_rti, root);
+                       Assert(child_rte->rtekind == RTE_RELATION);
+ 
+                       /* No work if the child is a plain table */
+                       if (child_rte->relkind == RELKIND_RELATION)
+                       {
+                               partition_rels = lappend_int(partition_rels, 
child_rti);
+                               fdw_private_list = lappend(fdw_private_list, 
NIL);
+                               continue;
+                       }
+ 
+                       /*
+                        * expand_inherited_rtentry() always processes a parent 
before any
+                        * of that parent's children, so the query for its 
parent should
+                        * already be available.
+                        */
+                       parent_parse = parent_parses[parent_rti];
+                       Assert(parent_parse);
+ 
+                       /* Generate the query with the child as target */
+                       child_parse = (Query *)
+                               adjust_appendrel_attrs(root,
+                                                                          
(Node *) parent_parse,
+                                                                          1, 
&appinfo);
+ 
+                       /* Ignore if the child is a partitioned table */
+                       if (child_rte->relkind == RELKIND_PARTITIONED_TABLE)
+                       {
+                               parent_parses[child_rti] = child_parse;
+                               parent_relids = bms_add_member(parent_relids, 
child_rti);
+                               continue;
+                       }
+ 
+                       /* The child should be a foreign table */
+                       Assert(child_rte->relkind == RELKIND_FOREIGN_TABLE);
+ 
+                       fdwroutine = GetFdwRoutineByRelId(child_rte->relid);
+                       Assert(fdwroutine != NULL);
+ 
+                       if (fdwroutine->PlanForeignModify != NULL)
+                       {
+                               List       *tlist;
+ 
+                               /* Adjust the plan node to refer to the child 
as target. */
+                               node->nominalRelation = child_rti;
+                               node->resultRelations = 
list_make1_int(child_rti);
+                               node->withCheckOptionLists =
+                                       
list_make1(child_parse->withCheckOptions);
+                               node->returningLists =
+                                       list_make1(child_parse->returningList);
+ 
+                               /*
+                                * The column list of the child might have a 
different column
+                                * order and/or a different set of dropped 
columns than that
+                                * of its parent, so adjust the subplan's tlist.
+                                */
+                               tlist = preprocess_targetlist(root,
+                                                                               
          child_parse->targetList);
+                               subplan->targetlist = tlist;
+ 
+                               fdw_private = 
fdwroutine->PlanForeignModify(root,
+                                                                               
                                        node,
+                                                                               
                                        child_rti,
+                                                                               
                                        0);
+                       }
+ 
+                       partition_rels = lappend_int(partition_rels, child_rti);
+                       fdw_private_list = lappend(fdw_private_list, 
fdw_private);
+               }
+ 
+               root->parse = parent_parses[nominalRelation];
+               node->nominalRelation = nominalRelation;
+               node->resultRelations = list_make1_int(nominalRelation);
+               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
***************
*** 870,879 **** subquery_planner(PlannerGlobal *glob, Query *parse,
                reduce_outer_joins(root);
  
        /*
!        * 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
--- 870,881 ----
                reduce_outer_joins(root);
  
        /*
!        * Do the main planning.  If we have an inherited UPDATE/DELETE target
!        * relation, that needs special processing, 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
***************
*** 854,859 **** set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
--- 854,863 ----
                                {
                                        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/prepunion.c
--- b/src/backend/optimizer/prep/prepunion.c
***************
*** 1972,1979 **** 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);
--- 1972,1980 ----
                        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);
***************
*** 2323,2329 **** adjust_child_relids_multilevel(PlannerInfo *root, Relids 
relids,
  }
  
  /*
!  * 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
--- 2324,2330 ----
  }
  
  /*
!  * 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
***************
*** 2335,2342 **** adjust_child_relids_multilevel(PlannerInfo *root, Relids 
relids,
   * 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)
--- 2336,2341 ----
*** a/src/backend/parser/analyze.c
--- b/src/backend/parser/analyze.c
***************
*** 542,547 **** transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
--- 542,552 ----
        qry->resultRelation = setTargetTable(pstate, stmt->relation,
                                                                                
 false, false, targetPerms);
  
+       /* Set the inh flag to true if the target table is partitioned */
+       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/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
***************
*** 181,187 **** extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, 
CmdType operation)
  extern void InitResultRelInfo(ResultRelInfo *resultRelInfo,
                                  Relation resultRelationDesc,
                                  Index resultRelationIndex,
!                                 Relation partition_root,
                                  int instrument_options);
  extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid);
  extern void ExecCleanUpTriggerState(EState *estate);
--- 181,187 ----
  extern void InitResultRelInfo(ResultRelInfo *resultRelInfo,
                                  Relation resultRelationDesc,
                                  Index resultRelationIndex,
!                                 ResultRelInfo *partition_root,
                                  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,
--- 207,224 ----
                                         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,
+                                 ResultRelInfo *rootRelInfo,
+                                 Oid partOid,
+                                 Index partRTindex,
+                                 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
***************
*** 404,417 **** typedef struct ResultRelInfo
        /* list of ON CONFLICT DO UPDATE exprs (qual) */
        ExprState  *ri_onConflictSetWhere;
  
        /* partition check expression */
        List       *ri_PartitionCheck;
  
        /* partition check expression state */
        ExprState  *ri_PartitionCheckExpr;
  
!       /* relation descriptor for root partitioned table */
!       Relation        ri_PartitionRoot;
  } ResultRelInfo;
  
  /* ----------------
--- 404,420 ----
        /* list of ON CONFLICT DO UPDATE exprs (qual) */
        ExprState  *ri_onConflictSetWhere;
  
+       /* root partitioned table */
+       struct ResultRelInfo *ri_PartitionRoot;
+ 
        /* partition check expression */
        List       *ri_PartitionCheck;
  
        /* partition check expression state */
        ExprState  *ri_PartitionCheckExpr;
  
!       /* 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