aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/executor/nodeModifyTable.c9
-rw-r--r--src/backend/optimizer/plan/planner.c14
-rw-r--r--src/backend/optimizer/util/pathnode.c2
-rw-r--r--src/include/nodes/pathnodes.h2
-rw-r--r--src/include/nodes/plannodes.h13
-rw-r--r--src/test/regress/expected/inherit.out27
-rw-r--r--src/test/regress/sql/inherit.sql19
7 files changed, 64 insertions, 22 deletions
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index c87bb03219c..d54424071c4 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -3966,10 +3966,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
* must be converted, and
* - the root partitioned table used for tuple routing.
*
- * If it's a partitioned table, the root partition doesn't appear
- * elsewhere in the plan and its RT index is given explicitly in
- * node->rootRelation. Otherwise (i.e. table inheritance) the target
- * relation is the first relation in the node->resultRelations list.
+ * If it's a partitioned or inherited table, the root partition or
+ * appendrel RTE doesn't appear elsewhere in the plan and its RT index is
+ * given explicitly in node->rootRelation. Otherwise, the target relation
+ * is the sole relation in the node->resultRelations list.
*----------
*/
if (node->rootRelation > 0)
@@ -3980,6 +3980,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
}
else
{
+ Assert(list_length(node->resultRelations) == 1);
mtstate->rootResultRelInfo = mtstate->resultRelInfo;
ExecInitResultRelation(estate, mtstate->resultRelInfo,
linitial_int(node->resultRelations));
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index af69ce8d095..80ad6bf1741 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1780,6 +1780,9 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
parse->resultRelation);
int resultRelation = -1;
+ /* Pass the root result rel forward to the executor. */
+ rootRelation = parse->resultRelation;
+
/* Add only leaf children to ModifyTable. */
while ((resultRelation = bms_next_member(root->leaf_result_relids,
resultRelation)) >= 0)
@@ -1903,6 +1906,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
else
{
/* Single-relation INSERT/UPDATE/DELETE/MERGE. */
+ rootRelation = 0; /* there's no separate root rel */
resultRelations = list_make1_int(parse->resultRelation);
if (parse->commandType == CMD_UPDATE)
updateColnosLists = list_make1(root->update_colnos);
@@ -1915,16 +1919,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
}
/*
- * If target is a partition root table, we need to mark the
- * ModifyTable node appropriately for that.
- */
- if (rt_fetch(parse->resultRelation, parse->rtable)->relkind ==
- RELKIND_PARTITIONED_TABLE)
- rootRelation = parse->resultRelation;
- else
- rootRelation = 0;
-
- /*
* If there was a FOR [KEY] UPDATE/SHARE clause, the LockRows node
* will have dealt with fetching non-locked marked rows, else we
* need to have ModifyTable do that.
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index e518a07e2c6..2185fc35a37 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -3646,7 +3646,7 @@ create_lockrows_path(PlannerInfo *root, RelOptInfo *rel,
* 'operation' is the operation type
* 'canSetTag' is true if we set the command tag/es_processed
* 'nominalRelation' is the parent RT index for use of EXPLAIN
- * 'rootRelation' is the partitioned table root RT index, or 0 if none
+ * 'rootRelation' is the partitioned/inherited table root RTI, or 0 if none
* 'partColsUpdated' is true if any partitioning columns are being updated,
* either from the target relation or a descendent partitioned table.
* 'resultRelations' is an integer list of actual RT indexes of target rel(s)
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index a1dc1d07e18..94aebadd9f9 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -2334,7 +2334,7 @@ typedef struct ModifyTablePath
CmdType operation; /* INSERT, UPDATE, DELETE, or MERGE */
bool canSetTag; /* do we set the command tag/es_processed? */
Index nominalRelation; /* Parent RT index for use of EXPLAIN */
- Index rootRelation; /* Root RT index, if target is partitioned */
+ Index rootRelation; /* Root RT index, if partitioned/inherited */
bool partColsUpdated; /* some part key in hierarchy updated? */
List *resultRelations; /* integer list of RT indexes */
List *updateColnosLists; /* per-target-table update_colnos lists */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 8cafbf3f8a4..d64fe6a328b 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -216,11 +216,12 @@ typedef struct ProjectSet
* Apply rows produced by outer plan to result table(s),
* by inserting, updating, or deleting.
*
- * If the originally named target table is a partitioned table, both
- * nominalRelation and rootRelation contain the RT index of the partition
- * root, which is not otherwise mentioned in the plan. Otherwise rootRelation
- * is zero. However, nominalRelation will always be set, as it's the rel that
- * EXPLAIN should claim is the INSERT/UPDATE/DELETE/MERGE target.
+ * If the originally named target table is a partitioned table or inheritance
+ * tree, both nominalRelation and rootRelation contain the RT index of the
+ * partition root or appendrel RTE, which is not otherwise mentioned in the
+ * plan. Otherwise rootRelation is zero. However, nominalRelation will
+ * always be set, as it's the rel that EXPLAIN should claim is the
+ * INSERT/UPDATE/DELETE/MERGE target.
*
* Note that rowMarks and epqParam are presumed to be valid for all the
* table(s); they can't contain any info that varies across tables.
@@ -232,7 +233,7 @@ typedef struct ModifyTable
CmdType operation; /* INSERT, UPDATE, DELETE, or MERGE */
bool canSetTag; /* do we set the command tag/es_processed? */
Index nominalRelation; /* Parent RT index for use of EXPLAIN */
- Index rootRelation; /* Root RT index, if target is partitioned */
+ Index rootRelation; /* Root RT index, if partitioned/inherited */
bool partColsUpdated; /* some part key in hierarchy updated? */
List *resultRelations; /* integer list of RT indexes */
List *updateColnosLists; /* per-target-table update_colnos lists */
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index a7fbeed9eb9..0aa0d410a12 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -539,6 +539,33 @@ 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" of relation "z" violates not-null constraint
DETAIL: Failing row contains (null, text).
+-- Check inherited UPDATE with first child excluded
+create table some_tab (f1 int, f2 int, f3 int, check (f1 < 10) no inherit);
+create table some_tab_child () inherits(some_tab);
+insert into some_tab_child select i, i+1, 0 from generate_series(1,1000) i;
+create index on some_tab_child(f1, f2);
+-- while at it, also check that statement-level triggers fire
+create function some_tab_stmt_trig_func() returns trigger as
+$$begin raise notice 'updating some_tab'; return NULL; end;$$
+language plpgsql;
+create trigger some_tab_stmt_trig
+ before update on some_tab execute function some_tab_stmt_trig_func();
+explain (costs off)
+update some_tab set f3 = 11 where f1 = 12 and f2 = 13;
+ QUERY PLAN
+------------------------------------------------------------------------------------
+ Update on some_tab
+ Update on some_tab_child some_tab_1
+ -> Result
+ -> Index Scan using some_tab_child_f1_f2_idx on some_tab_child some_tab_1
+ Index Cond: ((f1 = 12) AND (f2 = 13))
+(5 rows)
+
+update some_tab set f3 = 11 where f1 = 12 and f2 = 13;
+NOTICE: updating some_tab
+drop table some_tab cascade;
+NOTICE: drop cascades to table some_tab_child
+drop function some_tab_stmt_trig_func();
-- Check inherited UPDATE with all children excluded
create table some_tab (a int, b int);
create table some_tab_child () inherits (some_tab);
diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql
index 215d58e80d3..e15f9c364c2 100644
--- a/src/test/regress/sql/inherit.sql
+++ b/src/test/regress/sql/inherit.sql
@@ -97,6 +97,25 @@ 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 first child excluded
+create table some_tab (f1 int, f2 int, f3 int, check (f1 < 10) no inherit);
+create table some_tab_child () inherits(some_tab);
+insert into some_tab_child select i, i+1, 0 from generate_series(1,1000) i;
+create index on some_tab_child(f1, f2);
+-- while at it, also check that statement-level triggers fire
+create function some_tab_stmt_trig_func() returns trigger as
+$$begin raise notice 'updating some_tab'; return NULL; end;$$
+language plpgsql;
+create trigger some_tab_stmt_trig
+ before update on some_tab execute function some_tab_stmt_trig_func();
+
+explain (costs off)
+update some_tab set f3 = 11 where f1 = 12 and f2 = 13;
+update some_tab set f3 = 11 where f1 = 12 and f2 = 13;
+
+drop table some_tab cascade;
+drop function some_tab_stmt_trig_func();
+
-- Check inherited UPDATE with all children excluded
create table some_tab (a int, b int);
create table some_tab_child () inherits (some_tab);