foreign-routing-fdwapi-WIP.patch
text/x-diff
Filename: foreign-routing-fdwapi-WIP.patch
Type: text/x-diff
Part: 0
Patch
Format: context
| File | + | − |
|---|---|---|
| contrib/file_fdw/output/file_fdw.source | 3 | 0 |
| contrib/postgres_fdw/expected/postgres_fdw.out | 79 | 0 |
| contrib/postgres_fdw/postgres_fdw.c | 124 | 0 |
| contrib/postgres_fdw/sql/postgres_fdw.sql | 30 | 0 |
| src/backend/commands/copy.c | 3 | 0 |
| src/backend/executor/execMain.c | 16 | 0 |
| src/backend/executor/execPartition.c | 29 | 0 |
| src/backend/executor/nodeModifyTable.c | 20 | 0 |
| src/include/executor/execPartition.h | 2 | 0 |
| src/include/foreign/fdwapi.h | 14 | 0 |
| src/include/nodes/execnodes.h | 6 | 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,351 **** 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');
UPDATE pt set a = 1 where a = 2; -- ERROR
! ERROR: cannot route inserted tuples to a foreign table
SELECT tableoid::regclass, * FROM pt;
tableoid | a | b
----------+---+-------
--- 342,351 ----
(2 rows)
INSERT INTO pt VALUES (1, 'xyzzy'); -- ERROR
! ERROR: cannot route inserted tuples to foreign table "p1"
INSERT INTO pt VALUES (2, 'xyzzy');
UPDATE pt set a = 1 where a = 2; -- ERROR
! ERROR: cannot route inserted tuples to foreign table "p1"
SELECT tableoid::regclass, * FROM pt;
tableoid | a | b
----------+---+-------
*** a/contrib/postgres_fdw/expected/postgres_fdw.out
--- b/contrib/postgres_fdw/expected/postgres_fdw.out
***************
*** 7364,7369 **** NOTICE: drop cascades to foreign table bar2
--- 7364,7448 ----
drop table loct1;
drop table loct2;
-- ===================================================================
+ -- test tuple routing for foreign-table partitions
+ -- ===================================================================
+ create table pt (a int, b int, c text) partition by list (a);
+ create table loct1 (a int check (a in (1)), b int, c text, constraint locp1_pkey primary key (b));
+ create table loct2 (b int, c text, a int check (a in (2)), constraint locp2_pkey primary key (b));
+ create foreign table ptp1 partition of pt for values in (1) server loopback options (table_name 'loct1');
+ create foreign table ptp2 partition of pt for values in (2) server loopback options (table_name 'loct2');
+ insert into pt values (1, 1, 'foo');
+ insert into pt values (1, 2, 'bar') returning *;
+ a | b | c
+ ---+---+-----
+ 1 | 2 | bar
+ (1 row)
+
+ insert into pt values (2, 1, 'baz') returning *;
+ a | b | c
+ ---+---+-----
+ 2 | 1 | baz
+ (1 row)
+
+ select tableoid::regclass, * FROM pt;
+ tableoid | a | b | c
+ ----------+---+---+-----
+ ptp1 | 1 | 1 | foo
+ ptp1 | 1 | 2 | bar
+ ptp2 | 2 | 1 | baz
+ (3 rows)
+
+ select tableoid::regclass, * FROM ptp1;
+ tableoid | a | b | c
+ ----------+---+---+-----
+ ptp1 | 1 | 1 | foo
+ ptp1 | 1 | 2 | bar
+ (2 rows)
+
+ select tableoid::regclass, * FROM ptp2;
+ tableoid | a | b | c
+ ----------+---+---+-----
+ ptp2 | 2 | 1 | baz
+ (1 row)
+
+ insert into pt values (2, 1, 'baz');
+ ERROR: duplicate key value violates unique constraint "locp2_pkey"
+ DETAIL: Key (b)=(1) already exists.
+ CONTEXT: Remote SQL command: INSERT INTO public.loct2(a, b, c) VALUES ($1, $2, $3)
+ insert into pt values (2, 1, 'baz') on conflict do nothing;
+ insert into pt values (2, 2, 'qux') on conflict do nothing returning *;
+ a | b | c
+ ---+---+-----
+ 2 | 2 | qux
+ (1 row)
+
+ select tableoid::regclass, * FROM pt;
+ tableoid | a | b | c
+ ----------+---+---+-----
+ ptp1 | 1 | 1 | foo
+ ptp1 | 1 | 2 | bar
+ ptp2 | 2 | 1 | baz
+ ptp2 | 2 | 2 | qux
+ (4 rows)
+
+ select tableoid::regclass, * FROM ptp1;
+ tableoid | a | b | c
+ ----------+---+---+-----
+ ptp1 | 1 | 1 | foo
+ ptp1 | 1 | 2 | bar
+ (2 rows)
+
+ select tableoid::regclass, * FROM ptp2;
+ tableoid | a | b | c
+ ----------+---+---+-----
+ ptp2 | 2 | 1 | baz
+ ptp2 | 2 | 2 | qux
+ (2 rows)
+
+ drop table pt;
+ drop table loct1;
+ drop table loct2;
+ -- ===================================================================
-- test IMPORT FOREIGN SCHEMA
-- ===================================================================
CREATE SCHEMA import_source;
*** a/contrib/postgres_fdw/postgres_fdw.c
--- b/contrib/postgres_fdw/postgres_fdw.c
***************
*** 319,324 **** static TupleTableSlot *postgresExecForeignDelete(EState *estate,
--- 319,332 ----
TupleTableSlot *planSlot);
static void postgresEndForeignModify(EState *estate,
ResultRelInfo *resultRelInfo);
+ static void postgresBeginForeignRouting(ModifyTableState *mtstate,
+ ResultRelInfo *resultRelInfo,
+ int partition_index);
+ static TupleTableSlot *postgresExecForeignRouting(EState *estate,
+ ResultRelInfo *resultRelInfo,
+ TupleTableSlot *slot);
+ static void postgresEndForeignRouting(EState *estate,
+ ResultRelInfo *resultRelInfo);
static int postgresIsForeignRelUpdatable(Relation rel);
static bool postgresPlanDirectModify(PlannerInfo *root,
ModifyTable *plan,
***************
*** 473,478 **** postgres_fdw_handler(PG_FUNCTION_ARGS)
--- 481,489 ----
routine->ExecForeignUpdate = postgresExecForeignUpdate;
routine->ExecForeignDelete = postgresExecForeignDelete;
routine->EndForeignModify = postgresEndForeignModify;
+ routine->BeginForeignRouting = postgresBeginForeignRouting;
+ routine->ExecForeignRouting = postgresExecForeignRouting;
+ routine->EndForeignRouting = postgresEndForeignRouting;
routine->IsForeignRelUpdatable = postgresIsForeignRelUpdatable;
routine->PlanDirectModify = postgresPlanDirectModify;
routine->BeginDirectModify = postgresBeginDirectModify;
***************
*** 1857,1862 **** postgresEndForeignModify(EState *estate,
--- 1868,1986 ----
}
/*
+ * postgresBeginForeignRouting
+ * Begin a row-routing operation on a foreign table
+ */
+ static void
+ postgresBeginForeignRouting(ModifyTableState *mtstate,
+ ResultRelInfo *resultRelInfo,
+ int partition_index)
+ {
+ PgFdwModifyState *fmstate;
+ Relation rel = resultRelInfo->ri_RelationDesc;
+ TupleDesc tupdesc = RelationGetDescr(rel);
+ int attnum;
+ RangeTblEntry *rte;
+ Query *query;
+ PlannerInfo *root;
+ StringInfoData sql;
+ List *targetAttrs = NIL;
+ List *retrieved_attrs = NIL;
+ bool doNothing = false;
+
+ initStringInfo(&sql);
+
+ /* Set up largely-dummy planner state */
+ rte = makeNode(RangeTblEntry);
+ rte->rtekind = RTE_RELATION;
+ rte->relid = RelationGetRelid(rel);
+ rte->relkind = RELKIND_FOREIGN_TABLE;
+ query = makeNode(Query);
+ query->commandType = CMD_INSERT;
+ query->resultRelation = 1;
+ query->rtable = list_make1(rte);
+ root = makeNode(PlannerInfo);
+ root->parse = query;
+
+ /* We transmit all columns that are defined in the foreign table. */
+ for (attnum = 1; attnum <= tupdesc->natts; attnum++)
+ {
+ Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
+
+ if (!attr->attisdropped)
+ targetAttrs = lappend_int(targetAttrs, attnum);
+ }
+
+ /* We only support DO NOTHING without an inference specification. */
+ if (mtstate->mt_onconflict == ONCONFLICT_NOTHING)
+ doNothing = true;
+ else if (mtstate->mt_onconflict != ONCONFLICT_NONE)
+ elog(ERROR, "unexpected ON CONFLICT specification: %d",
+ (int) mtstate->mt_onconflict);
+
+ /* Construct the SQL command string. */
+ deparseInsertSql(&sql, root, 1, rel, targetAttrs, doNothing,
+ resultRelInfo->ri_returningList, &retrieved_attrs);
+
+ /* Construct an execution state. */
+ fmstate = create_fdw_modify_state(mtstate,
+ resultRelInfo,
+ CMD_INSERT,
+ 0, /* dummy subplan index */
+ sql.data,
+ targetAttrs,
+ retrieved_attrs != NIL,
+ retrieved_attrs);
+
+ resultRelInfo->ri_FdwState = fmstate;
+ }
+
+ /*
+ * postgresExecForeignRouting
+ * Route one row to a foreign table
+ */
+ static TupleTableSlot *
+ postgresExecForeignRouting(EState *estate,
+ ResultRelInfo *resultRelInfo,
+ TupleTableSlot *slot)
+ {
+ PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
+ const char **p_values;
+ int n_rows;
+
+ /* Set up the prepared statement on the remote server, if we didn't yet */
+ if (!fmstate->p_name)
+ prepare_foreign_modify(fmstate);
+
+ /* Convert parameters needed by prepared statement to text form */
+ p_values = convert_prep_stmt_params(fmstate, NULL, slot);
+
+ /* Execute the prepared statement and fetch RETURNING tuple if any */
+ n_rows = execute_prep_stmt(fmstate, p_values, slot);
+
+ MemoryContextReset(fmstate->temp_cxt);
+
+ /* Return NULL if nothing was inserted on the remote end */
+ return (n_rows > 0) ? slot : NULL;
+ }
+
+ /*
+ * postgresEndForeignRouting
+ * Finish a row-routing operation on a foreign table
+ */
+ static void
+ postgresEndForeignRouting(EState *estate,
+ ResultRelInfo *resultRelInfo)
+ {
+ PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
+
+ Assert(fmstate != NULL);
+
+ /* Destroy the execution state. */
+ finish_foreign_modify(fmstate);
+ }
+
+ /*
* postgresIsForeignRelUpdatable
* Determine whether a foreign table supports INSERT, UPDATE and/or
* DELETE.
*** a/contrib/postgres_fdw/sql/postgres_fdw.sql
--- b/contrib/postgres_fdw/sql/postgres_fdw.sql
***************
*** 1759,1764 **** drop table loct1;
--- 1759,1794 ----
drop table loct2;
-- ===================================================================
+ -- test tuple routing for foreign-table partitions
+ -- ===================================================================
+
+ create table pt (a int, b int, c text) partition by list (a);
+ create table loct1 (a int check (a in (1)), b int, c text, constraint locp1_pkey primary key (b));
+ create table loct2 (b int, c text, a int check (a in (2)), constraint locp2_pkey primary key (b));
+ create foreign table ptp1 partition of pt for values in (1) server loopback options (table_name 'loct1');
+ create foreign table ptp2 partition of pt for values in (2) server loopback options (table_name 'loct2');
+
+ insert into pt values (1, 1, 'foo');
+ insert into pt values (1, 2, 'bar') returning *;
+ insert into pt values (2, 1, 'baz') returning *;
+
+ select tableoid::regclass, * FROM pt;
+ select tableoid::regclass, * FROM ptp1;
+ select tableoid::regclass, * FROM ptp2;
+
+ insert into pt values (2, 1, 'baz');
+ insert into pt values (2, 1, 'baz') on conflict do nothing;
+ insert into pt values (2, 2, 'qux') on conflict do nothing returning *;
+
+ select tableoid::regclass, * FROM pt;
+ select tableoid::regclass, * FROM ptp1;
+ select tableoid::regclass, * FROM ptp2;
+
+ drop table pt;
+ drop table loct1;
+ drop table loct2;
+
+ -- ===================================================================
-- test IMPORT FOREIGN SCHEMA
-- ===================================================================
*** a/src/backend/commands/copy.c
--- b/src/backend/commands/copy.c
***************
*** 2616,2626 **** CopyFrom(CopyState cstate)
Assert(resultRelInfo != NULL);
}
! /* 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
--- 2616,2626 ----
Assert(resultRelInfo != NULL);
}
! /* We do not yet have a way to copy into a foreign partition */
if (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
***************
*** 2814,2820 **** CopyFrom(CopyState cstate)
/* Close all the partitioned tables, leaf partitions, and their indices */
if (cstate->partition_tuple_routing)
! ExecCleanupTupleRouting(cstate->partition_tuple_routing);
/* Close any trigger target relations */
ExecCleanUpTriggerState(estate);
--- 2814,2820 ----
/* Close all the partitioned tables, leaf partitions, and their indices */
if (cstate->partition_tuple_routing)
! ExecCleanupTupleRouting(NULL, cstate->partition_tuple_routing);
/* Close any trigger target relations */
ExecCleanUpTriggerState(estate);
*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
***************
*** 1172,1189 **** 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,
--- 1172,1193 ----
switch (operation)
{
case CMD_INSERT:
if (resultRelInfo->ri_PartitionRoot)
! {
! if (fdwroutine->ExecForeignRouting == NULL)
! ereport(ERROR,
! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("cannot route inserted tuples to foreign table \"%s\"",
! RelationGetRelationName(resultRel))));
! }
! else
! {
! 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,
***************
*** 1364,1369 **** InitResultRelInfo(ResultRelInfo *resultRelInfo,
--- 1368,1374 ----
resultRelInfo->ri_PartitionCheck = partition_check;
resultRelInfo->ri_PartitionRoot = partition_root;
+ resultRelInfo->ri_PartitionIsValid = false;
}
/*
*** a/src/backend/executor/execPartition.c
--- b/src/backend/executor/execPartition.c
***************
*** 17,22 ****
--- 17,23 ----
#include "catalog/pg_inherits_fn.h"
#include "executor/execPartition.h"
#include "executor/executor.h"
+ #include "foreign/fdwapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "utils/lsyscache.h"
***************
*** 162,173 **** ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel)
gettext_noop("could not convert row type"));
/*
! * Verify result relation is a valid target for an INSERT. An
! * UPDATE of a partition-key becomes a DELETE+INSERT operation,
! * so this check is required even when the operation is
! * CMD_UPDATE.
*/
! CheckValidResultRel(leaf_part_rri, CMD_INSERT);
}
}
--- 163,175 ----
gettext_noop("could not convert row type"));
/*
! * Also let the FDW init itself if this parition is foreign.
*/
! if (leaf_part_rri->ri_FdwRoutine != NULL &&
! leaf_part_rri->ri_FdwRoutine->BeginForeignRouting != NULL)
! leaf_part_rri->ri_FdwRoutine->BeginForeignRouting(mtstate,
! leaf_part_rri,
! i);
}
}
***************
*** 342,354 **** ExecInitPartitionInfo(ModifyTableState *mtstate,
estate->es_instrument);
/*
- * Verify result relation is a valid target for an INSERT. An UPDATE
- * of a partition-key becomes a DELETE+INSERT operation, so this check
- * is still required when the operation is CMD_UPDATE.
- */
- CheckValidResultRel(leaf_part_rri, CMD_INSERT);
-
- /*
* Since we've just initialized this ResultRelInfo, it's not in
* any list attached to the estate as yet. Add it, so that it can
* be found later.
--- 344,349 ----
***************
*** 468,473 **** ExecInitPartitionInfo(ModifyTableState *mtstate,
--- 463,469 ----
returningList = map_partition_varattnos(returningList, firstVarno,
partrel, firstResultRel,
NULL);
+ leaf_part_rri->ri_returningList = returningList;
/*
* Initialize the projection itself.
***************
*** 498,503 **** ExecInitPartitionInfo(ModifyTableState *mtstate,
--- 494,509 ----
MemoryContextSwitchTo(oldContext);
+ /*
+ * Also let the FDW init itself if this parition is foreign.
+ */
+ if (mtstate &&
+ leaf_part_rri->ri_FdwRoutine != NULL &&
+ leaf_part_rri->ri_FdwRoutine->BeginForeignRouting != NULL)
+ leaf_part_rri->ri_FdwRoutine->BeginForeignRouting(mtstate,
+ leaf_part_rri,
+ partidx);
+
return leaf_part_rri;
}
***************
*** 603,609 **** ConvertPartitionTupleSlot(TupleConversionMap *map,
* Close all the partitioned tables, leaf partitions, and their indices.
*/
void
! ExecCleanupTupleRouting(PartitionTupleRouting *proute)
{
int i;
int subplan_index = 0;
--- 609,616 ----
* Close all the partitioned tables, leaf partitions, and their indices.
*/
void
! ExecCleanupTupleRouting(ModifyTableState *node,
! PartitionTupleRouting *proute)
{
int i;
int subplan_index = 0;
***************
*** 632,637 **** ExecCleanupTupleRouting(PartitionTupleRouting *proute)
--- 639,653 ----
continue;
/*
+ * Allow any FDWs to shut down
+ */
+ if (node &&
+ resultRelInfo->ri_FdwRoutine != NULL &&
+ resultRelInfo->ri_FdwRoutine->EndForeignRouting != NULL)
+ resultRelInfo->ri_FdwRoutine->EndForeignRouting(node->ps.state,
+ resultRelInfo);
+
+ /*
* If this result rel is one of the UPDATE subplan result rels, let
* ExecEndPlan() close it. For INSERT or COPY,
* proute->subplan_partition_offsets will always be NULL. Note that
*** a/src/backend/executor/nodeModifyTable.c
--- b/src/backend/executor/nodeModifyTable.c
***************
*** 319,329 **** ExecInsert(ModifyTableState *mtstate,
Assert(resultRelInfo != NULL);
}
! /* 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;
--- 319,333 ----
Assert(resultRelInfo != NULL);
}
! /*
! * Verify the specified partition is a valid target for tuple routing
! * if not already done.
! */
! if (!resultRelInfo->ri_PartitionIsValid)
! {
! CheckValidResultRel(resultRelInfo, CMD_INSERT);
! resultRelInfo->ri_PartitionIsValid = true;
! }
/* For ExecInsertIndexTuples() to work on the partition's indexes */
estate->es_result_relation_info = resultRelInfo;
***************
*** 433,442 **** ExecInsert(ModifyTableState *mtstate,
/*
* insert into foreign table: let the FDW do it
*/
! slot = resultRelInfo->ri_FdwRoutine->ExecForeignInsert(estate,
! resultRelInfo,
! slot,
! planSlot);
if (slot == NULL) /* "do nothing" */
return NULL;
--- 437,451 ----
/*
* insert into foreign table: let the FDW do it
*/
! if (resultRelInfo->ri_PartitionRoot)
! slot = resultRelInfo->ri_FdwRoutine->ExecForeignRouting(estate,
! resultRelInfo,
! slot);
! else
! slot = resultRelInfo->ri_FdwRoutine->ExecForeignInsert(estate,
! resultRelInfo,
! slot,
! planSlot);
if (slot == NULL) /* "do nothing" */
return NULL;
***************
*** 2311,2316 **** ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
--- 2320,2326 ----
{
List *rlist = (List *) lfirst(l);
+ resultRelInfo->ri_returningList = rlist;
resultRelInfo->ri_projectReturning =
ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
resultRelInfo->ri_RelationDesc->rd_att);
***************
*** 2569,2575 **** ExecEndModifyTable(ModifyTableState *node)
/* Close all the partitioned tables, leaf partitions, and their indices */
if (node->mt_partition_tuple_routing)
! ExecCleanupTupleRouting(node->mt_partition_tuple_routing);
/*
* Free the exprcontext
--- 2579,2585 ----
/* Close all the partitioned tables, leaf partitions, and their indices */
if (node->mt_partition_tuple_routing)
! ExecCleanupTupleRouting(node, node->mt_partition_tuple_routing);
/*
* Free the exprcontext
*** a/src/include/executor/execPartition.h
--- b/src/include/executor/execPartition.h
***************
*** 121,126 **** extern HeapTuple ConvertPartitionTupleSlot(TupleConversionMap *map,
HeapTuple tuple,
TupleTableSlot *new_slot,
TupleTableSlot **p_my_slot);
! extern void ExecCleanupTupleRouting(PartitionTupleRouting *proute);
#endif /* EXECPARTITION_H */
--- 121,127 ----
HeapTuple tuple,
TupleTableSlot *new_slot,
TupleTableSlot **p_my_slot);
! extern void ExecCleanupTupleRouting(ModifyTableState *node,
! PartitionTupleRouting *proute);
#endif /* EXECPARTITION_H */
*** a/src/include/foreign/fdwapi.h
--- b/src/include/foreign/fdwapi.h
***************
*** 97,102 **** typedef TupleTableSlot *(*ExecForeignDelete_function) (EState *estate,
--- 97,113 ----
typedef void (*EndForeignModify_function) (EState *estate,
ResultRelInfo *rinfo);
+ typedef void (*BeginForeignRouting_function) (ModifyTableState *mtstate,
+ ResultRelInfo *rinfo,
+ int partition_index);
+
+ typedef TupleTableSlot *(*ExecForeignRouting_function) (EState *estate,
+ ResultRelInfo *rinfo,
+ TupleTableSlot *slot);
+
+ typedef void (*EndForeignRouting_function) (EState *estate,
+ ResultRelInfo *rinfo);
+
typedef int (*IsForeignRelUpdatable_function) (Relation rel);
typedef bool (*PlanDirectModify_function) (PlannerInfo *root,
***************
*** 204,209 **** typedef struct FdwRoutine
--- 215,223 ----
ExecForeignUpdate_function ExecForeignUpdate;
ExecForeignDelete_function ExecForeignDelete;
EndForeignModify_function EndForeignModify;
+ BeginForeignRouting_function BeginForeignRouting;
+ ExecForeignRouting_function ExecForeignRouting;
+ EndForeignRouting_function EndForeignRouting;
IsForeignRelUpdatable_function IsForeignRelUpdatable;
PlanDirectModify_function PlanDirectModify;
BeginDirectModify_function BeginDirectModify;
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
***************
*** 403,408 **** typedef struct ResultRelInfo
--- 403,411 ----
/* list of WithCheckOption expr states */
List *ri_WithCheckOptionExprs;
+ /* list of RETURNING expressions */
+ List *ri_returningList;
+
/* array of constraint-checking expr states */
ExprState **ri_ConstraintExprs;
***************
*** 426,431 **** typedef struct ResultRelInfo
--- 429,437 ----
/* relation descriptor for root partitioned table */
Relation ri_PartitionRoot;
+
+ /* true if valid target for tuple routing */
+ bool ri_PartitionIsValid;
} ResultRelInfo;
/* ----------------