Home Explore Blog CI



postgresql

60th chunk of `doc/src/sgml/plpgsql.sgml`
d255b560aee3c602b7f33e88f81caa07b0619830b36fefcc0000000100000fa1
 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

Title: Auditing with Transition Tables and Event Triggers in PL/pgSQL
Summary
This section explains how to use transition tables in AFTER triggers to audit changes in a table, providing an example of an auditing system for an 'emp' (employee) table. It demonstrates how to create triggers that fire once per statement (instead of per row) after collecting information in transition tables, potentially improving performance when many rows are modified. The example includes triggers for INSERT, UPDATE, and DELETE operations, all calling a single PL/pgSQL function (`process_emp_audit`) that determines the operation type using `TG_OP` and inserts audit records into an `emp_audit` table. The section then transitions to introduce event triggers in PL/pgSQL, noting that event trigger functions must have no arguments and a return type of `event_trigger`.