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