v2-0001-Re-index-ModifyTable-FDW-arrays-when-pruning-resu-efujita.patch
application/octet-stream
Filename: v2-0001-Re-index-ModifyTable-FDW-arrays-when-pruning-resu-efujita.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v2-0001
| File | + | − |
|---|---|---|
| contrib/postgres_fdw/expected/postgres_fdw.out | 64 | 0 |
| contrib/postgres_fdw/sql/postgres_fdw.sql | 34 | 0 |
| src/backend/commands/explain.c | 1 | 1 |
| src/backend/executor/nodeModifyTable.c | 20 | 2 |
| src/include/nodes/execnodes.h | 6 | 0 |
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index e90289e4ab1..13853b8b720 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -7197,6 +7197,70 @@ RESET enable_material;
DROP FOREIGN TABLE remt2;
DROP TABLE loct1;
DROP TABLE loct2;
+-- Test that direct modify and foreign modify work with runtime pruning of
+-- result relations (bug #19484)
+create table fdw_part_update (a int not null, b int) partition by list (a);
+create table fdw_part_update_p1 partition of fdw_part_update for values in (1);
+create table fdw_part_update_remote (a int not null, b int);
+create foreign table fdw_part_update_p2 partition of fdw_part_update
+ for values in (2)
+ server loopback options (table_name 'fdw_part_update_remote');
+insert into fdw_part_update_p1 values (1, 10);
+insert into fdw_part_update_remote values (2, 20);
+set plan_cache_mode = force_generic_plan;
+-- Check DirectModify case
+prepare fdw_part_upd(int) as
+ update fdw_part_update set b = b + 1 where a = $1
+ returning tableoid::regclass, a, b;
+explain (verbose, costs off)
+ execute fdw_part_upd(2);
+ QUERY PLAN
+-------------------------------------------------------------------------------------------------------------------------
+ Update on public.fdw_part_update
+ Output: (fdw_part_update_1.tableoid)::regclass, fdw_part_update_1.a, fdw_part_update_1.b
+ Foreign Update on public.fdw_part_update_p2 fdw_part_update_2
+ -> Append
+ Subplans Removed: 1
+ -> Foreign Update on public.fdw_part_update_p2 fdw_part_update_2
+ Remote SQL: UPDATE public.fdw_part_update_remote SET b = (b + 1) WHERE ((a = $1::integer)) RETURNING a, b
+(7 rows)
+
+execute fdw_part_upd(2);
+ tableoid | a | b
+--------------------+---+----
+ fdw_part_update_p2 | 2 | 21
+(1 row)
+
+deallocate fdw_part_upd;
+-- Check ForeignModify case
+prepare fdw_part_upd2(int) as
+ update fdw_part_update set b = b + random()::int * 0 + 1 where a = $1
+ returning tableoid::regclass, a, b;
+explain (verbose, costs off)
+ execute fdw_part_upd2(2);
+ QUERY PLAN
+--------------------------------------------------------------------------------------------------------------------------------------------------------
+ Update on public.fdw_part_update
+ Output: (fdw_part_update_1.tableoid)::regclass, fdw_part_update_1.a, fdw_part_update_1.b
+ Foreign Update on public.fdw_part_update_p2 fdw_part_update_2
+ Remote SQL: UPDATE public.fdw_part_update_remote SET b = $2 WHERE ctid = $1 RETURNING a, b
+ -> Append
+ Subplans Removed: 1
+ -> Foreign Scan on public.fdw_part_update_p2 fdw_part_update_2
+ Output: ((fdw_part_update_2.b + ((random())::integer * 0)) + 1), fdw_part_update_2.tableoid, fdw_part_update_2.ctid, fdw_part_update_2.*
+ Remote SQL: SELECT a, b, ctid FROM public.fdw_part_update_remote WHERE ((a = $1::integer)) FOR UPDATE
+(9 rows)
+
+execute fdw_part_upd2(2);
+ tableoid | a | b
+--------------------+---+----
+ fdw_part_update_p2 | 2 | 22
+(1 row)
+
+deallocate fdw_part_upd2;
+reset plan_cache_mode;
+drop table fdw_part_update;
+drop table fdw_part_update_remote;
-- ===================================================================
-- test check constraints
-- ===================================================================
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index dfc58beb0d2..697c4a92e2d 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -1778,6 +1778,40 @@ DROP FOREIGN TABLE remt2;
DROP TABLE loct1;
DROP TABLE loct2;
+-- Test that direct modify and foreign modify work with runtime pruning of
+-- result relations (bug #19484)
+create table fdw_part_update (a int not null, b int) partition by list (a);
+create table fdw_part_update_p1 partition of fdw_part_update for values in (1);
+create table fdw_part_update_remote (a int not null, b int);
+create foreign table fdw_part_update_p2 partition of fdw_part_update
+ for values in (2)
+ server loopback options (table_name 'fdw_part_update_remote');
+insert into fdw_part_update_p1 values (1, 10);
+insert into fdw_part_update_remote values (2, 20);
+set plan_cache_mode = force_generic_plan;
+
+-- Check DirectModify case
+prepare fdw_part_upd(int) as
+ update fdw_part_update set b = b + 1 where a = $1
+ returning tableoid::regclass, a, b;
+explain (verbose, costs off)
+ execute fdw_part_upd(2);
+execute fdw_part_upd(2);
+deallocate fdw_part_upd;
+
+-- Check ForeignModify case
+prepare fdw_part_upd2(int) as
+ update fdw_part_update set b = b + random()::int * 0 + 1 where a = $1
+ returning tableoid::regclass, a, b;
+explain (verbose, costs off)
+ execute fdw_part_upd2(2);
+execute fdw_part_upd2(2);
+deallocate fdw_part_upd2;
+
+reset plan_cache_mode;
+drop table fdw_part_update;
+drop table fdw_part_update_remote;
+
-- ===================================================================
-- test check constraints
-- ===================================================================
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 112c17b0d64..a40d03d35f3 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -4821,7 +4821,7 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
fdwroutine != NULL &&
fdwroutine->ExplainForeignModify != NULL)
{
- List *fdw_private = (List *) list_nth(node->fdwPrivLists, j);
+ List *fdw_private = (List *) list_nth(mtstate->mt_fdwPrivLists, j);
fdwroutine->ExplainForeignModify(mtstate,
resultRelInfo,
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 33a6735f08d..846dc516b43 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -5108,6 +5108,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
List *updateColnosLists = NIL;
List *mergeActionLists = NIL;
List *mergeJoinConditions = NIL;
+ List *fdwPrivLists = NIL;
+ Bitmapset *fdwDirectModifyPlans = NULL;
ResultRelInfo *resultRelInfo;
List *arowmarks;
ListCell *l;
@@ -5150,6 +5152,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
if (keep_rel)
{
+ List *fdwPrivList = (List *) list_nth(node->fdwPrivLists, i);
+
resultRelations = lappend_int(resultRelations, rti);
if (node->withCheckOptionLists)
{
@@ -5185,6 +5189,19 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
mergeJoinConditions = lappend(mergeJoinConditions, mergeJoinCondition);
}
+
+ /*
+ * fdwPrivLists/fdwDirectModifyPlans are re-indexed to match
+ * resultRelations
+ */
+ fdwPrivLists = lappend(fdwPrivLists, fdwPrivList);
+ if (bms_is_member(i, node->fdwDirectModifyPlans))
+ {
+ int new_index = list_length(resultRelations) - 1;
+
+ fdwDirectModifyPlans = bms_add_member(fdwDirectModifyPlans,
+ new_index);
+ }
}
i++;
}
@@ -5213,6 +5230,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
mtstate->mt_updateColnosLists = updateColnosLists;
mtstate->mt_mergeActionLists = mergeActionLists;
mtstate->mt_mergeJoinConditions = mergeJoinConditions;
+ mtstate->mt_fdwPrivLists = fdwPrivLists;
/*----------
* Resolve the target relation. This is the same as:
@@ -5288,7 +5306,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
/* Initialize the usesFdwDirectModify flag */
resultRelInfo->ri_usesFdwDirectModify =
- bms_is_member(i, node->fdwDirectModifyPlans);
+ bms_is_member(i, fdwDirectModifyPlans);
/*
* Verify result relation is a valid target for the current operation
@@ -5317,7 +5335,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
resultRelInfo->ri_FdwRoutine != NULL &&
resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
{
- List *fdw_private = (List *) list_nth(node->fdwPrivLists, i);
+ List *fdw_private = (List *) list_nth(fdwPrivLists, i);
resultRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate,
resultRelInfo,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 53c138310db..5871383961f 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1446,6 +1446,12 @@ typedef struct ModifyTableState
int mt_nrels; /* number of entries in resultRelInfo[] */
ResultRelInfo *resultRelInfo; /* info about target relation(s) */
+ /*
+ * Re-indexed fdw private data lists, aligned with resultRelInfo[] after
+ * pruning
+ */
+ List *mt_fdwPrivLists;
+
/*
* Target relation mentioned in the original statement, used to fire
* statement-level triggers and as the root for tuple routing. (This