VALUES(2,2,1,40,15,135);
INSERT INTO sales_fact VALUES(2,3,1,10,1,13);
SELECT * FROM sales_summary_bytime;
DELETE FROM sales_fact WHERE product_key = 1;
SELECT * FROM sales_summary_bytime;
UPDATE sales_fact SET units_sold = units_sold * 2;
SELECT * FROM sales_summary_bytime;
</programlisting>
</example>
<para>
<literal>AFTER</literal> triggers can also make use of <firstterm>transition
tables</firstterm> to inspect the entire set of rows changed by the triggering
statement. The <command>CREATE TRIGGER</command> command assigns names to one
or both transition tables, and then the function can refer to those names
as though they were read-only temporary tables.
<xref linkend="plpgsql-trigger-audit-transition-example"/> shows an example.
</para>
<example id="plpgsql-trigger-audit-transition-example">
<title>Auditing with Transition Tables</title>
<para>
This example produces the same results as
<xref linkend="plpgsql-trigger-audit-example"/>, but instead of using a
trigger that fires for every row, it uses a trigger that fires once
per statement, after collecting the relevant information in a transition
table. This can be significantly faster than the row-trigger approach
when the invoking statement has modified many rows. Notice that we must
make a separate trigger declaration for each kind of event, since the
<literal>REFERENCING</literal> clauses must be different for each case. But
this does not stop us from using a single trigger function if we choose.
(In practice, it might be better to use three separate functions and
avoid the run-time tests on <varname>TG_OP</varname>.)
</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 rows in emp_audit to reflect the operations 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, o.* FROM old_table o;
ELSIF (TG_OP = 'UPDATE') THEN
INSERT INTO emp_audit
SELECT 'U', now(), current_user, n.* FROM new_table n;
ELSIF (TG_OP = 'INSERT') THEN
INSERT INTO emp_audit
SELECT 'I', now(), current_user, n.* FROM new_table n;
END IF;
RETURN NULL; -- result is ignored since this is an AFTER trigger
END;
$emp_audit$ LANGUAGE plpgsql;
CREATE TRIGGER emp_audit_ins
AFTER INSERT ON emp
REFERENCING NEW TABLE AS new_table
FOR EACH STATEMENT EXECUTE FUNCTION process_emp_audit();
CREATE TRIGGER emp_audit_upd
AFTER UPDATE ON emp
REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table
FOR EACH STATEMENT EXECUTE FUNCTION process_emp_audit();
CREATE TRIGGER emp_audit_del
AFTER DELETE ON emp
REFERENCING OLD TABLE AS old_table
FOR EACH STATEMENT EXECUTE FUNCTION process_emp_audit();
</programlisting>
</example>
</sect2>
<sect2 id="plpgsql-event-trigger">
<title>Triggers on Events</title>
<para>
<application>PL/pgSQL</application> can be used to define
<link linkend="event-triggers">event triggers</link>.
<productname>PostgreSQL</productname> requires that a function that
is to be called as an event trigger must be declared as a function with
no arguments and a return type of <literal>event_trigger</literal>.
</para>
<para>
When a <application>PL/pgSQL</application> function is called as an
event trigger, several special variables are created automatically
in