Pass the correct PlannerInfo to PlanForeignModify/PlanDirectModify.
Previously, we passed the toplevel PlannerInfo, but we actually want to pass the relevant subroot. One problem with passing the toplevel PlannerInfo is that the FDW which wants to push down an UPDATE or DELETE against a join won't find the relevant joinrel there. As of commit 1bc0100d270e5bcc980a0629b8726a32a497e788, postgres_fdw tries to do exactly this and can be made to fail an assertion as a result. It's possible that this should be regarded as a bug fix and back-patched to earlier releases, but for lack of a test case that fails in earlier releases, no back-patch for now. Etsuro Fujita, reviewed by Amit Langote. Discussion: http://postgr.es/m/5AF43E02.30000@lab.ntt.co.jp
This commit is contained in:
parent
09b12d52db
commit
7fc7dac1a7
@ -7370,6 +7370,81 @@ drop table bar cascade;
|
|||||||
NOTICE: drop cascades to foreign table bar2
|
NOTICE: drop cascades to foreign table bar2
|
||||||
drop table loct1;
|
drop table loct1;
|
||||||
drop table loct2;
|
drop table loct2;
|
||||||
|
-- Test pushing down UPDATE/DELETE joins to the remote server
|
||||||
|
create table parent (a int, b text);
|
||||||
|
create table loct1 (a int, b text);
|
||||||
|
create table loct2 (a int, b text);
|
||||||
|
create foreign table remt1 (a int, b text)
|
||||||
|
server loopback options (table_name 'loct1');
|
||||||
|
create foreign table remt2 (a int, b text)
|
||||||
|
server loopback options (table_name 'loct2');
|
||||||
|
alter foreign table remt1 inherit parent;
|
||||||
|
insert into remt1 values (1, 'foo');
|
||||||
|
insert into remt1 values (2, 'bar');
|
||||||
|
insert into remt2 values (1, 'foo');
|
||||||
|
insert into remt2 values (2, 'bar');
|
||||||
|
analyze remt1;
|
||||||
|
analyze remt2;
|
||||||
|
explain (verbose, costs off)
|
||||||
|
update parent set b = parent.b || remt2.b from remt2 where parent.a = remt2.a returning *;
|
||||||
|
QUERY PLAN
|
||||||
|
-----------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
Update on public.parent
|
||||||
|
Output: parent.a, parent.b, remt2.a, remt2.b
|
||||||
|
Update on public.parent
|
||||||
|
Foreign Update on public.remt1
|
||||||
|
-> Nested Loop
|
||||||
|
Output: parent.a, (parent.b || remt2.b), parent.ctid, remt2.*, remt2.a, remt2.b
|
||||||
|
Join Filter: (parent.a = remt2.a)
|
||||||
|
-> Seq Scan on public.parent
|
||||||
|
Output: parent.a, parent.b, parent.ctid
|
||||||
|
-> Foreign Scan on public.remt2
|
||||||
|
Output: remt2.b, remt2.*, remt2.a
|
||||||
|
Remote SQL: SELECT a, b FROM public.loct2
|
||||||
|
-> Foreign Update
|
||||||
|
Remote SQL: UPDATE public.loct1 r4 SET b = (r4.b || r2.b) FROM public.loct2 r2 WHERE ((r4.a = r2.a)) RETURNING r4.a, r4.b, r2.a, r2.b
|
||||||
|
(14 rows)
|
||||||
|
|
||||||
|
update parent set b = parent.b || remt2.b from remt2 where parent.a = remt2.a returning *;
|
||||||
|
a | b | a | b
|
||||||
|
---+--------+---+-----
|
||||||
|
1 | foofoo | 1 | foo
|
||||||
|
2 | barbar | 2 | bar
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
explain (verbose, costs off)
|
||||||
|
delete from parent using remt2 where parent.a = remt2.a returning parent;
|
||||||
|
QUERY PLAN
|
||||||
|
------------------------------------------------------------------------------------------------------------------
|
||||||
|
Delete on public.parent
|
||||||
|
Output: parent.*
|
||||||
|
Delete on public.parent
|
||||||
|
Foreign Delete on public.remt1
|
||||||
|
-> Nested Loop
|
||||||
|
Output: parent.ctid, remt2.*
|
||||||
|
Join Filter: (parent.a = remt2.a)
|
||||||
|
-> Seq Scan on public.parent
|
||||||
|
Output: parent.ctid, parent.a
|
||||||
|
-> Foreign Scan on public.remt2
|
||||||
|
Output: remt2.*, remt2.a
|
||||||
|
Remote SQL: SELECT a, b FROM public.loct2
|
||||||
|
-> Foreign Delete
|
||||||
|
Remote SQL: DELETE FROM public.loct1 r4 USING public.loct2 r2 WHERE ((r4.a = r2.a)) RETURNING r4.a, r4.b
|
||||||
|
(14 rows)
|
||||||
|
|
||||||
|
delete from parent using remt2 where parent.a = remt2.a returning parent;
|
||||||
|
parent
|
||||||
|
------------
|
||||||
|
(1,foofoo)
|
||||||
|
(2,barbar)
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- cleanup
|
||||||
|
drop foreign table remt1;
|
||||||
|
drop foreign table remt2;
|
||||||
|
drop table loct1;
|
||||||
|
drop table loct2;
|
||||||
|
drop table parent;
|
||||||
-- ===================================================================
|
-- ===================================================================
|
||||||
-- test tuple routing for foreign-table partitions
|
-- test tuple routing for foreign-table partitions
|
||||||
-- ===================================================================
|
-- ===================================================================
|
||||||
|
@ -1767,6 +1767,38 @@ drop table bar cascade;
|
|||||||
drop table loct1;
|
drop table loct1;
|
||||||
drop table loct2;
|
drop table loct2;
|
||||||
|
|
||||||
|
-- Test pushing down UPDATE/DELETE joins to the remote server
|
||||||
|
create table parent (a int, b text);
|
||||||
|
create table loct1 (a int, b text);
|
||||||
|
create table loct2 (a int, b text);
|
||||||
|
create foreign table remt1 (a int, b text)
|
||||||
|
server loopback options (table_name 'loct1');
|
||||||
|
create foreign table remt2 (a int, b text)
|
||||||
|
server loopback options (table_name 'loct2');
|
||||||
|
alter foreign table remt1 inherit parent;
|
||||||
|
|
||||||
|
insert into remt1 values (1, 'foo');
|
||||||
|
insert into remt1 values (2, 'bar');
|
||||||
|
insert into remt2 values (1, 'foo');
|
||||||
|
insert into remt2 values (2, 'bar');
|
||||||
|
|
||||||
|
analyze remt1;
|
||||||
|
analyze remt2;
|
||||||
|
|
||||||
|
explain (verbose, costs off)
|
||||||
|
update parent set b = parent.b || remt2.b from remt2 where parent.a = remt2.a returning *;
|
||||||
|
update parent set b = parent.b || remt2.b from remt2 where parent.a = remt2.a returning *;
|
||||||
|
explain (verbose, costs off)
|
||||||
|
delete from parent using remt2 where parent.a = remt2.a returning parent;
|
||||||
|
delete from parent using remt2 where parent.a = remt2.a returning parent;
|
||||||
|
|
||||||
|
-- cleanup
|
||||||
|
drop foreign table remt1;
|
||||||
|
drop foreign table remt2;
|
||||||
|
drop table loct1;
|
||||||
|
drop table loct2;
|
||||||
|
drop table parent;
|
||||||
|
|
||||||
-- ===================================================================
|
-- ===================================================================
|
||||||
-- test tuple routing for foreign-table partitions
|
-- test tuple routing for foreign-table partitions
|
||||||
-- ===================================================================
|
-- ===================================================================
|
||||||
|
@ -289,7 +289,7 @@ static ModifyTable *make_modifytable(PlannerInfo *root,
|
|||||||
CmdType operation, bool canSetTag,
|
CmdType operation, bool canSetTag,
|
||||||
Index nominalRelation, List *partitioned_rels,
|
Index nominalRelation, List *partitioned_rels,
|
||||||
bool partColsUpdated,
|
bool partColsUpdated,
|
||||||
List *resultRelations, List *subplans,
|
List *resultRelations, List *subplans, List *subroots,
|
||||||
List *withCheckOptionLists, List *returningLists,
|
List *withCheckOptionLists, List *returningLists,
|
||||||
List *rowMarks, OnConflictExpr *onconflict, int epqParam);
|
List *rowMarks, OnConflictExpr *onconflict, int epqParam);
|
||||||
static GatherMerge *create_gather_merge_plan(PlannerInfo *root,
|
static GatherMerge *create_gather_merge_plan(PlannerInfo *root,
|
||||||
@ -2484,6 +2484,7 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path)
|
|||||||
best_path->partColsUpdated,
|
best_path->partColsUpdated,
|
||||||
best_path->resultRelations,
|
best_path->resultRelations,
|
||||||
subplans,
|
subplans,
|
||||||
|
best_path->subroots,
|
||||||
best_path->withCheckOptionLists,
|
best_path->withCheckOptionLists,
|
||||||
best_path->returningLists,
|
best_path->returningLists,
|
||||||
best_path->rowMarks,
|
best_path->rowMarks,
|
||||||
@ -6558,7 +6559,7 @@ make_modifytable(PlannerInfo *root,
|
|||||||
CmdType operation, bool canSetTag,
|
CmdType operation, bool canSetTag,
|
||||||
Index nominalRelation, List *partitioned_rels,
|
Index nominalRelation, List *partitioned_rels,
|
||||||
bool partColsUpdated,
|
bool partColsUpdated,
|
||||||
List *resultRelations, List *subplans,
|
List *resultRelations, List *subplans, List *subroots,
|
||||||
List *withCheckOptionLists, List *returningLists,
|
List *withCheckOptionLists, List *returningLists,
|
||||||
List *rowMarks, OnConflictExpr *onconflict, int epqParam)
|
List *rowMarks, OnConflictExpr *onconflict, int epqParam)
|
||||||
{
|
{
|
||||||
@ -6566,9 +6567,11 @@ make_modifytable(PlannerInfo *root,
|
|||||||
List *fdw_private_list;
|
List *fdw_private_list;
|
||||||
Bitmapset *direct_modify_plans;
|
Bitmapset *direct_modify_plans;
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
|
ListCell *lc2;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
Assert(list_length(resultRelations) == list_length(subplans));
|
Assert(list_length(resultRelations) == list_length(subplans));
|
||||||
|
Assert(list_length(resultRelations) == list_length(subroots));
|
||||||
Assert(withCheckOptionLists == NIL ||
|
Assert(withCheckOptionLists == NIL ||
|
||||||
list_length(resultRelations) == list_length(withCheckOptionLists));
|
list_length(resultRelations) == list_length(withCheckOptionLists));
|
||||||
Assert(returningLists == NIL ||
|
Assert(returningLists == NIL ||
|
||||||
@ -6627,9 +6630,10 @@ make_modifytable(PlannerInfo *root,
|
|||||||
fdw_private_list = NIL;
|
fdw_private_list = NIL;
|
||||||
direct_modify_plans = NULL;
|
direct_modify_plans = NULL;
|
||||||
i = 0;
|
i = 0;
|
||||||
foreach(lc, resultRelations)
|
forboth(lc, resultRelations, lc2, subroots)
|
||||||
{
|
{
|
||||||
Index rti = lfirst_int(lc);
|
Index rti = lfirst_int(lc);
|
||||||
|
PlannerInfo *subroot = lfirst_node(PlannerInfo, lc2);
|
||||||
FdwRoutine *fdwroutine;
|
FdwRoutine *fdwroutine;
|
||||||
List *fdw_private;
|
List *fdw_private;
|
||||||
bool direct_modify;
|
bool direct_modify;
|
||||||
@ -6641,16 +6645,16 @@ make_modifytable(PlannerInfo *root,
|
|||||||
* so it's not a baserel; and there are also corner cases for
|
* so it's not a baserel; and there are also corner cases for
|
||||||
* updatable views where the target rel isn't a baserel.)
|
* updatable views where the target rel isn't a baserel.)
|
||||||
*/
|
*/
|
||||||
if (rti < root->simple_rel_array_size &&
|
if (rti < subroot->simple_rel_array_size &&
|
||||||
root->simple_rel_array[rti] != NULL)
|
subroot->simple_rel_array[rti] != NULL)
|
||||||
{
|
{
|
||||||
RelOptInfo *resultRel = root->simple_rel_array[rti];
|
RelOptInfo *resultRel = subroot->simple_rel_array[rti];
|
||||||
|
|
||||||
fdwroutine = resultRel->fdwroutine;
|
fdwroutine = resultRel->fdwroutine;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
RangeTblEntry *rte = planner_rt_fetch(rti, root);
|
RangeTblEntry *rte = planner_rt_fetch(rti, subroot);
|
||||||
|
|
||||||
Assert(rte->rtekind == RTE_RELATION);
|
Assert(rte->rtekind == RTE_RELATION);
|
||||||
if (rte->relkind == RELKIND_FOREIGN_TABLE)
|
if (rte->relkind == RELKIND_FOREIGN_TABLE)
|
||||||
@ -6672,15 +6676,15 @@ make_modifytable(PlannerInfo *root,
|
|||||||
fdwroutine->IterateDirectModify != NULL &&
|
fdwroutine->IterateDirectModify != NULL &&
|
||||||
fdwroutine->EndDirectModify != NULL &&
|
fdwroutine->EndDirectModify != NULL &&
|
||||||
withCheckOptionLists == NIL &&
|
withCheckOptionLists == NIL &&
|
||||||
!has_row_triggers(root, rti, operation))
|
!has_row_triggers(subroot, rti, operation))
|
||||||
direct_modify = fdwroutine->PlanDirectModify(root, node, rti, i);
|
direct_modify = fdwroutine->PlanDirectModify(subroot, node, rti, i);
|
||||||
if (direct_modify)
|
if (direct_modify)
|
||||||
direct_modify_plans = bms_add_member(direct_modify_plans, i);
|
direct_modify_plans = bms_add_member(direct_modify_plans, i);
|
||||||
|
|
||||||
if (!direct_modify &&
|
if (!direct_modify &&
|
||||||
fdwroutine != NULL &&
|
fdwroutine != NULL &&
|
||||||
fdwroutine->PlanForeignModify != NULL)
|
fdwroutine->PlanForeignModify != NULL)
|
||||||
fdw_private = fdwroutine->PlanForeignModify(root, node, rti, i);
|
fdw_private = fdwroutine->PlanForeignModify(subroot, node, rti, i);
|
||||||
else
|
else
|
||||||
fdw_private = NIL;
|
fdw_private = NIL;
|
||||||
fdw_private_list = lappend(fdw_private_list, fdw_private);
|
fdw_private_list = lappend(fdw_private_list, fdw_private);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user