2001-12-04 03:07:11 +01:00
|
|
|
<!--
|
2003-04-11 20:41:20 +02:00
|
|
|
$Header: /cvsroot/pgsql/doc/src/sgml/trigger.sgml,v 1.28 2003/04/11 18:41:20 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>
|
2003-04-11 20:41:20 +02:00
|
|
|
This chapter describes how to write trigger functions. In
|
|
|
|
particular, it describes the C-language interface for trigger
|
|
|
|
functions. The trigger interfaces in most procedural languages
|
|
|
|
work analogously. (Trigger functions cannot be written in SQL.)
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
A trigger function can execute before or after a
|
|
|
|
<command>INSERT</command>, <command>UPDATE</command>, or
|
|
|
|
<command>DELETE</command>, either once per modified row, or once
|
|
|
|
per <acronym>SQL</acronym> statement.
|
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>
|
2003-04-11 20:41:20 +02:00
|
|
|
If a trigger event occurs, the trigger manager is called by the
|
|
|
|
executor. It sets up an information structure of type
|
|
|
|
<structname>TriggerData</> (described below) and calls 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>
|
2003-03-25 17:15:44 +01:00
|
|
|
The syntax for creating triggers is described in <xref linkend="reference">.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2003-04-11 20:41:20 +02:00
|
|
|
Trigger functions return a value of type <structname>HeapTuple</>,
|
|
|
|
which represents a table row, to the calling executor. The return
|
|
|
|
value is ignored for triggers fired after an operation, but a
|
|
|
|
triggers fired before an operation has the following choices:
|
2000-05-02 22:02:03 +02:00
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2003-04-11 20:41:20 +02:00
|
|
|
It can return a <symbol>NULL</> pointer to skip the operation
|
|
|
|
for the current row (and so the row will not be
|
2002-09-21 20:32:54 +02:00
|
|
|
inserted/updated/deleted).
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2002-11-23 04:59:09 +01:00
|
|
|
For <command>INSERT</command> and <command>UPDATE</command>
|
2003-04-11 20:41:20 +02:00
|
|
|
triggers only, the returned row becomes the row that will
|
|
|
|
be inserted or will replace the row being updated. This
|
2002-11-23 04:59:09 +01:00
|
|
|
allows the trigger function to modify the row being inserted or
|
2002-04-19 18:36:08 +02:00
|
|
|
updated.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
2002-04-19 18:36:08 +02:00
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
A before trigger that does not intend to cause either of these
|
|
|
|
behaviors must be careful to return the same row that was passed
|
|
|
|
in as the new row (see below).
|
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
|
2003-04-11 20:41:20 +02:00
|
|
|
name. In the case of before triggers, the possibly-modified row
|
2002-09-21 20:32:54 +02:00
|
|
|
returned by each trigger becomes the input to the next trigger.
|
2003-04-11 20:41:20 +02:00
|
|
|
If any before trigger returns a <symbol>NULL</> pointer, the
|
|
|
|
operation is abandoned and subsequent triggers are not fired.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2003-04-11 20:41:20 +02:00
|
|
|
If a trigger function executes SQL commands (using SPI) then these
|
|
|
|
commands may fire triggers again. This is known as cascading
|
2002-11-23 04:59:09 +01:00
|
|
|
triggers. There is no direct limitation on the number of cascade
|
2003-04-11 20:41:20 +02:00
|
|
|
levels. It is possible for cascades to cause a recursive invocation
|
|
|
|
of the same trigger; for example, an <command>INSERT</command>
|
|
|
|
trigger might execute a command that inserts an additional row
|
2002-11-23 04:59:09 +01:00
|
|
|
into the same table, causing the <command>INSERT</command> 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>
|
2002-11-23 04:59:09 +01:00
|
|
|
|
|
|
|
<para>
|
2003-04-11 20:41:20 +02:00
|
|
|
When a trigger is being defined, arguments can be specified for
|
|
|
|
it. 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 column names and
|
|
|
|
puts the current user in one and the current time stamp in the
|
|
|
|
other. Properly written, this trigger function would be
|
|
|
|
independent of the specific table it is triggering on. So the
|
|
|
|
same function could be used for <command>INSERT</command> events
|
|
|
|
on any table with suitable columns, 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
|
|
|
|
<command>UPDATE</command> trigger.
|
2002-11-23 04:59:09 +01:00
|
|
|
</para>
|
|
|
|
|
2000-05-02 22:02:03 +02:00
|
|
|
</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
|
2003-04-11 20:41:20 +02:00
|
|
|
trigger function in C. If you are using a higher-level
|
2000-05-29 03:59:17 +02:00
|
|
|
language then these details are handled for you.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2002-11-23 04:59:09 +01:00
|
|
|
When a function is called by the trigger manager, it is not passed
|
2003-04-11 20:41:20 +02:00
|
|
|
any normal arguments, but it is passed a <quote>context</>
|
2002-11-23 04:59:09 +01:00
|
|
|
pointer pointing to a <structname>TriggerData</> structure. C
|
|
|
|
functions can check whether they were called from the trigger
|
|
|
|
manager or not by executing the macro
|
2003-04-11 20:41:20 +02:00
|
|
|
<programlisting>
|
|
|
|
CALLED_AS_TRIGGER(fcinfo)
|
|
|
|
</programlisting>
|
|
|
|
which expands to
|
2002-09-21 20:32:54 +02:00
|
|
|
<programlisting>
|
|
|
|
((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))
|
|
|
|
</programlisting>
|
2002-11-23 04:59:09 +01:00
|
|
|
If this returns true, then it is safe to cast
|
|
|
|
<literal>fcinfo->context</> to type <literal>TriggerData
|
|
|
|
*</literal> and make use of the pointed-to
|
|
|
|
<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>
|
2003-04-11 20:41:20 +02:00
|
|
|
Always <literal>T_TriggerData</literal>.
|
2000-05-29 03:59:17 +02:00
|
|
|
</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>
|
2003-04-11 20:41:20 +02:00
|
|
|
Describes the event for which the function is called. You may use the
|
2000-05-02 22:02:03 +02:00
|
|
|
following macros to examine <literal>tg_event</literal>:
|
|
|
|
|
|
|
|
<variablelist>
|
|
|
|
<varlistentry>
|
2003-04-11 20:41:20 +02:00
|
|
|
<term><literal>TRIGGER_FIRED_BEFORE(tg_event)</literal></term>
|
2000-05-02 22:02:03 +02:00
|
|
|
<listitem>
|
|
|
|
<para>
|
2003-04-11 20:41:20 +02:00
|
|
|
Returns true if the trigger fired before the operation.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
2003-04-11 20:41:20 +02:00
|
|
|
<term><literal>TRIGGER_FIRED_AFTER(tg_event)</literal></term>
|
2000-05-02 22:02:03 +02:00
|
|
|
<listitem>
|
|
|
|
<para>
|
2003-04-11 20:41:20 +02:00
|
|
|
Returns true if the trigger fired after the operation.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
2003-04-11 20:41:20 +02:00
|
|
|
<term><literal>TRIGGER_FIRED_FOR_ROW(tg_event)</literal></term>
|
2000-05-02 22:02:03 +02:00
|
|
|
<listitem>
|
|
|
|
<para>
|
2003-04-11 20:41:20 +02:00
|
|
|
Returns true if the trigger fired for a row-level event.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
2003-04-11 20:41:20 +02:00
|
|
|
<term><literal>TRIGGER_FIRED_FOR_STATEMENT(tg_event)</literal></term>
|
2000-05-02 22:02:03 +02:00
|
|
|
<listitem>
|
|
|
|
<para>
|
2003-04-11 20:41:20 +02:00
|
|
|
Returns true if the trigger fired for a statement-level event.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
2003-04-11 20:41:20 +02:00
|
|
|
<term><literal>TRIGGER_FIRED_BY_INSERT(tg_event)</literal></term>
|
2000-05-02 22:02:03 +02:00
|
|
|
<listitem>
|
|
|
|
<para>
|
2003-04-11 20:41:20 +02:00
|
|
|
Returns true if the trigger was fired by an <command>INSERT</command> command.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
2003-04-11 20:41:20 +02:00
|
|
|
<term><literal>TRIGGER_FIRED_BY_UPDATE(tg_event)</literal></term>
|
2000-05-02 22:02:03 +02:00
|
|
|
<listitem>
|
|
|
|
<para>
|
2003-04-11 20:41:20 +02:00
|
|
|
Returns true if the trigger was fired by an <command>UPDATE</command> command.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
2003-04-11 20:41:20 +02:00
|
|
|
<term><literal>TRIGGER_FIRED_BY_DELETE(tg_event)</literal></term>
|
2000-05-02 22:02:03 +02:00
|
|
|
<listitem>
|
|
|
|
<para>
|
2003-04-11 20:41:20 +02:00
|
|
|
Returns true if the trigger was fired by a <command>DELETE</command> command.
|
2000-05-02 22:02:03 +02:00
|
|
|
</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>
|
2003-04-11 20:41:20 +02:00
|
|
|
A pointer to a structure describing the relation that the trigger fired for.
|
|
|
|
Look at <filename>utils/rel.h</> for details about
|
2002-09-21 20:32:54 +02:00
|
|
|
this structure. The most interesting things are
|
|
|
|
<literal>tg_relation->rd_att</> (descriptor of the relation
|
|
|
|
tuples) and <literal>tg_relation->rd_rel->relname</>
|
2003-04-11 20:41:20 +02:00
|
|
|
(relation name; the type is not <type>char*</> but
|
|
|
|
<type>NameData</>; use
|
|
|
|
<literal>SPI_getrelname(tg_relation)</> to get a <type>char*</> if you
|
2002-09-21 20:32:54 +02:00
|
|
|
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>
|
2003-04-11 20:41:20 +02:00
|
|
|
A pointer to the row for which the trigger was fired. This is
|
|
|
|
the row being inserted, updated, or deleted. If this trigger
|
|
|
|
was fired for an <command>INSERT</command> or
|
|
|
|
<command>DELETE</command> then this is what you should return
|
|
|
|
to from the function if you don't want to replace the row with
|
|
|
|
a different one (in the case of <command>INSERT</command>) or
|
|
|
|
skip the operation.
|
2000-05-02 22:02:03 +02:00
|
|
|
</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>
|
2003-04-11 20:41:20 +02:00
|
|
|
A pointer to the new version of the row, if the trigger was
|
|
|
|
fired for an <command>UPDATE</command>, and <symbol>NULL</> if
|
|
|
|
it is for an <command>INSERT</command> or a
|
|
|
|
<command>DELETE</command>. This is what you have to return
|
|
|
|
from the function if the event is an <command>UPDATE</command>
|
|
|
|
and you don't want to replace this row by a different one or
|
|
|
|
skip the operation.
|
2000-05-02 22:02:03 +02:00
|
|
|
</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>
|
2003-04-11 20:41:20 +02:00
|
|
|
A pointer to a structure of type <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
|
2003-04-11 20:41:20 +02:00
|
|
|
<structfield>tgargs</>, and <structfield>tgargs</> is an array of
|
2002-11-23 04:59:09 +01:00
|
|
|
pointers to the arguments specified in the <command>CREATE
|
2003-04-11 20:41:20 +02:00
|
|
|
TRIGGER</command> statement. The other members are for internal use
|
2002-11-23 04:59:09 +01:00
|
|
|
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>
|
2003-04-11 20:41:20 +02:00
|
|
|
If you are using the SPI interface to execute SQL commands in your
|
|
|
|
trigger functions written in C (or you are using a different
|
|
|
|
language and execute SQL commands in some way, which internally
|
|
|
|
goes through SPI as well), be sure to read <xref
|
|
|
|
linkend="spi-visibility"> so that you know which data is visible
|
|
|
|
at which point during the execution of a trigger. For triggers,
|
|
|
|
the most important consequences of the data visibility rules are:
|
2000-05-02 22:02:03 +02:00
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
The row being inserted (<structfield>tg_trigtuple</>) is
|
|
|
|
<emphasis>not</emphasis> visible to SQL commands executed in a
|
|
|
|
before trigger.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
2000-05-02 22:02:03 +02:00
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
The row being inserted (<structfield>tg_trigtuple</>)
|
|
|
|
<emphasis>is</emphasis> visible to SQL commands executed in an
|
|
|
|
after trigger (because it was just inserted).
|
|
|
|
</para>
|
|
|
|
</listitem>
|
2000-05-02 22:02:03 +02:00
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
A just-inserted row is visible to all SQL commands executed
|
|
|
|
within any trigger that is fired later in the execution of the
|
|
|
|
outer command (e.g., for the next row).
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2003-04-11 20:41:20 +02:00
|
|
|
The next section contains a demonstration of these rules applied.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
<sect1 id="trigger-example">
|
|
|
|
<title>A Complete Example</title>
|
2000-05-02 22:02:03 +02:00
|
|
|
|
|
|
|
<para>
|
2003-04-11 20:41:20 +02:00
|
|
|
Here is a very simple example of a trigger function written in C.
|
|
|
|
The function <function>trigf</> reports the number of rows in the
|
|
|
|
table <literal>ttest</> and skips the actual operation if the
|
|
|
|
command attempts to insert a null value into the column
|
|
|
|
<literal>x</>. (So the trigger acts as a not-null constraint but
|
|
|
|
doesn't abort the transaction.)
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2003-04-11 20:41:20 +02:00
|
|
|
First, the table definition:
|
|
|
|
<programlisting>
|
|
|
|
CREATE TABLE ttest (
|
|
|
|
x integer
|
|
|
|
);
|
|
|
|
</programlisting>
|
|
|
|
</para>
|
2000-05-02 22:02:03 +02:00
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
<para>
|
|
|
|
This is the source code of the trigger function:
|
2002-09-21 20:32:54 +02:00
|
|
|
<programlisting>
|
2003-04-11 20:41:20 +02:00
|
|
|
#include "postgres.h"
|
2002-09-21 20:32:54 +02:00
|
|
|
#include "executor/spi.h" /* this is what you need to work with SPI */
|
2003-04-11 20:41:20 +02:00
|
|
|
#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;
|
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
/* make sure it's called as a trigger at all */
|
2002-09-21 20:32:54 +02:00
|
|
|
if (!CALLED_AS_TRIGGER(fcinfo))
|
2003-04-11 20:41:20 +02:00
|
|
|
elog(ERROR, "trigf: not called by trigger manager");
|
2002-09-21 20:32:54 +02:00
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
/* tuple to return to executor */
|
2002-09-21 20:32:54 +02:00
|
|
|
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;
|
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
/* connect to SPI manager */
|
2002-09-21 20:32:54 +02:00
|
|
|
if ((ret = SPI_connect()) < 0)
|
|
|
|
elog(INFO, "trigf (fired %s): SPI_connect returned %d", when, ret);
|
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
/* get number of rows in table */
|
2002-09-21 20:32:54 +02:00
|
|
|
ret = SPI_exec("SELECT count(*) FROM ttest", 0);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
elog(NOTICE, "trigf (fired %s): SPI_exec returned %d", when, ret);
|
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
/* count(*) returns int8, so be careful to convert */
|
|
|
|
i = DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[0],
|
|
|
|
SPI_tuptable->tupdesc,
|
|
|
|
1,
|
|
|
|
&isnull));
|
2002-09-21 20:32:54 +02:00
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
elog (INFO, "trigf (fired %s): there are %d rows in ttest", when, i);
|
2002-09-21 20:32:54 +02:00
|
|
|
|
|
|
|
SPI_finish();
|
|
|
|
|
|
|
|
if (checknull)
|
|
|
|
{
|
2003-04-11 20:41:20 +02:00
|
|
|
SPI_getbinval(rettuple, tupdesc, 1, &isnull);
|
2002-09-21 20:32:54 +02:00
|
|
|
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>
|
2003-04-11 20:41:20 +02:00
|
|
|
After you have compiled the source code, declare the function and
|
|
|
|
the triggers:
|
2002-09-21 20:32:54 +02:00
|
|
|
<programlisting>
|
2003-04-11 20:41:20 +02:00
|
|
|
CREATE FUNCTION trigf() RETURNS trigger
|
|
|
|
AS '<replaceable>filename</>'
|
|
|
|
LANGUAGE C;
|
|
|
|
|
|
|
|
CREATE TRIGGER tbefore BEFORE INSERT OR UPDATE OR DELETE ON ttest
|
|
|
|
FOR EACH ROW EXECUTE PROCEDURE trigf();
|
2000-08-25 01:59:38 +02:00
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
CREATE TRIGGER tafter AFTER INSERT OR UPDATE OR DELETE ON ttest
|
|
|
|
FOR EACH ROW EXECUTE PROCEDURE trigf();
|
2002-09-21 20:32:54 +02:00
|
|
|
</programlisting>
|
2003-04-11 20:41:20 +02:00
|
|
|
</para>
|
1998-03-01 09:16:16 +01:00
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
<para>
|
|
|
|
Now you can test the operation of the trigger:
|
|
|
|
<screen>
|
|
|
|
=> INSERT INTO ttest VALUES (NULL);
|
|
|
|
INFO: trigf (fired before): there are 0 rows in ttest
|
1998-03-01 09:16:16 +01:00
|
|
|
INSERT 0 0
|
|
|
|
|
|
|
|
-- Insertion skipped and AFTER trigger is not fired
|
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
=> SELECT * FROM ttest;
|
2002-09-21 20:32:54 +02:00
|
|
|
x
|
|
|
|
---
|
1998-03-01 09:16:16 +01:00
|
|
|
(0 rows)
|
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
=> INSERT INTO ttest VALUES (1);
|
|
|
|
INFO: trigf (fired before): there are 0 rows in ttest
|
|
|
|
INFO: trigf (fired after ): there are 1 rows 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)
|
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
=> INSERT INTO ttest SELECT x * 2 FROM ttest;
|
|
|
|
INFO: trigf (fired before): there are 1 rows in ttest
|
|
|
|
INFO: trigf (fired after ): there are 2 rows in ttest
|
|
|
|
^^^^^^
|
1998-03-01 09:16:16 +01:00
|
|
|
remember what we said about visibility.
|
|
|
|
INSERT 167794 1
|
2003-04-11 20:41:20 +02:00
|
|
|
=> SELECT * FROM ttest;
|
2002-09-21 20:32:54 +02:00
|
|
|
x
|
|
|
|
---
|
|
|
|
1
|
|
|
|
2
|
1998-03-01 09:16:16 +01:00
|
|
|
(2 rows)
|
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
=> UPDATE ttest SET x = NULL WHERE x = 2;
|
|
|
|
INFO: trigf (fired before): there are 2 rows in ttest
|
1998-03-01 09:16:16 +01:00
|
|
|
UPDATE 0
|
2003-04-11 20:41:20 +02:00
|
|
|
=> UPDATE ttest SET x = 4 WHERE x = 2;
|
|
|
|
INFO: trigf (fired before): there are 2 rows in ttest
|
|
|
|
INFO: trigf (fired after ): there are 2 rows 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)
|
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
=> DELETE FROM ttest;
|
|
|
|
INFO: trigf (fired before): there are 2 rows in ttest
|
|
|
|
INFO: trigf (fired after ): there are 1 rows in ttest
|
|
|
|
INFO: trigf (fired before): there are 1 rows in ttest
|
|
|
|
INFO: trigf (fired after ): there are 0 rows in ttest
|
|
|
|
^^^^^^
|
1998-03-01 09:16:16 +01:00
|
|
|
remember what we said about visibility.
|
|
|
|
DELETE 2
|
2003-04-11 20:41:20 +02:00
|
|
|
=> SELECT * FROM ttest;
|
2002-09-21 20:32:54 +02:00
|
|
|
x
|
|
|
|
---
|
1998-03-01 09:16:16 +01:00
|
|
|
(0 rows)
|
2003-04-11 20:41:20 +02:00
|
|
|
</screen>
|
2000-05-02 22:02:03 +02:00
|
|
|
|
|
|
|
</para>
|
2003-04-11 20:41:20 +02:00
|
|
|
|
|
|
|
<para>
|
|
|
|
There are more complex examples in
|
|
|
|
<filename>src/test/regress/regress.c</filename> and
|
|
|
|
in <filename>contrib/spi</filename>.
|
|
|
|
</para>
|
2000-05-02 22:02:03 +02:00
|
|
|
</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:
|
|
|
|
-->
|