Triggers

COMP3311 20T3 ♢ Triggers ♢ [0/18]
❖ Triggers

Triggers are

Examples of uses for triggers:
COMP3311 20T3 ♢ Triggers ♢ [1/18]
❖ Triggers (cont)

Triggers provide event-condition-action (ECA) programming:

Some typical variations within this:
COMP3311 20T3 ♢ Triggers ♢ [2/18]
❖ Triggers (cont)

SQL "standard" syntax for defining triggers:

CREATE TRIGGER TriggerName
{AFTER|BEFORE}  Event1 [ OR Event2 ... ]
[ FOR EACH ROW ]
ON TableName
[ WHEN ( Condition ) ]
Block of Procedural/SQL Code ;

Possible Events are INSERT, DELETE, UPDATE.

FOR EACH ROW clause ...

COMP3311 20T3 ♢ Triggers ♢ [3/18]
❖ Trigger Semantics

Triggers can be activated BEFORE or AFTER the event.

If activated BEFORE, can affect the change that occurs:

If activated AFTER, the effects of the event are visible: Note: OLD does not exist for insertion; NEW does not exist for deletion.
COMP3311 20T3 ♢ Triggers ♢ [4/18]
❖ Trigger Semantics (cont)

Sequence of activities during database update:

[Diagram:Pics/dbms/trigger-seq.png]


Reminder: BEFORE trigger can modify value of new tuple

COMP3311 20T3 ♢ Triggers ♢ [5/18]
❖ Trigger Semantics (cont)

Consider two triggers and an INSERT statement

create trigger X before insert on T Code1;
create trigger Y after insert on T Code2;
insert into T values (a,b,c,...);

Sequence of events:

Reminder: there is no OLD tuple for an INSERT trigger.
COMP3311 20T3 ♢ Triggers ♢ [6/18]
❖ Trigger Semantics (cont)

Consider two triggers and an UPDATE statement

create trigger X before update on T Code1;
create trigger Y after update on T Code2;
update T set b=j,c=k where a=m;

Sequence of events:

Reminder: both OLD and NEW exist in UPDATE triggers.
COMP3311 20T3 ♢ Triggers ♢ [7/18]
❖ Trigger Semantics (cont)

Consider two triggers and an DELETE statement

create trigger X before delete on T Code1;
create trigger Y after delete on T Code2;
delete from T where a=m;

Sequence of events:

Reminder: tuple NEW does not exist in DELETE triggers.
COMP3311 20T3 ♢ Triggers ♢ [8/18]
❖ Triggers in PostgreSQL

PostgreSQL triggers provide a mechanism for

Syntax for PostgreSQL trigger definition:

CREATE TRIGGER TriggerName
{AFTER|BEFORE}  Event1 [OR Event2 ...]
ON TableName
[ WHEN ( Condition ) ]
FOR EACH {ROW|STATEMENT}
EXECUTE PROCEDURE FunctionName(args...);

COMP3311 20T3 ♢ Triggers ♢ [9/18]
❖ Triggers in PostgreSQL (cont)

There is no restriction on what code can go in the function.

However a BEFORE function must contain one of:

RETURN old;    or    RETURN new;

depending on which version of the tuple is to be used.

It can also return NULL; no further checks are done and the operation fails.

If BEFORE trigger returns OLD, no change occurs.

If exception is raised in trigger function, no change occurs.

COMP3311 20T3 ♢ Triggers ♢ [10/18]
❖ Trigger Example #1

Consider a database of people in the USA:

create table Person (
   id      integer primary key,
   ssn     varchar(11) unique,
   ... e.g. family, given, street, town ...
   state   char(2), ...
);
create table States (
   id      integer primary key,
   code    char(2) unique,
   ... e.g. name, area, population, flag ... 
);

Constraint:  Person.state(select code from States),  or
exists (select id from States where code=Person.state)

COMP3311 20T3 ♢ Triggers ♢ [11/18]
❖ Trigger Example #1 (cont)

Example: ensure that only valid state codes are used:

create trigger checkState before insert or update
on Person for each row execute procedure checkState();

create function checkState() returns trigger as $$
begin
   -- normalise the user-supplied value
   new.state = upper(trim(new.state));
   if (new.state !~ '^[A-Z][A-Z]$') then
      raise exception 'Code must be two alpha chars';
   end if;
   -- implement referential integrity check
   select * from States where code=new.state;
   if (not found) then
      raise exception 'Invalid code %',new.state;
   end if;
   return new;
end;
$$ language plpgsql;

COMP3311 20T3 ♢ Triggers ♢ [12/18]
❖ Trigger Example #1 (cont)

Examples of how this trigger would behave:

insert into Person
   values('John',...,'Calif.',...);
-- fails with 'Statecode must be two alpha chars'

insert into Person
   values('Jane',...,'NY',...);
-- insert succeeds; Jane lives in New York

update Person
   set town='Sunnyvale',state='CA'
	 where name='Dave';
-- update succeeds; Dave moves to California

update Person
   set state='OZ' where name='Pete';
-- fails with 'Invalid state code OZ'

COMP3311 20T3 ♢ Triggers ♢ [13/18]
❖ Trigger Example #2

Example: department salary totals

Scenario:

Employee(id, name, address, dept, salary, ...)
Department(id, name, manager, totSal, ...)

An assertion that we wish to maintain:

create assertion TotalSalary check (
   not exists (
       select * from Department d
       where  d.totSal <> (select sum(e.salary)
                             from Employee e
                             where e.dept = d.id)
    )
)

COMP3311 20T3 ♢ Triggers ♢ [14/18]
❖ Trigger Example #2 (cont)

Events that might affect the validity of the database

A single assertion could check for this after each change.

With triggers, we have to program each case separately.

Each program implements updates to ensure assertion holds.

COMP3311 20T3 ♢ Triggers ♢ [15/18]
❖ Trigger Example #2 (cont)

Implement the Employee update triggers from above in PostgreSQL:

Case 1: new employees arrive

create trigger TotalSalary1
after insert on Employees
for each row execute procedure totalSalary1();

create function totalSalary1() returns trigger
as $$
begin
    if (new.dept is not null) then
        update Department
        set    totSal = totSal + new.salary
        where  Department.id = new.dept;
    end if;
    return new;
end;
$$ language plpgsql;

COMP3311 20T3 ♢ Triggers ♢ [16/18]
❖ Trigger Example #2 (cont)

Case 2: employees change departments/salaries

create trigger TotalSalary2
after update on Employee
for each row execute procedure totalSalary2();

create function totalSalary2() returns trigger
as $$
begin
    update Department
    set    totSal = totSal + new.salary
    where  Department.id = new.dept;
    update Department
    set    totSal = totSal - old.salary
    where  Department.id = old.dept;
    return new;
end;
$$ language plpgsql;

COMP3311 20T3 ♢ Triggers ♢ [17/18]
❖ Trigger Example #2 (cont)

Case 3: employees leave

create trigger TotalSalary3
after delete on Employee
for each row execute procedure totalSalary3();

create function totalSalary3() returns trigger
as $$
begin
    if (old.dept is not null) then
        update Department
        set    totSal = totSal - old.salary
        where  Department.id = old.dept;
    end if;
    return old;
end;
$$ language plpgsql;

COMP3311 20T3 ♢ Triggers ♢ [18/18]


Produced: 26 Oct 2021