aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/executor/nodeModifyTable.c16
-rw-r--r--src/test/regress/expected/triggers.out45
-rw-r--r--src/test/regress/sql/triggers.sql36
3 files changed, 97 insertions, 0 deletions
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 299c2c75be8..b16fbe9e22a 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -2899,6 +2899,22 @@ lmerge_matched:
}
result = ExecUpdateAct(context, resultRelInfo, tupleid, NULL,
newslot, false, &updateCxt);
+
+ /*
+ * As in ExecUpdate(), if ExecUpdateAct() reports that a
+ * cross-partition update was done, then there's nothing else
+ * for us to do --- the UPDATE has been turned into a DELETE
+ * and an INSERT, and we must not perform any of the usual
+ * post-update tasks.
+ */
+ if (updateCxt.crossPartUpdate)
+ {
+ mtstate->mt_merge_updated += 1;
+ if (canSetTag)
+ (estate->es_processed)++;
+ return true;
+ }
+
if (result == TM_Ok && updateCxt.updated)
{
ExecUpdateEpilogue(context, &updateCxt, resultRelInfo,
diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out
index e8de916dfe2..78e90309238 100644
--- a/src/test/regress/expected/triggers.out
+++ b/src/test/regress/expected/triggers.out
@@ -2309,6 +2309,51 @@ NOTICE: trigger zzz on parted_trig_1_1 AFTER INSERT for ROW
NOTICE: trigger bbb on parted_trig_2 AFTER INSERT for ROW
NOTICE: trigger zzz on parted_trig_2 AFTER INSERT for ROW
drop table parted_trig;
+-- Verify that the correct triggers fire for cross-partition updates
+create table parted_trig (a int) partition by list (a);
+create table parted_trig1 partition of parted_trig for values in (1);
+create table parted_trig2 partition of parted_trig for values in (2);
+insert into parted_trig values (1);
+create or replace function trigger_notice() returns trigger as $$
+ begin
+ raise notice 'trigger % on % % % for %', TG_NAME, TG_TABLE_NAME, TG_WHEN, TG_OP, TG_LEVEL;
+ if TG_LEVEL = 'ROW' then
+ if TG_OP = 'DELETE' then
+ return OLD;
+ else
+ return NEW;
+ end if;
+ end if;
+ return null;
+ end;
+ $$ language plpgsql;
+create trigger parted_trig_before_stmt before insert or update or delete on parted_trig
+ for each statement execute procedure trigger_notice();
+create trigger parted_trig_before_row before insert or update or delete on parted_trig
+ for each row execute procedure trigger_notice();
+create trigger parted_trig_after_row after insert or update or delete on parted_trig
+ for each row execute procedure trigger_notice();
+create trigger parted_trig_after_stmt after insert or update or delete on parted_trig
+ for each statement execute procedure trigger_notice();
+update parted_trig set a = 2 where a = 1;
+NOTICE: trigger parted_trig_before_stmt on parted_trig BEFORE UPDATE for STATEMENT
+NOTICE: trigger parted_trig_before_row on parted_trig1 BEFORE UPDATE for ROW
+NOTICE: trigger parted_trig_before_row on parted_trig1 BEFORE DELETE for ROW
+NOTICE: trigger parted_trig_before_row on parted_trig2 BEFORE INSERT for ROW
+NOTICE: trigger parted_trig_after_row on parted_trig1 AFTER DELETE for ROW
+NOTICE: trigger parted_trig_after_row on parted_trig2 AFTER INSERT for ROW
+NOTICE: trigger parted_trig_after_stmt on parted_trig AFTER UPDATE for STATEMENT
+-- update action in merge should behave the same
+merge into parted_trig using (select 1) as ss on true
+ when matched and a = 2 then update set a = 1;
+NOTICE: trigger parted_trig_before_stmt on parted_trig BEFORE UPDATE for STATEMENT
+NOTICE: trigger parted_trig_before_row on parted_trig2 BEFORE UPDATE for ROW
+NOTICE: trigger parted_trig_before_row on parted_trig2 BEFORE DELETE for ROW
+NOTICE: trigger parted_trig_before_row on parted_trig1 BEFORE INSERT for ROW
+NOTICE: trigger parted_trig_after_row on parted_trig2 AFTER DELETE for ROW
+NOTICE: trigger parted_trig_after_row on parted_trig1 AFTER INSERT for ROW
+NOTICE: trigger parted_trig_after_stmt on parted_trig AFTER UPDATE for STATEMENT
+drop table parted_trig;
-- Verify propagation of trigger arguments to partitions
create table parted_trig (a int) partition by list (a);
create table parted_trig1 partition of parted_trig for values in (1);
diff --git a/src/test/regress/sql/triggers.sql b/src/test/regress/sql/triggers.sql
index d29e98d2ac9..46795a9c789 100644
--- a/src/test/regress/sql/triggers.sql
+++ b/src/test/regress/sql/triggers.sql
@@ -1583,6 +1583,42 @@ create trigger qqq after insert on parted_trig_1_1 for each row execute procedur
insert into parted_trig values (50), (1500);
drop table parted_trig;
+-- Verify that the correct triggers fire for cross-partition updates
+create table parted_trig (a int) partition by list (a);
+create table parted_trig1 partition of parted_trig for values in (1);
+create table parted_trig2 partition of parted_trig for values in (2);
+insert into parted_trig values (1);
+
+create or replace function trigger_notice() returns trigger as $$
+ begin
+ raise notice 'trigger % on % % % for %', TG_NAME, TG_TABLE_NAME, TG_WHEN, TG_OP, TG_LEVEL;
+ if TG_LEVEL = 'ROW' then
+ if TG_OP = 'DELETE' then
+ return OLD;
+ else
+ return NEW;
+ end if;
+ end if;
+ return null;
+ end;
+ $$ language plpgsql;
+create trigger parted_trig_before_stmt before insert or update or delete on parted_trig
+ for each statement execute procedure trigger_notice();
+create trigger parted_trig_before_row before insert or update or delete on parted_trig
+ for each row execute procedure trigger_notice();
+create trigger parted_trig_after_row after insert or update or delete on parted_trig
+ for each row execute procedure trigger_notice();
+create trigger parted_trig_after_stmt after insert or update or delete on parted_trig
+ for each statement execute procedure trigger_notice();
+
+update parted_trig set a = 2 where a = 1;
+
+-- update action in merge should behave the same
+merge into parted_trig using (select 1) as ss on true
+ when matched and a = 2 then update set a = 1;
+
+drop table parted_trig;
+
-- Verify propagation of trigger arguments to partitions
create table parted_trig (a int) partition by list (a);
create table parted_trig1 partition of parted_trig for values in (1);