fix-empty-plan-with-RETURNING-3.patch
text/plain
Filename: fix-empty-plan-with-RETURNING-3.patch
Type: text/plain
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/backend/optimizer/plan/planner.c | 27 | 4 |
| src/test/regress/expected/inherit.out | 41 | 0 |
| src/test/regress/expected/triggers.out | 32 | 0 |
| src/test/regress/sql/inherit.sql | 15 | 0 |
| src/test/regress/sql/triggers.sql | 22 | 0 |
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 5579dfa65e..4a9b9c16f0 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1584,13 +1584,36 @@ inheritance_planner(PlannerInfo *root)
*/
/*
- * If we managed to exclude every child rel, return a dummy plan; it
- * doesn't even need a ModifyTable node.
+ * We managed to exclude every child rel, so generate a dummy path
+ * representing empty set. Although it's clear that no data will be
+ * updated or deleted, we will still need to have a ModifyTable node so
+ * that any statement triggers are executed.
*/
if (subpaths == NIL)
{
- set_dummy_rel_pathlist(final_rel);
- return;
+ Path *dummy_path;
+ List *tlist;
+
+ tlist = root->processed_tlist = preprocess_targetlist(root);
+ final_rel->reltarget = create_pathtarget(root, tlist);
+
+ /*
+ * Following information will be needed to make a minimally valid
+ * ModifyTable.
+ */
+ dummy_path = (Path *) create_append_path(NULL, final_rel, NIL, NIL,
+ NULL, 0, false, NIL, -1);
+ resultRelations = list_make1_int(parse->resultRelation);
+ subpaths = list_make1(dummy_path);
+ subroots = list_make1(root);
+ if (parse->returningList)
+ returningLists = list_make1(parse->returningList);
+
+ /*
+ * At least simple_rte_array must be valid for plan creation to work
+ * correctly.
+ */
+ final_rtable = parse->rtable;
}
/*
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index f259d07535..565d947b6d 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -539,6 +539,47 @@ CREATE TEMP TABLE z (b TEXT, PRIMARY KEY(aa, b)) inherits (a);
INSERT INTO z VALUES (NULL, 'text'); -- should fail
ERROR: null value in column "aa" violates not-null constraint
DETAIL: Failing row contains (null, text).
+-- Check inherited UPDATE with all children excluded
+create table some_tab (a int, b int);
+create table some_tab_child () inherits (some_tab);
+insert into some_tab_child values(1,2);
+explain (verbose, costs off)
+update some_tab set a = a + 1 where false;
+ QUERY PLAN
+----------------------------------
+ Update on public.some_tab
+ Update on public.some_tab
+ -> Result
+ Output: (a + 1), b, ctid
+ One-Time Filter: false
+(5 rows)
+
+update some_tab set a = a + 1 where false;
+explain (verbose, costs off)
+update some_tab set a = a + 1 where false returning b, a;
+ QUERY PLAN
+----------------------------------
+ Update on public.some_tab
+ Output: b, a
+ Update on public.some_tab
+ -> Result
+ Output: (a + 1), b, ctid
+ One-Time Filter: false
+(6 rows)
+
+update some_tab set a = a + 1 where false returning b, a;
+ b | a
+---+---
+(0 rows)
+
+table some_tab;
+ a | b
+---+---
+ 1 | 2
+(1 row)
+
+drop table some_tab cascade;
+NOTICE: drop cascades to table some_tab_child
-- Check UPDATE with inherited target and an inherited source table
create temp table foo(f1 int, f2 int);
create temp table foo2(f3 int) inherits (foo);
diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out
index 2b908ceec4..40aa8d18ae 100644
--- a/src/test/regress/expected/triggers.out
+++ b/src/test/regress/expected/triggers.out
@@ -2790,3 +2790,35 @@ drop table self_ref;
drop function dump_insert();
drop function dump_update();
drop function dump_delete();
+-- Check that statement triggers work correctly even with an empty update
+create table stmt_trig_on_empty_upd (a int);
+create table stmt_trig_on_empty_upd1 () inherits (stmt_trig_on_empty_upd);
+create or replace function update_stmt_notice() returns trigger as $$
+begin
+ raise notice 'updating %', TG_TABLE_NAME;
+ return null;
+end;
+$$ language plpgsql;
+create trigger before_stmt_trigger
+ before update on stmt_trig_on_empty_upd
+ execute function update_stmt_notice();
+create trigger before_stmt_trigger
+ before update on stmt_trig_on_empty_upd1
+ execute function update_stmt_notice();
+-- inherited update
+update stmt_trig_on_empty_upd set a = a where false returning a+1 as aa;
+NOTICE: updating stmt_trig_on_empty_upd
+ aa
+----
+(0 rows)
+
+-- simple update
+update stmt_trig_on_empty_upd1 set a = a where false returning a+1 as aa;
+NOTICE: updating stmt_trig_on_empty_upd1
+ aa
+----
+(0 rows)
+
+drop table stmt_trig_on_empty_upd cascade;
+NOTICE: drop cascades to table stmt_trig_on_empty_upd1
+drop function update_stmt_notice;
diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql
index 425052c1f4..5480fe7db4 100644
--- a/src/test/regress/sql/inherit.sql
+++ b/src/test/regress/sql/inherit.sql
@@ -97,6 +97,21 @@ SELECT relname, d.* FROM ONLY d, pg_class where d.tableoid = pg_class.oid;
CREATE TEMP TABLE z (b TEXT, PRIMARY KEY(aa, b)) inherits (a);
INSERT INTO z VALUES (NULL, 'text'); -- should fail
+-- Check inherited UPDATE with all children excluded
+create table some_tab (a int, b int);
+create table some_tab_child () inherits (some_tab);
+insert into some_tab_child values(1,2);
+
+explain (verbose, costs off)
+update some_tab set a = a + 1 where false;
+update some_tab set a = a + 1 where false;
+explain (verbose, costs off)
+update some_tab set a = a + 1 where false returning b, a;
+update some_tab set a = a + 1 where false returning b, a;
+table some_tab;
+
+drop table some_tab cascade;
+
-- Check UPDATE with inherited target and an inherited source table
create temp table foo(f1 int, f2 int);
create temp table foo2(f3 int) inherits (foo);
diff --git a/src/test/regress/sql/triggers.sql b/src/test/regress/sql/triggers.sql
index 60d1dc6f46..73128a6542 100644
--- a/src/test/regress/sql/triggers.sql
+++ b/src/test/regress/sql/triggers.sql
@@ -2124,3 +2124,25 @@ drop table self_ref;
drop function dump_insert();
drop function dump_update();
drop function dump_delete();
+
+-- Check that statement triggers work correctly even with an empty update
+create table stmt_trig_on_empty_upd (a int);
+create table stmt_trig_on_empty_upd1 () inherits (stmt_trig_on_empty_upd);
+create or replace function update_stmt_notice() returns trigger as $$
+begin
+ raise notice 'updating %', TG_TABLE_NAME;
+ return null;
+end;
+$$ language plpgsql;
+create trigger before_stmt_trigger
+ before update on stmt_trig_on_empty_upd
+ execute function update_stmt_notice();
+create trigger before_stmt_trigger
+ before update on stmt_trig_on_empty_upd1
+ execute function update_stmt_notice();
+-- inherited update
+update stmt_trig_on_empty_upd set a = a where false returning a+1 as aa;
+-- simple update
+update stmt_trig_on_empty_upd1 set a = a where false returning a+1 as aa;
+drop table stmt_trig_on_empty_upd cascade;
+drop function update_stmt_notice;