tuple-routing-to-foreign-partitions-v2.patch
text/plain
Filename: tuple-routing-to-foreign-partitions-v2.patch
Type: text/plain
Part: 0
Patch
Format: context
Series: patch v2
| File | + | − |
|---|---|---|
| contrib/file_fdw/output/file_fdw.source | 2 | 0 |
| contrib/postgres_fdw/expected/postgres_fdw.out | 138 | 0 |
| contrib/postgres_fdw/sql/postgres_fdw.sql | 40 | 0 |
| src/backend/commands/copy.c | 46 | 0 |
| src/backend/commands/explain.c | 113 | 0 |
| src/backend/commands/tablecmds.c | 1 | 0 |
| src/backend/executor/execMain.c | 82 | 0 |
| src/backend/executor/nodeModifyTable.c | 75 | 0 |
| src/backend/nodes/copyfuncs.c | 2 | 0 |
| src/backend/nodes/outfuncs.c | 2 | 0 |
| src/backend/nodes/readfuncs.c | 2 | 0 |
| src/backend/optimizer/plan/createplan.c | 97 | 0 |
| src/backend/optimizer/plan/planner.c | 4 | 0 |
| src/backend/optimizer/plan/setrefs.c | 4 | 0 |
| src/backend/optimizer/prep/preptlist.c | 3 | 3 |
| src/backend/optimizer/prep/prepunion.c | 4 | 0 |
| src/backend/parser/analyze.c | 7 | 0 |
| src/backend/replication/logical/worker.c | 1 | 0 |
| src/backend/rewrite/rewriteHandler.c | 0 | 0 |
| src/include/executor/executor.h | 8 | 0 |
| src/include/nodes/execnodes.h | 6 | 0 |
| src/include/nodes/plannodes.h | 4 | 0 |
*** 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;
/* ----------------