2001-12-04 03:07:11 +01:00
|
|
|
<!--
|
2002-09-21 20:32:54 +02:00
|
|
|
$Header: /cvsroot/pgsql/doc/src/sgml/trigger.sgml,v 1.25 2002/09/21 18:32:54 petere Exp $
|
2001-12-04 03:07:11 +01:00
|
|
|
-->
|
|
|
|
|
2000-05-02 22:02:03 +02:00
|
|
|
<chapter id="triggers">
|
|
|
|
<title>Triggers</title>
|
|
|
|
|
|
|
|
<para>
|
2002-09-21 20:32:54 +02:00
|
|
|
<productname>PostgreSQL</productname> has various server-side
|
|
|
|
function interfaces. Server-side functions can be written in SQL,
|
|
|
|
C, or any defined procedural language. Trigger functions can be
|
|
|
|
written in C and most procedural languages, but not in SQL. Note that
|
|
|
|
statement-level trigger events are not supported in the current
|
|
|
|
version. You can currently specify BEFORE or AFTER on INSERT,
|
|
|
|
DELETE or UPDATE of a tuple as a trigger event.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
|
2002-04-19 18:36:08 +02:00
|
|
|
<sect1 id="trigger-definition">
|
|
|
|
<title>Trigger Definition</title>
|
2000-05-02 22:02:03 +02:00
|
|
|
|
|
|
|
<para>
|
|
|
|
If a trigger event occurs, the trigger manager (called by the Executor)
|
2002-09-21 20:32:54 +02:00
|
|
|
sets up a <structname>TriggerData</> information structure (described below) and calls
|
2000-05-29 03:59:17 +02:00
|
|
|
the trigger function to handle the event.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2002-04-19 18:36:08 +02:00
|
|
|
The trigger function must be defined before the trigger itself can be
|
|
|
|
created. The trigger function must be declared as a
|
2002-08-22 02:01:51 +02:00
|
|
|
function taking no arguments and returning type <literal>trigger</>.
|
2002-09-21 20:32:54 +02:00
|
|
|
(The trigger function receives its input through a <structname>TriggerData</>
|
2002-04-19 18:36:08 +02:00
|
|
|
structure, not in the form of ordinary function arguments.)
|
|
|
|
If the function is written in C, it must use the <quote>version 1</>
|
|
|
|
function manager interface.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2002-04-19 18:36:08 +02:00
|
|
|
The syntax for creating triggers is:
|
2000-05-02 22:02:03 +02:00
|
|
|
|
2002-09-21 20:32:54 +02:00
|
|
|
<programlisting>
|
2000-05-02 22:02:03 +02:00
|
|
|
CREATE TRIGGER <replaceable>trigger</replaceable> [ BEFORE | AFTER ] [ INSERT | DELETE | UPDATE [ OR ... ] ]
|
|
|
|
ON <replaceable>relation</replaceable> FOR EACH [ ROW | STATEMENT ]
|
|
|
|
EXECUTE PROCEDURE <replaceable>procedure</replaceable>
|
|
|
|
(<replaceable>args</replaceable>);
|
2002-09-21 20:32:54 +02:00
|
|
|
</programlisting>
|
2000-05-02 22:02:03 +02:00
|
|
|
|
|
|
|
where the arguments are:
|
|
|
|
|
|
|
|
<variablelist>
|
|
|
|
<varlistentry>
|
|
|
|
<term>
|
|
|
|
<replaceable>trigger</replaceable>
|
|
|
|
</term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2002-04-19 18:36:08 +02:00
|
|
|
The trigger must have a name distinct from all other triggers on
|
|
|
|
the same table. The name is needed
|
|
|
|
if you ever have to delete the trigger.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term>BEFORE</term>
|
|
|
|
<term>AFTER</term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Determines whether the function is called before or after
|
|
|
|
the event.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term>INSERT</term>
|
|
|
|
<term>DELETE</term>
|
|
|
|
<term>UPDATE</term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2002-04-19 18:36:08 +02:00
|
|
|
The next element of the command determines what event(s) will trigger
|
2000-05-02 22:02:03 +02:00
|
|
|
the function. Multiple events can be specified separated by OR.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term><replaceable>relation</replaceable></term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2002-04-19 18:36:08 +02:00
|
|
|
The relation name indicates which table the event applies to.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term>ROW</term>
|
|
|
|
<term>STATEMENT</term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
The FOR EACH clause determines whether the trigger is fired for each
|
|
|
|
affected row or before (or after) the entire statement has completed.
|
2002-04-19 18:36:08 +02:00
|
|
|
Currently only the ROW case is supported.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term><replaceable>procedure</replaceable></term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2002-04-19 18:36:08 +02:00
|
|
|
The procedure name is the function to be called.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term><replaceable>args</replaceable></term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2002-09-21 20:32:54 +02:00
|
|
|
The arguments passed to the function in the <structname>TriggerData</> structure.
|
2002-04-19 18:36:08 +02:00
|
|
|
This is either empty or a list of one or more simple literal
|
|
|
|
constants (which will be passed to the function as strings).
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2002-04-19 18:36:08 +02:00
|
|
|
The purpose of including arguments in the trigger definition
|
|
|
|
is to allow different
|
|
|
|
triggers with similar requirements to call the same function.
|
|
|
|
As an example, there could be a generalized trigger
|
|
|
|
function that takes as its arguments two field names and puts the
|
2002-09-21 20:32:54 +02:00
|
|
|
current user in one and the current time stamp in the other.
|
2002-04-19 18:36:08 +02:00
|
|
|
Properly written, this trigger function would be independent of
|
|
|
|
the specific table it is triggering on. So the same function
|
|
|
|
could be used for INSERT events on any table with suitable fields,
|
|
|
|
to automatically track creation of records in a transaction table for
|
|
|
|
example. It could also be used to track last-update events if
|
|
|
|
defined as an UPDATE trigger.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
</variablelist>
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2002-09-21 20:32:54 +02:00
|
|
|
Trigger functions return a <structname>HeapTuple</> to the calling executor. The return
|
2002-04-19 18:36:08 +02:00
|
|
|
value is ignored for triggers fired AFTER an operation,
|
2000-05-02 22:02:03 +02:00
|
|
|
but it allows BEFORE triggers to:
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2002-09-21 20:32:54 +02:00
|
|
|
Return a <symbol>NULL</> pointer to skip the operation for the
|
|
|
|
current tuple (and so the tuple will not be
|
|
|
|
inserted/updated/deleted).
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2002-04-19 18:36:08 +02:00
|
|
|
For INSERT and UPDATE triggers only, the returned tuple becomes the
|
|
|
|
tuple which will be inserted or will replace the tuple being updated.
|
|
|
|
This allows the trigger function to modify the row being inserted or
|
|
|
|
updated.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
2002-04-19 18:36:08 +02:00
|
|
|
|
|
|
|
A BEFORE trigger that does not intend to cause either of these behaviors
|
|
|
|
must be careful to return the same NEW tuple it is passed.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Note that there is no initialization performed by the CREATE TRIGGER
|
2002-04-19 18:36:08 +02:00
|
|
|
handler. This may be changed in the future.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2002-09-21 20:32:54 +02:00
|
|
|
If more than one trigger is defined for the same event on the same
|
|
|
|
relation, the triggers will be fired in alphabetical order by
|
|
|
|
name. In the case of BEFORE triggers, the possibly-modified tuple
|
|
|
|
returned by each trigger becomes the input to the next trigger.
|
|
|
|
If any BEFORE trigger returns <symbol>NULL</>, the operation is
|
|
|
|
abandoned and subsequent triggers are not fired.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2002-04-19 18:36:08 +02:00
|
|
|
If a trigger function executes SQL-queries (using SPI) then these queries
|
|
|
|
may fire triggers again. This is known as cascading triggers. There is no
|
|
|
|
direct limitation on the number of cascade levels. It is possible for
|
|
|
|
cascades to cause recursive invocation of the same trigger --- for
|
|
|
|
example, an INSERT trigger might execute a query that inserts an
|
|
|
|
additional tuple into the same table, causing the INSERT trigger to be
|
|
|
|
fired again. It is the trigger programmer's
|
|
|
|
responsibility to avoid infinite recursion in such scenarios.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
|
2000-09-29 22:21:34 +02:00
|
|
|
<sect1 id="trigger-manager">
|
2000-05-02 22:02:03 +02:00
|
|
|
<title>Interaction with the Trigger Manager</title>
|
|
|
|
|
|
|
|
<para>
|
2000-05-29 03:59:17 +02:00
|
|
|
This section describes the low-level details of the interface to a
|
|
|
|
trigger function. This information is only needed when writing a
|
|
|
|
trigger function in C. If you are using a higher-level function
|
|
|
|
language then these details are handled for you.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<note>
|
|
|
|
<para>
|
|
|
|
The interface described here applies for
|
2001-11-21 07:09:45 +01:00
|
|
|
<productname>PostgreSQL</productname> 7.1 and later.
|
2002-09-21 20:32:54 +02:00
|
|
|
Earlier versions passed the <structname>TriggerData</> pointer in a global
|
|
|
|
variable <varname>CurrentTriggerData</>.
|
2000-05-29 03:59:17 +02:00
|
|
|
</para>
|
|
|
|
</note>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
When a function is called by the trigger manager, it is not passed any
|
2001-09-13 17:55:24 +02:00
|
|
|
normal parameters, but it is passed a <quote>context</> pointer pointing to a
|
2002-09-21 20:32:54 +02:00
|
|
|
<structname>TriggerData</> structure. C functions can check whether they were called
|
2000-05-29 03:59:17 +02:00
|
|
|
from the trigger manager or not by executing the macro
|
|
|
|
<literal>CALLED_AS_TRIGGER(fcinfo)</literal>, which expands to
|
2002-09-21 20:32:54 +02:00
|
|
|
<programlisting>
|
|
|
|
((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))
|
|
|
|
</programlisting>
|
|
|
|
If this returns true, then it is safe to cast <literal>fcinfo->context</> to type
|
2000-05-29 03:59:17 +02:00
|
|
|
<literal>TriggerData *</literal> and make use of the pointed-to
|
2002-09-21 20:32:54 +02:00
|
|
|
<structname>TriggerData</> structure.
|
|
|
|
The function must <emphasis>not</emphasis> alter the <structname>TriggerData</>
|
2000-05-29 03:59:17 +02:00
|
|
|
structure or any of the data it points to.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2002-03-22 20:20:45 +01:00
|
|
|
<structname>struct TriggerData</structname> is defined in
|
|
|
|
<filename>commands/trigger.h</filename>:
|
2000-05-02 22:02:03 +02:00
|
|
|
|
2002-03-22 20:20:45 +01:00
|
|
|
<programlisting>
|
1998-03-01 09:16:16 +01:00
|
|
|
typedef struct TriggerData
|
|
|
|
{
|
2000-05-29 03:59:17 +02:00
|
|
|
NodeTag type;
|
2000-05-02 22:02:03 +02:00
|
|
|
TriggerEvent tg_event;
|
|
|
|
Relation tg_relation;
|
|
|
|
HeapTuple tg_trigtuple;
|
|
|
|
HeapTuple tg_newtuple;
|
|
|
|
Trigger *tg_trigger;
|
1998-03-01 09:16:16 +01:00
|
|
|
} TriggerData;
|
2002-03-22 20:20:45 +01:00
|
|
|
</programlisting>
|
2000-05-02 22:02:03 +02:00
|
|
|
|
|
|
|
where the members are defined as follows:
|
|
|
|
|
|
|
|
<variablelist>
|
2000-05-29 03:59:17 +02:00
|
|
|
<varlistentry>
|
2002-09-21 20:32:54 +02:00
|
|
|
<term><structfield>type</></term>
|
2000-05-29 03:59:17 +02:00
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Always <literal>T_TriggerData</literal> if this is a trigger event.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
2000-05-02 22:02:03 +02:00
|
|
|
<varlistentry>
|
2002-09-21 20:32:54 +02:00
|
|
|
<term><structfield>tg_event</></term>
|
2000-05-02 22:02:03 +02:00
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
describes the event for which the function is called. You may use the
|
|
|
|
following macros to examine <literal>tg_event</literal>:
|
|
|
|
|
|
|
|
<variablelist>
|
|
|
|
<varlistentry>
|
|
|
|
<term>TRIGGER_FIRED_BEFORE(tg_event)</term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
returns TRUE if trigger fired BEFORE.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term>TRIGGER_FIRED_AFTER(tg_event)</term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Returns TRUE if trigger fired AFTER.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term>TRIGGER_FIRED_FOR_ROW(event)</term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Returns TRUE if trigger fired for
|
|
|
|
a ROW-level event.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term>TRIGGER_FIRED_FOR_STATEMENT(event)</term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Returns TRUE if trigger fired for
|
|
|
|
STATEMENT-level event.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term>TRIGGER_FIRED_BY_INSERT(event)</term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Returns TRUE if trigger fired by INSERT.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term>TRIGGER_FIRED_BY_DELETE(event)</term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Returns TRUE if trigger fired by DELETE.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term>TRIGGER_FIRED_BY_UPDATE(event)</term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Returns TRUE if trigger fired by UPDATE.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
</variablelist>
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
2002-09-21 20:32:54 +02:00
|
|
|
<term><structfield>tg_relation</></term>
|
2000-05-02 22:02:03 +02:00
|
|
|
<listitem>
|
|
|
|
<para>
|
2002-09-21 20:32:54 +02:00
|
|
|
is a pointer to structure describing the triggered
|
|
|
|
relation. Look at <filename>utils/rel.h</> for details about
|
|
|
|
this structure. The most interesting things are
|
|
|
|
<literal>tg_relation->rd_att</> (descriptor of the relation
|
|
|
|
tuples) and <literal>tg_relation->rd_rel->relname</>
|
|
|
|
(relation's name. This is not <type>char*</>, but
|
|
|
|
<type>NameData</>. Use
|
|
|
|
<literal>SPI_getrelname(tg_relation)</> to get <type>char*</> if you
|
|
|
|
need a copy of the name).
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
2002-09-21 20:32:54 +02:00
|
|
|
<term><structfield>tg_trigtuple</></term>
|
2000-05-02 22:02:03 +02:00
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
is a pointer to the tuple for which the trigger is fired. This is the tuple
|
|
|
|
being inserted (if INSERT), deleted (if DELETE) or updated (if UPDATE).
|
|
|
|
If INSERT/DELETE then this is what you are to return to Executor if
|
|
|
|
you don't want to replace tuple with another one (INSERT) or skip the
|
|
|
|
operation.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
2002-09-21 20:32:54 +02:00
|
|
|
<term><structfield>tg_newtuple</></term>
|
2000-05-02 22:02:03 +02:00
|
|
|
<listitem>
|
|
|
|
<para>
|
2002-09-21 20:32:54 +02:00
|
|
|
is a pointer to the new version of tuple if UPDATE and <symbol>NULL</> if this is
|
2000-05-02 22:02:03 +02:00
|
|
|
for an INSERT or a DELETE. This is what you are to return to Executor if
|
|
|
|
UPDATE and you don't want to replace this tuple with another one or skip
|
|
|
|
the operation.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
2002-09-21 20:32:54 +02:00
|
|
|
<term><structfield>tg_trigger</></term>
|
2000-05-02 22:02:03 +02:00
|
|
|
<listitem>
|
|
|
|
<para>
|
2002-09-21 20:32:54 +02:00
|
|
|
is pointer to structure <structname>Trigger</> defined in <filename>utils/rel.h</>:
|
2000-05-02 22:02:03 +02:00
|
|
|
|
2002-09-21 20:32:54 +02:00
|
|
|
<programlisting>
|
1998-03-01 09:16:16 +01:00
|
|
|
typedef struct Trigger
|
|
|
|
{
|
2000-01-11 06:37:11 +01:00
|
|
|
Oid tgoid;
|
|
|
|
char *tgname;
|
|
|
|
Oid tgfoid;
|
|
|
|
int16 tgtype;
|
|
|
|
bool tgenabled;
|
|
|
|
bool tgisconstraint;
|
2002-04-02 00:36:13 +02:00
|
|
|
Oid tgconstrrelid;
|
2000-01-11 06:37:11 +01:00
|
|
|
bool tgdeferrable;
|
|
|
|
bool tginitdeferred;
|
|
|
|
int16 tgnargs;
|
|
|
|
int16 tgattr[FUNC_MAX_ARGS];
|
|
|
|
char **tgargs;
|
1998-03-01 09:16:16 +01:00
|
|
|
} Trigger;
|
2002-09-21 20:32:54 +02:00
|
|
|
</programlisting>
|
2000-05-02 22:02:03 +02:00
|
|
|
|
2002-09-21 20:32:54 +02:00
|
|
|
where <structfield>tgname</> is the trigger's name,
|
|
|
|
<structfield>tgnargs</> is number of arguments in
|
|
|
|
<structfield>tgargs</>, <structfield>tgargs</> is an array of
|
|
|
|
pointers to the arguments specified in the CREATE TRIGGER
|
|
|
|
statement. Other members are for internal use only.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
</variablelist>
|
|
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
|
2000-09-29 22:21:34 +02:00
|
|
|
<sect1 id="trigger-datachanges">
|
2000-05-02 22:02:03 +02:00
|
|
|
<title>Visibility of Data Changes</title>
|
|
|
|
|
|
|
|
<para>
|
2001-11-21 07:09:45 +01:00
|
|
|
<productname>PostgreSQL</productname> data changes visibility rule: during a query execution, data
|
2000-05-02 22:02:03 +02:00
|
|
|
changes made by the query itself (via SQL-function, SPI-function, triggers)
|
|
|
|
are invisible to the query scan. For example, in query
|
|
|
|
|
2002-09-21 20:32:54 +02:00
|
|
|
<programlisting>
|
2000-05-02 22:02:03 +02:00
|
|
|
INSERT INTO a SELECT * FROM a;
|
2002-09-21 20:32:54 +02:00
|
|
|
</programlisting>
|
2000-05-02 22:02:03 +02:00
|
|
|
|
|
|
|
tuples inserted are invisible for SELECT scan. In effect, this
|
|
|
|
duplicates the database table within itself (subject to unique index
|
|
|
|
rules, of course) without recursing.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
But keep in mind this notice about visibility in the SPI documentation:
|
|
|
|
|
|
|
|
<blockquote>
|
|
|
|
<para>
|
2000-12-22 19:57:50 +01:00
|
|
|
Changes made by query Q are visible by queries that are started after
|
2000-05-02 22:02:03 +02:00
|
|
|
query Q, no matter whether they are started inside Q (during the
|
|
|
|
execution of Q) or after Q is done.
|
|
|
|
</para>
|
|
|
|
</blockquote>
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
This is true for triggers as well so, though a tuple being inserted
|
2002-09-21 20:32:54 +02:00
|
|
|
(<structfield>tg_trigtuple</>) is not visible to queries in a BEFORE trigger, this tuple
|
2000-05-02 22:02:03 +02:00
|
|
|
(just inserted) is visible to queries in an AFTER trigger, and to queries
|
|
|
|
in BEFORE/AFTER triggers fired after this!
|
|
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
|
2000-09-29 22:21:34 +02:00
|
|
|
<sect1 id="trigger-examples">
|
2000-05-02 22:02:03 +02:00
|
|
|
<title>Examples</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
There are more complex examples in
|
|
|
|
<filename>src/test/regress/regress.c</filename> and
|
|
|
|
in <filename>contrib/spi</filename>.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2002-09-21 20:32:54 +02:00
|
|
|
Here is a very simple example of trigger usage. Function <function>trigf</> reports
|
|
|
|
the number of tuples in the triggered relation <literal>ttest</> and skips the
|
|
|
|
operation if the query attempts to insert a null value into x (i.e - it acts as a
|
|
|
|
not-null constraint but doesn't abort the transaction).
|
2000-05-02 22:02:03 +02:00
|
|
|
|
2002-09-21 20:32:54 +02:00
|
|
|
<programlisting>
|
|
|
|
#include "executor/spi.h" /* this is what you need to work with SPI */
|
|
|
|
#include "commands/trigger.h" /* -"- and triggers */
|
1998-03-01 09:16:16 +01:00
|
|
|
|
2000-05-29 03:59:17 +02:00
|
|
|
extern Datum trigf(PG_FUNCTION_ARGS);
|
1998-03-01 09:16:16 +01:00
|
|
|
|
2000-11-20 21:36:57 +01:00
|
|
|
PG_FUNCTION_INFO_V1(trigf);
|
|
|
|
|
2000-05-29 03:59:17 +02:00
|
|
|
Datum
|
|
|
|
trigf(PG_FUNCTION_ARGS)
|
1998-03-01 09:16:16 +01:00
|
|
|
{
|
2002-09-21 20:32:54 +02:00
|
|
|
TriggerData *trigdata = (TriggerData *) fcinfo->context;
|
|
|
|
TupleDesc tupdesc;
|
|
|
|
HeapTuple rettuple;
|
|
|
|
char *when;
|
|
|
|
bool checknull = false;
|
|
|
|
bool isnull;
|
|
|
|
int ret, i;
|
|
|
|
|
|
|
|
/* Make sure trigdata is pointing at what I expect */
|
|
|
|
if (!CALLED_AS_TRIGGER(fcinfo))
|
|
|
|
elog(ERROR, "trigf: not fired by trigger manager");
|
|
|
|
|
|
|
|
/* tuple to return to Executor */
|
|
|
|
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
|
|
|
|
rettuple = trigdata->tg_newtuple;
|
|
|
|
else
|
|
|
|
rettuple = trigdata->tg_trigtuple;
|
|
|
|
|
|
|
|
/* check for null values */
|
|
|
|
if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)
|
|
|
|
&& TRIGGER_FIRED_BEFORE(trigdata->tg_event))
|
|
|
|
checknull = true;
|
|
|
|
|
|
|
|
if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
|
|
|
|
when = "before";
|
|
|
|
else
|
|
|
|
when = "after ";
|
|
|
|
|
|
|
|
tupdesc = trigdata->tg_relation->rd_att;
|
|
|
|
|
|
|
|
/* Connect to SPI manager */
|
|
|
|
if ((ret = SPI_connect()) < 0)
|
|
|
|
elog(INFO, "trigf (fired %s): SPI_connect returned %d", when, ret);
|
|
|
|
|
|
|
|
/* Get number of tuples in relation */
|
|
|
|
ret = SPI_exec("SELECT count(*) FROM ttest", 0);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
elog(NOTICE, "trigf (fired %s): SPI_exec returned %d", when, ret);
|
|
|
|
|
|
|
|
/* count(*) returns int8 as of PG 7.2, so be careful to convert */
|
|
|
|
i = (int) DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[0],
|
|
|
|
SPI_tuptable->tupdesc,
|
|
|
|
1,
|
|
|
|
&isnull));
|
|
|
|
|
|
|
|
elog (NOTICE, "trigf (fired %s): there are %d tuples in ttest", when, i);
|
|
|
|
|
|
|
|
SPI_finish();
|
|
|
|
|
|
|
|
if (checknull)
|
|
|
|
{
|
|
|
|
(void) SPI_getbinval(rettuple, tupdesc, 1, &isnull);
|
|
|
|
if (isnull)
|
|
|
|
rettuple = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return PointerGetDatum(rettuple);
|
1998-03-01 09:16:16 +01:00
|
|
|
}
|
2002-09-21 20:32:54 +02:00
|
|
|
</programlisting>
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
1998-03-01 09:16:16 +01:00
|
|
|
|
2000-05-02 22:02:03 +02:00
|
|
|
<para>
|
2000-08-25 01:59:38 +02:00
|
|
|
Now, compile and create the trigger function:
|
2000-05-02 22:02:03 +02:00
|
|
|
|
2002-09-21 20:32:54 +02:00
|
|
|
<programlisting>
|
2002-08-22 02:01:51 +02:00
|
|
|
CREATE FUNCTION trigf () RETURNS TRIGGER AS
|
2002-09-21 20:32:54 +02:00
|
|
|
'...path_to_so' LANGUAGE C;
|
2000-08-25 01:59:38 +02:00
|
|
|
|
2001-10-13 01:32:34 +02:00
|
|
|
CREATE TABLE ttest (x int4);
|
2002-09-21 20:32:54 +02:00
|
|
|
</programlisting>
|
1998-03-01 09:16:16 +01:00
|
|
|
|
2002-09-21 20:32:54 +02:00
|
|
|
<programlisting>
|
2001-10-13 01:32:34 +02:00
|
|
|
vac=> CREATE TRIGGER tbefore BEFORE INSERT OR UPDATE OR DELETE ON ttest
|
|
|
|
FOR EACH ROW EXECUTE PROCEDURE trigf();
|
1998-03-01 09:16:16 +01:00
|
|
|
CREATE
|
2001-10-13 01:32:34 +02:00
|
|
|
vac=> CREATE TRIGGER tafter AFTER INSERT OR UPDATE OR DELETE ON ttest
|
|
|
|
FOR EACH ROW EXECUTE PROCEDURE trigf();
|
1998-03-01 09:16:16 +01:00
|
|
|
CREATE
|
2001-10-13 01:32:34 +02:00
|
|
|
vac=> INSERT INTO ttest VALUES (NULL);
|
2002-03-06 07:44:33 +01:00
|
|
|
WARNING: trigf (fired before): there are 0 tuples in ttest
|
1998-03-01 09:16:16 +01:00
|
|
|
INSERT 0 0
|
|
|
|
|
|
|
|
-- Insertion skipped and AFTER trigger is not fired
|
|
|
|
|
2001-10-13 01:32:34 +02:00
|
|
|
vac=> SELECT * FROM ttest;
|
2002-09-21 20:32:54 +02:00
|
|
|
x
|
|
|
|
---
|
1998-03-01 09:16:16 +01:00
|
|
|
(0 rows)
|
|
|
|
|
2001-10-13 01:32:34 +02:00
|
|
|
vac=> INSERT INTO ttest VALUES (1);
|
2002-03-06 07:44:33 +01:00
|
|
|
INFO: trigf (fired before): there are 0 tuples in ttest
|
|
|
|
INFO: trigf (fired after ): there are 1 tuples in ttest
|
1998-03-01 09:16:16 +01:00
|
|
|
^^^^^^^^
|
|
|
|
remember what we said about visibility.
|
|
|
|
INSERT 167793 1
|
2001-10-13 01:32:34 +02:00
|
|
|
vac=> SELECT * FROM ttest;
|
2002-09-21 20:32:54 +02:00
|
|
|
x
|
|
|
|
---
|
|
|
|
1
|
1998-03-01 09:16:16 +01:00
|
|
|
(1 row)
|
|
|
|
|
2001-10-13 01:32:34 +02:00
|
|
|
vac=> INSERT INTO ttest SELECT x * 2 FROM ttest;
|
2002-03-06 07:44:33 +01:00
|
|
|
INFO: trigf (fired before): there are 1 tuples in ttest
|
|
|
|
INFO: trigf (fired after ): there are 2 tuples in ttest
|
1998-03-01 09:16:16 +01:00
|
|
|
^^^^^^^^
|
|
|
|
remember what we said about visibility.
|
|
|
|
INSERT 167794 1
|
2001-10-13 01:32:34 +02:00
|
|
|
vac=> SELECT * FROM ttest;
|
2002-09-21 20:32:54 +02:00
|
|
|
x
|
|
|
|
---
|
|
|
|
1
|
|
|
|
2
|
1998-03-01 09:16:16 +01:00
|
|
|
(2 rows)
|
|
|
|
|
2002-09-21 20:32:54 +02:00
|
|
|
vac=> UPDATE ttest SET x = NULL WHERE x = 2;
|
2002-03-06 07:44:33 +01:00
|
|
|
INFO: trigf (fired before): there are 2 tuples in ttest
|
1998-03-01 09:16:16 +01:00
|
|
|
UPDATE 0
|
2001-10-13 01:32:34 +02:00
|
|
|
vac=> UPDATE ttest SET x = 4 WHERE x = 2;
|
2002-03-06 07:44:33 +01:00
|
|
|
INFO: trigf (fired before): there are 2 tuples in ttest
|
|
|
|
INFO: trigf (fired after ): there are 2 tuples in ttest
|
1998-03-01 09:16:16 +01:00
|
|
|
UPDATE 1
|
2001-10-13 01:32:34 +02:00
|
|
|
vac=> SELECT * FROM ttest;
|
2002-09-21 20:32:54 +02:00
|
|
|
x
|
|
|
|
---
|
|
|
|
1
|
|
|
|
4
|
1998-03-01 09:16:16 +01:00
|
|
|
(2 rows)
|
|
|
|
|
2001-10-13 01:32:34 +02:00
|
|
|
vac=> DELETE FROM ttest;
|
2002-03-06 07:44:33 +01:00
|
|
|
INFO: trigf (fired before): there are 2 tuples in ttest
|
|
|
|
INFO: trigf (fired after ): there are 1 tuples in ttest
|
|
|
|
INFO: trigf (fired before): there are 1 tuples in ttest
|
|
|
|
INFO: trigf (fired after ): there are 0 tuples in ttest
|
1998-03-01 09:16:16 +01:00
|
|
|
^^^^^^^^
|
|
|
|
remember what we said about visibility.
|
|
|
|
DELETE 2
|
2001-10-13 01:32:34 +02:00
|
|
|
vac=> SELECT * FROM ttest;
|
2002-09-21 20:32:54 +02:00
|
|
|
x
|
|
|
|
---
|
1998-03-01 09:16:16 +01:00
|
|
|
(0 rows)
|
2002-09-21 20:32:54 +02:00
|
|
|
</programlisting>
|
2000-05-02 22:02:03 +02:00
|
|
|
|
|
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
</chapter>
|
|
|
|
|
|
|
|
<!-- Keep this comment at the end of the file
|
|
|
|
Local variables:
|
|
|
|
mode:sgml
|
|
|
|
sgml-omittag:nil
|
|
|
|
sgml-shorttag:t
|
|
|
|
sgml-minimize-attributes:nil
|
|
|
|
sgml-always-quote-attributes:t
|
|
|
|
sgml-indent-step:1
|
|
|
|
sgml-indent-data:t
|
|
|
|
sgml-parent-document:nil
|
|
|
|
sgml-default-dtd-file:"./reference.ced"
|
|
|
|
sgml-exposed-tags:nil
|
|
|
|
sgml-local-catalogs:("/usr/lib/sgml/catalog")
|
|
|
|
sgml-local-ecat-files:nil
|
|
|
|
End:
|
|
|
|
-->
|