Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 58947fb

Browse files
committed
Fix plan created for inherited UPDATE/DELETE with all tables excluded.
In the case where inheritance_planner() finds that every table has been excluded by constraints, it thought it could get away with making a plan consisting of just a dummy Result node. While certainly there's no updating or deleting to be done, this had two user-visible problems: the plan did not report the correct set of output columns when a RETURNING clause was present, and if there were any statement-level triggers that should be fired, it didn't fire them. Hence, rather than only generating the dummy Result, we need to stick a valid ModifyTable node on top, which requires a tad more effort here. It's been broken this way for as long as inheritance_planner() has known about deleting excluded subplans at all (cf commit 635d42e), so back-patch to all supported branches. Amit Langote and Tom Lane, per a report from Petr Fedorov. Discussion: https://postgr.es/m/[email protected]
1 parent 398cc6f commit 58947fb

File tree

5 files changed

+163
-21
lines changed

5 files changed

+163
-21
lines changed

src/backend/optimizer/plan/planner.c

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,32 +1398,57 @@ inheritance_planner(PlannerInfo *root)
13981398
* to get control here.
13991399
*/
14001400

1401-
/*
1402-
* If we managed to exclude every child rel, return a dummy plan; it
1403-
* doesn't even need a ModifyTable node.
1404-
*/
14051401
if (subpaths == NIL)
14061402
{
1407-
set_dummy_rel_pathlist(final_rel);
1408-
return;
1409-
}
1403+
/*
1404+
* We managed to exclude every child rel, so generate a dummy path
1405+
* representing the empty set. Although it's clear that no data will
1406+
* be updated or deleted, we will still need to have a ModifyTable
1407+
* node so that any statement triggers are executed. (This could be
1408+
* cleaner if we fixed nodeModifyTable.c to support zero child nodes,
1409+
* but that probably wouldn't be a net win.)
1410+
*/
1411+
List *tlist;
1412+
Path *dummy_path;
14101413

1411-
/*
1412-
* Put back the final adjusted rtable into the master copy of the Query.
1413-
* (We mustn't do this if we found no non-excluded children.)
1414-
*/
1415-
parse->rtable = final_rtable;
1416-
root->simple_rel_array_size = save_rel_array_size;
1417-
root->simple_rel_array = save_rel_array;
1418-
/* Must reconstruct master's simple_rte_array, too */
1419-
root->simple_rte_array = (RangeTblEntry **)
1420-
palloc0((list_length(final_rtable) + 1) * sizeof(RangeTblEntry *));
1421-
rti = 1;
1422-
foreach(lc, final_rtable)
1414+
/* tlist processing never got done, either */
1415+
tlist = root->processed_tlist = preprocess_targetlist(root);
1416+
final_rel->reltarget = create_pathtarget(root, tlist);
1417+
1418+
/* Make a dummy path, cf set_dummy_rel_pathlist() */
1419+
dummy_path = (Path *) create_append_path(final_rel, NIL,
1420+
NULL, 0, NIL);
1421+
1422+
/* These lists must be nonempty to make a valid ModifyTable node */
1423+
subpaths = list_make1(dummy_path);
1424+
subroots = list_make1(root);
1425+
resultRelations = list_make1_int(parse->resultRelation);
1426+
if (parse->withCheckOptions)
1427+
withCheckOptionLists = list_make1(parse->withCheckOptions);
1428+
if (parse->returningList)
1429+
returningLists = list_make1(parse->returningList);
1430+
}
1431+
else
14231432
{
1424-
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
1433+
/*
1434+
* Put back the final adjusted rtable into the master copy of the
1435+
* Query. (We mustn't do this if we found no non-excluded children,
1436+
* since we never saved an adjusted rtable at all.)
1437+
*/
1438+
parse->rtable = final_rtable;
1439+
root->simple_rel_array_size = save_rel_array_size;
1440+
root->simple_rel_array = save_rel_array;
1441+
1442+
/* Must reconstruct master's simple_rte_array, too */
1443+
root->simple_rte_array = (RangeTblEntry **)
1444+
palloc0((list_length(final_rtable) + 1) * sizeof(RangeTblEntry *));
1445+
rti = 1;
1446+
foreach(lc, final_rtable)
1447+
{
1448+
RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);
14251449

1426-
root->simple_rte_array[rti++] = rte;
1450+
root->simple_rte_array[rti++] = rte;
1451+
}
14271452
}
14281453

14291454
/*

src/test/regress/expected/inherit.out

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,47 @@ CREATE TEMP TABLE z (b TEXT, PRIMARY KEY(aa, b)) inherits (a);
539539
INSERT INTO z VALUES (NULL, 'text'); -- should fail
540540
ERROR: null value in column "aa" violates not-null constraint
541541
DETAIL: Failing row contains (null, text).
542+
-- Check inherited UPDATE with all children excluded
543+
create table some_tab (a int, b int);
544+
create table some_tab_child () inherits (some_tab);
545+
insert into some_tab_child values(1,2);
546+
explain (verbose, costs off)
547+
update some_tab set a = a + 1 where false;
548+
QUERY PLAN
549+
----------------------------------
550+
Update on public.some_tab
551+
Update on public.some_tab
552+
-> Result
553+
Output: (a + 1), b, ctid
554+
One-Time Filter: false
555+
(5 rows)
556+
557+
update some_tab set a = a + 1 where false;
558+
explain (verbose, costs off)
559+
update some_tab set a = a + 1 where false returning b, a;
560+
QUERY PLAN
561+
----------------------------------
562+
Update on public.some_tab
563+
Output: b, a
564+
Update on public.some_tab
565+
-> Result
566+
Output: (a + 1), b, ctid
567+
One-Time Filter: false
568+
(6 rows)
569+
570+
update some_tab set a = a + 1 where false returning b, a;
571+
b | a
572+
---+---
573+
(0 rows)
574+
575+
table some_tab;
576+
a | b
577+
---+---
578+
1 | 2
579+
(1 row)
580+
581+
drop table some_tab cascade;
582+
NOTICE: drop cascades to table some_tab_child
542583
-- Check UPDATE with inherited target and an inherited source table
543584
create temp table foo(f1 int, f2 int);
544585
create temp table foo2(f3 int) inherits (foo);

src/test/regress/expected/triggers.out

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1700,6 +1700,40 @@ drop table self_ref_trigger;
17001700
drop function self_ref_trigger_ins_func();
17011701
drop function self_ref_trigger_del_func();
17021702
--
1703+
-- Check that statement triggers work correctly even with all children excluded
1704+
--
1705+
create table stmt_trig_on_empty_upd (a int);
1706+
create table stmt_trig_on_empty_upd1 () inherits (stmt_trig_on_empty_upd);
1707+
create function update_stmt_notice() returns trigger as $$
1708+
begin
1709+
raise notice 'updating %', TG_TABLE_NAME;
1710+
return null;
1711+
end;
1712+
$$ language plpgsql;
1713+
create trigger before_stmt_trigger
1714+
before update on stmt_trig_on_empty_upd
1715+
execute procedure update_stmt_notice();
1716+
create trigger before_stmt_trigger
1717+
before update on stmt_trig_on_empty_upd1
1718+
execute procedure update_stmt_notice();
1719+
-- inherited no-op update
1720+
update stmt_trig_on_empty_upd set a = a where false returning a+1 as aa;
1721+
NOTICE: updating stmt_trig_on_empty_upd
1722+
aa
1723+
----
1724+
(0 rows)
1725+
1726+
-- simple no-op update
1727+
update stmt_trig_on_empty_upd1 set a = a where false returning a+1 as aa;
1728+
NOTICE: updating stmt_trig_on_empty_upd1
1729+
aa
1730+
----
1731+
(0 rows)
1732+
1733+
drop table stmt_trig_on_empty_upd cascade;
1734+
NOTICE: drop cascades to table stmt_trig_on_empty_upd1
1735+
drop function update_stmt_notice();
1736+
--
17031737
-- Check that index creation (or DDL in general) is prohibited in a trigger
17041738
--
17051739
create table trigger_ddl_table (

src/test/regress/sql/inherit.sql

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,21 @@ SELECT relname, d.* FROM ONLY d, pg_class where d.tableoid = pg_class.oid;
9797
CREATE TEMP TABLE z (b TEXT, PRIMARY KEY(aa, b)) inherits (a);
9898
INSERT INTO z VALUES (NULL, 'text'); -- should fail
9999

100+
-- Check inherited UPDATE with all children excluded
101+
create table some_tab (a int, b int);
102+
create table some_tab_child () inherits (some_tab);
103+
insert into some_tab_child values(1,2);
104+
105+
explain (verbose, costs off)
106+
update some_tab set a = a + 1 where false;
107+
update some_tab set a = a + 1 where false;
108+
explain (verbose, costs off)
109+
update some_tab set a = a + 1 where false returning b, a;
110+
update some_tab set a = a + 1 where false returning b, a;
111+
table some_tab;
112+
113+
drop table some_tab cascade;
114+
100115
-- Check UPDATE with inherited target and an inherited source table
101116
create temp table foo(f1 int, f2 int);
102117
create temp table foo2(f3 int) inherits (foo);

src/test/regress/sql/triggers.sql

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,33 @@ drop table self_ref_trigger;
11991199
drop function self_ref_trigger_ins_func();
12001200
drop function self_ref_trigger_del_func();
12011201

1202+
--
1203+
-- Check that statement triggers work correctly even with all children excluded
1204+
--
1205+
1206+
create table stmt_trig_on_empty_upd (a int);
1207+
create table stmt_trig_on_empty_upd1 () inherits (stmt_trig_on_empty_upd);
1208+
create function update_stmt_notice() returns trigger as $$
1209+
begin
1210+
raise notice 'updating %', TG_TABLE_NAME;
1211+
return null;
1212+
end;
1213+
$$ language plpgsql;
1214+
create trigger before_stmt_trigger
1215+
before update on stmt_trig_on_empty_upd
1216+
execute procedure update_stmt_notice();
1217+
create trigger before_stmt_trigger
1218+
before update on stmt_trig_on_empty_upd1
1219+
execute procedure update_stmt_notice();
1220+
1221+
-- inherited no-op update
1222+
update stmt_trig_on_empty_upd set a = a where false returning a+1 as aa;
1223+
-- simple no-op update
1224+
update stmt_trig_on_empty_upd1 set a = a where false returning a+1 as aa;
1225+
1226+
drop table stmt_trig_on_empty_upd cascade;
1227+
drop function update_stmt_notice();
1228+
12021229
--
12031230
-- Check that index creation (or DDL in general) is prohibited in a trigger
12041231
--

0 commit comments

Comments
 (0)