<literal>AFTER</literal> or a statement-level trigger
fired <literal>BEFORE</literal> or <literal>AFTER</literal> is
always ignored; it might as well be null. However, any of these types of
triggers might still abort the entire operation by raising an error.
</para>
<para>
<xref linkend="plpgsql-trigger-example"/> shows an example of a
trigger function in <application>PL/pgSQL</application>.
</para>
<example id="plpgsql-trigger-example">
<title>A <application>PL/pgSQL</application> Trigger Function</title>
<para>
This example trigger ensures that any time a row is inserted or updated
in the table, the current user name and time are stamped into the
row. And it checks that an employee's name is given and that the
salary is a positive value.
</para>
<programlisting>
CREATE TABLE emp (
empname text,
salary integer,
last_date timestamp,
last_user text
);
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Check that empname and salary are given
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname cannot be null';
END IF;
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% cannot have null salary', NEW.empname;
END IF;
-- Who works for us when they must pay for it?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
END IF;
-- Remember who changed the payroll when
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;
CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
FOR EACH ROW EXECUTE FUNCTION emp_stamp();
</programlisting>
</example>
<para>
Another way to log changes to a table involves creating a new table that
holds a row for each insert, update, or delete that occurs. This approach
can be thought of as auditing changes to a table.
<xref linkend="plpgsql-trigger-audit-example"/> shows an example of an
audit trigger function in <application>PL/pgSQL</application>.
</para>
<example id="plpgsql-trigger-audit-example">
<title>A <application>PL/pgSQL</application> Trigger Function for Auditing</title>
<para>
This example trigger ensures that any insert, update or delete of a row
in the <literal>emp</literal> table is recorded (i.e., audited) in the <literal>emp_audit</literal> table.
The current time and user name are stamped into the row, together with
the type of operation performed on it.
</para>
<programlisting>
CREATE TABLE emp (
empname text NOT NULL,
salary integer
);
CREATE TABLE emp_audit(
operation char(1) NOT NULL,
stamp timestamp NOT NULL,
userid text NOT NULL,
empname text NOT NULL,
salary integer
);
CREATE OR REPLACE FUNCTION process_emp_audit() RETURNS TRIGGER AS $emp_audit$
BEGIN
--
-- Create a row in emp_audit to reflect the operation performed on emp,
-- making use of the special variable TG_OP to work out the operation.
--
IF (TG_OP = 'DELETE') THEN
INSERT INTO emp_audit SELECT 'D', now(), current_user, OLD.*;
ELSIF (TG_OP = 'UPDATE') THEN
INSERT INTO emp_audit SELECT 'U', now(), current_user, NEW.*;
ELSIF (TG_OP = 'INSERT') THEN
INSERT INTO emp_audit SELECT 'I', now(), current_user, NEW.*;
END IF;
RETURN NULL; -- result is ignored since this is an AFTER trigger
END;
$emp_audit$ LANGUAGE plpgsql;
CREATE TRIGGER emp_audit
AFTER INSERT OR UPDATE OR DELETE ON emp
FOR EACH ROW EXECUTE FUNCTION process_emp_audit();
</programlisting>
</example>
<para>
A variation of the previous example uses a view joining the main table
to the audit