Skip to content

Commit b2bdba8

Browse files
davidfetterhackorum
authored andcommitted
Allow WHEN in INSTEAD OF triggers
This was disallowed for reasons that aren't entirely obvious, so allow.
1 parent 6e89028 commit b2bdba8

4 files changed

Lines changed: 57 additions & 24 deletions

File tree

doc/src/sgml/ref/create_trigger.sgml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -398,10 +398,6 @@ UPDATE OF <replaceable>column_name1</replaceable> [, <replaceable>column_name2</
398398
and <literal>DELETE</literal> triggers cannot refer to <literal>NEW</literal>.
399399
</para>
400400

401-
<para><literal>INSTEAD OF</literal> triggers do not support <literal>WHEN</literal>
402-
conditions.
403-
</para>
404-
405401
<para>
406402
Currently, <literal>WHEN</literal> expressions cannot contain
407403
subqueries.

src/backend/commands/trigger.c

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -395,17 +395,13 @@ CreateTriggerFiringOn(const CreateTrigStmt *stmt, const char *queryString,
395395
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
396396
errmsg("TRUNCATE FOR EACH ROW triggers are not supported")));
397397

398-
/* INSTEAD triggers must be row-level, and can't have WHEN or columns */
398+
/* INSTEAD triggers must be row-level, and can't have columns */
399399
if (TRIGGER_FOR_INSTEAD(tgtype))
400400
{
401401
if (!TRIGGER_FOR_ROW(tgtype))
402402
ereport(ERROR,
403403
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
404404
errmsg("INSTEAD OF triggers must be FOR EACH ROW")));
405-
if (stmt->whenClause)
406-
ereport(ERROR,
407-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
408-
errmsg("INSTEAD OF triggers cannot have WHEN conditions")));
409405
if (stmt->columns != NIL)
410406
ereport(ERROR,
411407
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),

src/test/regress/expected/triggers.out

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -777,7 +777,11 @@ begin
777777
end if;
778778

779779
if TG_OP = 'UPDATE' then
780-
raise NOTICE 'OLD: %, NEW: %', OLD, NEW;
780+
if strpos(argstr, 'instead_of_when') > 0 then
781+
raise NOTICE 'instead_of_when fired';
782+
else
783+
raise NOTICE 'OLD: %, NEW: %', OLD, NEW;
784+
end if;
781785
UPDATE main_table SET a = NEW.a, b = NEW.b WHERE a = OLD.a AND b = OLD.b;
782786
if NOT FOUND then RETURN NULL; end if;
783787
RETURN NEW;
@@ -842,10 +846,6 @@ CREATE TRIGGER invalid_trig INSTEAD OF DELETE ON main_table
842846
FOR EACH ROW EXECUTE PROCEDURE view_trigger('instead_of_del');
843847
ERROR: "main_table" is a table
844848
DETAIL: Tables cannot have INSTEAD OF triggers.
845-
-- Don't support WHEN clauses with INSTEAD OF triggers
846-
CREATE TRIGGER invalid_trig INSTEAD OF UPDATE ON main_view
847-
FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE view_trigger('instead_of_upd');
848-
ERROR: INSTEAD OF triggers cannot have WHEN conditions
849849
-- Don't support column-level INSTEAD OF triggers
850850
CREATE TRIGGER invalid_trig INSTEAD OF UPDATE OF a ON main_view
851851
FOR EACH ROW EXECUTE PROCEDURE view_trigger('instead_of_upd');
@@ -861,6 +861,9 @@ CREATE TRIGGER instead_of_update_trig INSTEAD OF UPDATE ON main_view
861861
FOR EACH ROW EXECUTE PROCEDURE view_trigger('instead_of_upd');
862862
CREATE TRIGGER instead_of_delete_trig INSTEAD OF DELETE ON main_view
863863
FOR EACH ROW EXECUTE PROCEDURE view_trigger('instead_of_del');
864+
CREATE TRIGGER when_different_update INSTEAD OF UPDATE ON main_view
865+
FOR EACH ROW WHEN (OLD.a IS DISTINCT FROM NEW.a)
866+
EXECUTE PROCEDURE view_trigger('instead_of_when');
864867
-- Valid BEFORE statement VIEW triggers
865868
CREATE TRIGGER before_ins_stmt_trig BEFORE INSERT ON main_view
866869
FOR EACH STATEMENT EXECUTE PROCEDURE view_trigger('before_view_ins_stmt');
@@ -957,18 +960,47 @@ UPDATE 1
957960
UPDATE main_view SET b = 0 WHERE false;
958961
NOTICE: main_view BEFORE UPDATE STATEMENT (before_view_upd_stmt)
959962
NOTICE: main_view AFTER UPDATE STATEMENT (after_view_upd_stmt)
963+
UPDATE 0
964+
-- INSTEAD OF ... WHEN trigger fires.
965+
UPDATE main_view SET a = 23 WHERE a = 21 RETURNING *;
966+
NOTICE: main_view BEFORE UPDATE STATEMENT (before_view_upd_stmt)
967+
NOTICE: main_view INSTEAD OF UPDATE ROW (instead_of_upd)
968+
NOTICE: OLD: (21,10), NEW: (23,10)
969+
NOTICE: trigger_func(before_upd_a_stmt) called: action = UPDATE, when = BEFORE, level = STATEMENT
970+
NOTICE: trigger_func(after_upd_a_b_row) called: action = UPDATE, when = AFTER, level = ROW
971+
NOTICE: trigger_func(after_upd_b_row) called: action = UPDATE, when = AFTER, level = ROW
972+
NOTICE: trigger_func(after_upd_b_stmt) called: action = UPDATE, when = AFTER, level = STATEMENT
973+
NOTICE: trigger_func(after_upd_stmt) called: action = UPDATE, when = AFTER, level = STATEMENT
974+
NOTICE: main_view INSTEAD OF UPDATE ROW (instead_of_when)
975+
NOTICE: instead_of_when fired
976+
NOTICE: trigger_func(before_upd_a_stmt) called: action = UPDATE, when = BEFORE, level = STATEMENT
977+
NOTICE: trigger_func(after_upd_b_stmt) called: action = UPDATE, when = AFTER, level = STATEMENT
978+
NOTICE: trigger_func(after_upd_stmt) called: action = UPDATE, when = AFTER, level = STATEMENT
979+
NOTICE: main_view INSTEAD OF UPDATE ROW (instead_of_upd)
980+
NOTICE: OLD: (21,32), NEW: (23,32)
981+
NOTICE: trigger_func(before_upd_a_stmt) called: action = UPDATE, when = BEFORE, level = STATEMENT
982+
NOTICE: trigger_func(after_upd_a_b_row) called: action = UPDATE, when = AFTER, level = ROW
983+
NOTICE: trigger_func(after_upd_b_row) called: action = UPDATE, when = AFTER, level = ROW
984+
NOTICE: trigger_func(after_upd_b_stmt) called: action = UPDATE, when = AFTER, level = STATEMENT
985+
NOTICE: trigger_func(after_upd_stmt) called: action = UPDATE, when = AFTER, level = STATEMENT
986+
NOTICE: main_view INSTEAD OF UPDATE ROW (instead_of_when)
987+
NOTICE: instead_of_when fired
988+
NOTICE: trigger_func(before_upd_a_stmt) called: action = UPDATE, when = BEFORE, level = STATEMENT
989+
NOTICE: trigger_func(after_upd_b_stmt) called: action = UPDATE, when = AFTER, level = STATEMENT
990+
NOTICE: trigger_func(after_upd_stmt) called: action = UPDATE, when = AFTER, level = STATEMENT
991+
NOTICE: main_view AFTER UPDATE STATEMENT (after_view_upd_stmt)
992+
a | b
993+
---+---
994+
(0 rows)
995+
960996
UPDATE 0
961997
-- Delete from view using trigger
962998
DELETE FROM main_view WHERE a IN (20,21);
963999
NOTICE: main_view BEFORE DELETE STATEMENT (before_view_del_stmt)
9641000
NOTICE: main_view INSTEAD OF DELETE ROW (instead_of_del)
965-
NOTICE: OLD: (21,10)
966-
NOTICE: main_view INSTEAD OF DELETE ROW (instead_of_del)
9671001
NOTICE: OLD: (20,31)
968-
NOTICE: main_view INSTEAD OF DELETE ROW (instead_of_del)
969-
NOTICE: OLD: (21,32)
9701002
NOTICE: main_view AFTER DELETE STATEMENT (after_view_del_stmt)
971-
DELETE 3
1003+
DELETE 1
9721004
DELETE FROM main_view WHERE a = 31 RETURNING a, b;
9731005
NOTICE: main_view BEFORE DELETE STATEMENT (before_view_del_stmt)
9741006
NOTICE: main_view INSTEAD OF DELETE ROW (instead_of_del)
@@ -998,6 +1030,7 @@ Triggers:
9981030
instead_of_delete_trig INSTEAD OF DELETE ON main_view FOR EACH ROW EXECUTE FUNCTION view_trigger('instead_of_del')
9991031
instead_of_insert_trig INSTEAD OF INSERT ON main_view FOR EACH ROW EXECUTE FUNCTION view_trigger('instead_of_ins')
10001032
instead_of_update_trig INSTEAD OF UPDATE ON main_view FOR EACH ROW EXECUTE FUNCTION view_trigger('instead_of_upd')
1033+
when_different_update INSTEAD OF UPDATE ON main_view FOR EACH ROW WHEN (old.a IS DISTINCT FROM new.a) EXECUTE FUNCTION view_trigger('instead_of_when')
10011034

10021035
-- Test dropping view triggers
10031036
DROP TRIGGER instead_of_insert_trig ON main_view;
@@ -1020,6 +1053,7 @@ Triggers:
10201053
before_ins_stmt_trig BEFORE INSERT ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('before_view_ins_stmt')
10211054
before_upd_stmt_trig BEFORE UPDATE ON main_view FOR EACH STATEMENT EXECUTE FUNCTION view_trigger('before_view_upd_stmt')
10221055
instead_of_update_trig INSTEAD OF UPDATE ON main_view FOR EACH ROW EXECUTE FUNCTION view_trigger('instead_of_upd')
1056+
when_different_update INSTEAD OF UPDATE ON main_view FOR EACH ROW WHEN (old.a IS DISTINCT FROM new.a) EXECUTE FUNCTION view_trigger('instead_of_when')
10231057

10241058
DROP VIEW main_view;
10251059
--

src/test/regress/sql/triggers.sql

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,11 @@ begin
544544
end if;
545545

546546
if TG_OP = 'UPDATE' then
547-
raise NOTICE 'OLD: %, NEW: %', OLD, NEW;
547+
if strpos(argstr, 'instead_of_when') > 0 then
548+
raise NOTICE 'instead_of_when fired';
549+
else
550+
raise NOTICE 'OLD: %, NEW: %', OLD, NEW;
551+
end if;
548552
UPDATE main_table SET a = NEW.a, b = NEW.b WHERE a = OLD.a AND b = OLD.b;
549553
if NOT FOUND then RETURN NULL; end if;
550554
RETURN NEW;
@@ -599,10 +603,6 @@ FOR EACH ROW EXECUTE PROCEDURE view_trigger('instead_of_upd');
599603
CREATE TRIGGER invalid_trig INSTEAD OF DELETE ON main_table
600604
FOR EACH ROW EXECUTE PROCEDURE view_trigger('instead_of_del');
601605

602-
-- Don't support WHEN clauses with INSTEAD OF triggers
603-
CREATE TRIGGER invalid_trig INSTEAD OF UPDATE ON main_view
604-
FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE view_trigger('instead_of_upd');
605-
606606
-- Don't support column-level INSTEAD OF triggers
607607
CREATE TRIGGER invalid_trig INSTEAD OF UPDATE OF a ON main_view
608608
FOR EACH ROW EXECUTE PROCEDURE view_trigger('instead_of_upd');
@@ -621,6 +621,10 @@ FOR EACH ROW EXECUTE PROCEDURE view_trigger('instead_of_upd');
621621
CREATE TRIGGER instead_of_delete_trig INSTEAD OF DELETE ON main_view
622622
FOR EACH ROW EXECUTE PROCEDURE view_trigger('instead_of_del');
623623

624+
CREATE TRIGGER when_different_update INSTEAD OF UPDATE ON main_view
625+
FOR EACH ROW WHEN (OLD.a IS DISTINCT FROM NEW.a)
626+
EXECUTE PROCEDURE view_trigger('instead_of_when');
627+
624628
-- Valid BEFORE statement VIEW triggers
625629
CREATE TRIGGER before_ins_stmt_trig BEFORE INSERT ON main_view
626630
FOR EACH STATEMENT EXECUTE PROCEDURE view_trigger('before_view_ins_stmt');
@@ -659,6 +663,9 @@ UPDATE main_view SET b = 32 WHERE a = 21 AND b = 31 RETURNING a, b;
659663
-- Before and after stmt triggers should fire even when no rows are affected
660664
UPDATE main_view SET b = 0 WHERE false;
661665

666+
-- INSTEAD OF ... WHEN trigger fires.
667+
UPDATE main_view SET a = 23 WHERE a = 21 RETURNING *;
668+
662669
-- Delete from view using trigger
663670
DELETE FROM main_view WHERE a IN (20,21);
664671
DELETE FROM main_view WHERE a = 31 RETURNING a, b;

0 commit comments

Comments
 (0)