2010-01-17 23:56:23 +01:00
|
|
|
<!-- $PostgreSQL: pgsql/doc/src/sgml/trigger.sgml,v 1.62 2010/01/17 22:56:21 tgl Exp $ -->
|
2001-12-04 03:07:11 +01:00
|
|
|
|
2000-05-02 22:02:03 +02:00
|
|
|
<chapter id="triggers">
|
|
|
|
<title>Triggers</title>
|
|
|
|
|
2003-08-31 19:32:24 +02:00
|
|
|
<indexterm zone="triggers">
|
|
|
|
<primary>trigger</primary>
|
|
|
|
</indexterm>
|
|
|
|
|
2000-05-02 22:02:03 +02:00
|
|
|
<para>
|
2005-10-13 23:09:38 +02:00
|
|
|
This chapter provides general information about writing trigger functions.
|
|
|
|
Trigger functions can be written in most of the available procedural
|
|
|
|
languages, including
|
|
|
|
<application>PL/pgSQL</application> (<xref linkend="plpgsql">),
|
|
|
|
<application>PL/Tcl</application> (<xref linkend="pltcl">),
|
|
|
|
<application>PL/Perl</application> (<xref linkend="plperl">), and
|
|
|
|
<application>PL/Python</application> (<xref linkend="plpython">).
|
|
|
|
After reading this chapter, you should consult the chapter for
|
|
|
|
your favorite procedural language to find out the language-specific
|
|
|
|
details of writing a trigger in it.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
It is also possible to write a trigger function in C, although
|
|
|
|
most people find it easier to use one of the procedural languages.
|
|
|
|
It is not currently possible to write a trigger function in the
|
|
|
|
plain SQL function language.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
|
2002-04-19 18:36:08 +02:00
|
|
|
<sect1 id="trigger-definition">
|
2003-10-23 00:28:10 +02:00
|
|
|
<title>Overview of Trigger Behavior</title>
|
2000-05-02 22:02:03 +02:00
|
|
|
|
|
|
|
<para>
|
2005-10-13 23:09:38 +02:00
|
|
|
A trigger is a specification that the database should automatically
|
|
|
|
execute a particular function whenever a certain type of operation is
|
|
|
|
performed. Triggers can be defined to execute either before or after any
|
2003-10-23 00:28:10 +02:00
|
|
|
<command>INSERT</command>, <command>UPDATE</command>, or
|
|
|
|
<command>DELETE</command> operation, either once per modified row,
|
2009-10-15 00:14:25 +02:00
|
|
|
or once per <acronym>SQL</acronym> statement.
|
|
|
|
<command>UPDATE</command> triggers can moreover be set to fire only if
|
|
|
|
certain columns are mentioned in the <literal>SET</literal> clause of the
|
|
|
|
<command>UPDATE</command> statement.
|
|
|
|
Triggers can also fire for <command>TRUNCATE</command> statements.
|
|
|
|
If a trigger event occurs, the trigger's function is called at the
|
|
|
|
appropriate time 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
|
2008-03-28 01:21:56 +01:00
|
|
|
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</>.
|
2003-10-23 00:28:10 +02:00
|
|
|
(The trigger function receives its input through a specially-passed
|
|
|
|
<structname>TriggerData</> structure, not in the form of ordinary function
|
|
|
|
arguments.)
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2003-10-23 00:28:10 +02:00
|
|
|
Once a suitable trigger function has been created, the trigger is
|
|
|
|
established with
|
2003-08-10 03:20:34 +02:00
|
|
|
<xref linkend="sql-createtrigger" endterm="sql-createtrigger-title">.
|
2003-10-23 00:28:10 +02:00
|
|
|
The same trigger function can be used for multiple triggers.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2005-10-13 23:09:38 +02:00
|
|
|
<productname>PostgreSQL</productname> offers both <firstterm>per-row</>
|
|
|
|
triggers and <firstterm>per-statement</> triggers. With a per-row
|
|
|
|
trigger, the trigger function
|
|
|
|
is invoked once for each row that is affected by the statement
|
2004-01-22 20:50:21 +01:00
|
|
|
that fired the trigger. In contrast, a per-statement trigger is
|
|
|
|
invoked only once when an appropriate statement is executed,
|
|
|
|
regardless of the number of rows affected by that statement. In
|
|
|
|
particular, a statement that affects zero rows will still result
|
|
|
|
in the execution of any applicable per-statement triggers. These
|
2005-10-13 23:09:38 +02:00
|
|
|
two types of triggers are sometimes called <firstterm>row-level</>
|
|
|
|
triggers and <firstterm>statement-level</> triggers,
|
2008-03-28 01:21:56 +01:00
|
|
|
respectively. Triggers on <command>TRUNCATE</command> may only be
|
|
|
|
defined at statement-level.
|
2004-01-22 20:50:21 +01:00
|
|
|
</para>
|
|
|
|
|
2004-12-30 04:13:56 +01:00
|
|
|
<para>
|
2005-10-13 23:09:38 +02:00
|
|
|
Triggers are also classified as <firstterm>before</> triggers and
|
|
|
|
<firstterm>after</> triggers.
|
|
|
|
Statement-level before triggers naturally fire before the
|
|
|
|
statement starts to do anything, while statement-level after
|
|
|
|
triggers fire at the very end of the statement. Row-level before
|
2004-12-30 04:13:56 +01:00
|
|
|
triggers fire immediately before a particular row is operated on,
|
2005-10-13 23:09:38 +02:00
|
|
|
while row-level after triggers fire at the end of the statement
|
|
|
|
(but before any statement-level after triggers).
|
2004-12-30 04:13:56 +01:00
|
|
|
</para>
|
|
|
|
|
2004-01-22 20:50:21 +01:00
|
|
|
<para>
|
|
|
|
Trigger functions invoked by per-statement triggers should always
|
|
|
|
return <symbol>NULL</symbol>. Trigger functions invoked by per-row
|
|
|
|
triggers can return a table row (a value of
|
|
|
|
type <structname>HeapTuple</structname>) to the calling executor,
|
|
|
|
if they choose. A row-level trigger fired before an operation has
|
|
|
|
the following choices:
|
2000-05-02 22:02:03 +02:00
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2004-01-22 20:50:21 +01:00
|
|
|
It can return <symbol>NULL</> to skip the operation for the
|
|
|
|
current row. This instructs the executor to not perform the
|
|
|
|
row-level operation that invoked the trigger (the insertion or
|
|
|
|
modification of a particular table row).
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2004-01-22 20:50:21 +01:00
|
|
|
For row-level <command>INSERT</command>
|
|
|
|
and <command>UPDATE</command> triggers only, the returned row
|
|
|
|
becomes the row that will be inserted or will replace the row
|
|
|
|
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
|
|
|
|
2004-01-22 20:50:21 +01:00
|
|
|
A row-level before trigger that does not intend to cause either of
|
|
|
|
these behaviors must be careful to return as its result the same
|
|
|
|
row that was passed in (that is, the <varname>NEW</varname> row
|
|
|
|
for <command>INSERT</command> and <command>UPDATE</command>
|
|
|
|
triggers, the <varname>OLD</varname> row for
|
2003-11-15 20:46:31 +01:00
|
|
|
<command>DELETE</command> triggers).
|
2003-10-23 00:28:10 +02:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2004-01-22 20:50:21 +01:00
|
|
|
The return value is ignored for row-level triggers fired after an
|
Update documentation on may/can/might:
Standard English uses "may", "can", and "might" in different ways:
may - permission, "You may borrow my rake."
can - ability, "I can lift that log."
might - possibility, "It might rain today."
Unfortunately, in conversational English, their use is often mixed, as
in, "You may use this variable to do X", when in fact, "can" is a better
choice. Similarly, "It may crash" is better stated, "It might crash".
Also update two error messages mentioned in the documenation to match.
2007-01-31 21:56:20 +01:00
|
|
|
operation, and so they can return <symbol>NULL</>.
|
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
|
2004-01-22 20:50:21 +01:00
|
|
|
relation, the triggers will be fired in alphabetical order by
|
|
|
|
trigger name. In the case of before triggers, the
|
|
|
|
possibly-modified row returned by each trigger becomes the input
|
|
|
|
to the next trigger. If any before trigger returns
|
2005-10-13 23:09:38 +02:00
|
|
|
<symbol>NULL</>, the operation is abandoned for that row and subsequent
|
2004-01-22 20:50:21 +01:00
|
|
|
triggers are not fired.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
|
2009-11-20 21:38:12 +01:00
|
|
|
<para>
|
|
|
|
A trigger definition can also specify a boolean <literal>WHEN</>
|
|
|
|
condition, which will be tested to see whether the trigger should
|
|
|
|
be fired. In row-level triggers the <literal>WHEN</> condition can
|
|
|
|
examine the old and/or new values of columns of the row. (Statement-level
|
|
|
|
triggers can also have <literal>WHEN</> conditions, although the feature
|
|
|
|
is not so useful for them.) In a before trigger, the <literal>WHEN</>
|
|
|
|
condition is evaluated just before the function is or would be executed,
|
|
|
|
so using <literal>WHEN</> is not materially different from testing the
|
|
|
|
same condition at the beginning of the trigger function. However, in
|
|
|
|
an after trigger, the <literal>WHEN</> condition is evaluated just after
|
|
|
|
the row update occurs, and it determines whether an event is queued to
|
|
|
|
fire the trigger at the end of statement. So when an after trigger's
|
|
|
|
<literal>WHEN</> condition does not return true, it is not necessary
|
|
|
|
to queue an event nor to re-fetch the row at end of statement. This
|
|
|
|
can result in significant speedups in statements that modify many
|
|
|
|
rows, if the trigger only needs to be fired for a few of the rows.
|
|
|
|
</para>
|
|
|
|
|
2004-12-30 04:13:56 +01:00
|
|
|
<para>
|
|
|
|
Typically, row before triggers are used for checking or
|
|
|
|
modifying the data that will be inserted or updated. For example,
|
|
|
|
a before trigger might be used to insert the current time into a
|
2005-11-05 00:14:02 +01:00
|
|
|
<type>timestamp</type> column, or to check that two elements of the row are
|
2004-12-30 04:13:56 +01:00
|
|
|
consistent. Row after triggers are most sensibly
|
|
|
|
used to propagate the updates to other tables, or make consistency
|
|
|
|
checks against other tables. The reason for this division of labor is
|
|
|
|
that an after trigger can be certain it is seeing the final value of the
|
|
|
|
row, while a before trigger cannot; there might be other before triggers
|
|
|
|
firing after it. If you have no specific reason to make a trigger before
|
|
|
|
or after, the before case is more efficient, since the information about
|
|
|
|
the operation doesn't have to be saved until end of statement.
|
|
|
|
</para>
|
|
|
|
|
2000-05-02 22:02:03 +02:00
|
|
|
<para>
|
2003-10-23 00:28:10 +02:00
|
|
|
If a trigger function executes SQL commands then these
|
Update documentation on may/can/might:
Standard English uses "may", "can", and "might" in different ways:
may - permission, "You may borrow my rake."
can - ability, "I can lift that log."
might - possibility, "It might rain today."
Unfortunately, in conversational English, their use is often mixed, as
in, "You may use this variable to do X", when in fact, "can" is a better
choice. Similarly, "It may crash" is better stated, "It might crash".
Also update two error messages mentioned in the documenation to match.
2007-01-31 21:56:20 +01:00
|
|
|
commands might 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
|
2003-08-31 19:32:24 +02:00
|
|
|
it.<indexterm><primary>trigger</><secondary>arguments for trigger
|
|
|
|
functions</></indexterm> 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>
|
|
|
|
|
2004-12-30 04:13:56 +01:00
|
|
|
<para>
|
|
|
|
Each programming language that supports triggers has its own method
|
|
|
|
for making the trigger input data available to the trigger function.
|
|
|
|
This input data includes the type of trigger event (e.g.,
|
|
|
|
<command>INSERT</command> or <command>UPDATE</command>) as well as any
|
|
|
|
arguments that were listed in <command>CREATE TRIGGER</>.
|
|
|
|
For a row-level trigger, the input data also includes the
|
|
|
|
<varname>NEW</varname> row for <command>INSERT</command> and
|
|
|
|
<command>UPDATE</command> triggers, and/or the <varname>OLD</varname> row
|
|
|
|
for <command>UPDATE</command> and <command>DELETE</command> triggers.
|
|
|
|
Statement-level triggers do not currently have any way to examine the
|
|
|
|
individual row(s) modified by the statement.
|
|
|
|
</para>
|
|
|
|
|
2000-05-02 22:02:03 +02:00
|
|
|
</sect1>
|
|
|
|
|
2003-10-23 00:28:10 +02:00
|
|
|
<sect1 id="trigger-datachanges">
|
|
|
|
<title>Visibility of Data Changes</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
If you execute SQL commands in your trigger function, and these
|
|
|
|
commands access the table that the trigger is for, then
|
|
|
|
you need to be aware of the data visibility rules, because they determine
|
|
|
|
whether these SQL commands will see the data change that the trigger
|
|
|
|
is fired for. Briefly:
|
|
|
|
|
|
|
|
<itemizedlist>
|
2004-01-22 20:50:21 +01:00
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Statement-level triggers follow simple visibility rules: none of
|
|
|
|
the changes made by a statement are visible to statement-level
|
|
|
|
triggers that are invoked before the statement, whereas all
|
|
|
|
modifications are visible to statement-level after triggers.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
|
2003-10-23 00:28:10 +02:00
|
|
|
<listitem>
|
|
|
|
<para>
|
2004-01-22 20:50:21 +01:00
|
|
|
The data change (insertion, update, or deletion) causing the
|
|
|
|
trigger to fire is naturally <emphasis>not</emphasis> visible
|
|
|
|
to SQL commands executed in a row-level before trigger, because
|
|
|
|
it hasn't happened yet.
|
2003-10-23 00:28:10 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2004-01-22 20:50:21 +01:00
|
|
|
However, SQL commands executed in a row-level before
|
|
|
|
trigger <emphasis>will</emphasis> see the effects of data
|
|
|
|
changes for rows previously processed in the same outer
|
|
|
|
command. This requires caution, since the ordering of these
|
|
|
|
change events is not in general predictable; a SQL command that
|
Update documentation on may/can/might:
Standard English uses "may", "can", and "might" in different ways:
may - permission, "You may borrow my rake."
can - ability, "I can lift that log."
might - possibility, "It might rain today."
Unfortunately, in conversational English, their use is often mixed, as
in, "You may use this variable to do X", when in fact, "can" is a better
choice. Similarly, "It may crash" is better stated, "It might crash".
Also update two error messages mentioned in the documenation to match.
2007-01-31 21:56:20 +01:00
|
|
|
affects multiple rows can visit the rows in any order.
|
2003-10-23 00:28:10 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2004-01-22 20:50:21 +01:00
|
|
|
When a row-level after trigger is fired, all data changes made
|
|
|
|
by the outer command are already complete, and are visible to
|
|
|
|
the invoked trigger function.
|
2003-10-23 00:28:10 +02:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
</para>
|
|
|
|
|
2009-05-27 03:18:06 +02:00
|
|
|
<para>
|
|
|
|
If your trigger function is written in any of the standard procedural
|
|
|
|
languages, then the above statements apply only if the function is
|
|
|
|
declared <literal>VOLATILE</>. Functions that are declared
|
|
|
|
<literal>STABLE</> or <literal>IMMUTABLE</> will not see changes made by
|
|
|
|
the calling command in any case.
|
|
|
|
</para>
|
|
|
|
|
2003-10-23 00:28:10 +02:00
|
|
|
<para>
|
|
|
|
Further information about data visibility rules can be found in
|
|
|
|
<xref linkend="spi-visibility">. The example in <xref
|
|
|
|
linkend="trigger-example"> contains a demonstration of these rules.
|
|
|
|
</para>
|
|
|
|
</sect1>
|
2000-05-02 22:02:03 +02:00
|
|
|
|
2003-10-23 00:28:10 +02:00
|
|
|
<sect1 id="trigger-interface">
|
|
|
|
<title>Writing Trigger Functions in C</title>
|
|
|
|
|
|
|
|
<indexterm zone="trigger-interface">
|
2003-08-31 19:32:24 +02:00
|
|
|
<primary>trigger</primary>
|
|
|
|
<secondary>in C</secondary>
|
|
|
|
</indexterm>
|
|
|
|
|
2000-05-02 22:02:03 +02:00
|
|
|
<para>
|
2000-05-29 03:59:17 +02:00
|
|
|
This section describes the low-level details of the interface to a
|
2005-10-13 23:09:38 +02:00
|
|
|
trigger function. This information is only needed when writing
|
|
|
|
trigger functions in C. If you are using a higher-level language then
|
|
|
|
these details are handled for you. In most cases you should consider
|
|
|
|
using a procedural language before writing your triggers in C. The
|
|
|
|
documentation of each procedural language explains how to write a
|
|
|
|
trigger in that language.
|
2003-10-23 00:28:10 +02:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Trigger functions must use the <quote>version 1</> function manager
|
|
|
|
interface.
|
2000-05-29 03:59:17 +02:00
|
|
|
</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
|
2007-02-01 01:28:19 +01:00
|
|
|
manager or not by executing the macro:
|
2003-04-11 20:41:20 +02:00
|
|
|
<programlisting>
|
|
|
|
CALLED_AS_TRIGGER(fcinfo)
|
|
|
|
</programlisting>
|
2007-02-01 01:28:19 +01:00
|
|
|
which expands to:
|
2002-09-21 20:32:54 +02:00
|
|
|
<programlisting>
|
2007-12-04 00:49:51 +01:00
|
|
|
((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))
|
2002-09-21 20:32:54 +02:00
|
|
|
</programlisting>
|
2002-11-23 04:59:09 +01:00
|
|
|
If this returns true, then it is safe to cast
|
2005-01-22 23:06:27 +01:00
|
|
|
<literal>fcinfo->context</> to type <literal>TriggerData
|
2002-11-23 04:59:09 +01:00
|
|
|
*</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;
|
2004-10-30 22:53:06 +02:00
|
|
|
Buffer tg_trigtuplebuf;
|
|
|
|
Buffer tg_newtuplebuf;
|
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>
|
Update documentation on may/can/might:
Standard English uses "may", "can", and "might" in different ways:
may - permission, "You may borrow my rake."
can - ability, "I can lift that log."
might - possibility, "It might rain today."
Unfortunately, in conversational English, their use is often mixed, as
in, "You may use this variable to do X", when in fact, "can" is a better
choice. Similarly, "It may crash" is better stated, "It might crash".
Also update two error messages mentioned in the documenation to match.
2007-01-31 21:56:20 +01:00
|
|
|
Describes the event for which the function is called. You can use the
|
2004-12-30 04:13:56 +01:00
|
|
|
following macros to examine <literal>tg_event</literal>:
|
|
|
|
|
|
|
|
<variablelist>
|
|
|
|
<varlistentry>
|
|
|
|
<term><literal>TRIGGER_FIRED_BEFORE(tg_event)</literal></term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Returns true if the trigger fired before the operation.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term><literal>TRIGGER_FIRED_AFTER(tg_event)</literal></term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Returns true if the trigger fired after the operation.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term><literal>TRIGGER_FIRED_FOR_ROW(tg_event)</literal></term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Returns true if the trigger fired for a row-level event.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term><literal>TRIGGER_FIRED_FOR_STATEMENT(tg_event)</literal></term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Returns true if the trigger fired for a statement-level event.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term><literal>TRIGGER_FIRED_BY_INSERT(tg_event)</literal></term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Returns true if the trigger was fired by an <command>INSERT</command> command.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term><literal>TRIGGER_FIRED_BY_UPDATE(tg_event)</literal></term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Returns true if the trigger was fired by an <command>UPDATE</command> command.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term><literal>TRIGGER_FIRED_BY_DELETE(tg_event)</literal></term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Returns true if the trigger was fired by a <command>DELETE</command> command.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
2008-03-28 01:21:56 +01:00
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term><literal>TRIGGER_FIRED_BY_TRUNCATE(tg_event)</literal></term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Returns true if the trigger was fired by a <command>TRUNCATE</command> command.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
2004-12-30 04:13:56 +01:00
|
|
|
</variablelist>
|
2000-05-02 22:02:03 +02:00
|
|
|
</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>
|
2004-12-30 04:13:56 +01:00
|
|
|
A pointer to a structure describing the relation that the trigger fired for.
|
|
|
|
Look at <filename>utils/rel.h</> for details about
|
|
|
|
this structure. The most interesting things are
|
2005-01-22 23:06:27 +01:00
|
|
|
<literal>tg_relation->rd_att</> (descriptor of the relation
|
|
|
|
tuples) and <literal>tg_relation->rd_rel->relname</>
|
2004-12-30 04:13:56 +01: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
|
|
|
|
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>
|
2004-12-30 04:13:56 +01: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
|
|
|
|
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>
|
2004-12-30 04:13:56 +01: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>
|
2004-12-30 04:13:56 +01: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;
|
2010-01-17 23:56:23 +01:00
|
|
|
bool tgisinternal;
|
2002-04-02 00:36:13 +02:00
|
|
|
Oid tgconstrrelid;
|
2009-07-28 04:56:31 +02:00
|
|
|
Oid tgconstrindid;
|
2007-02-14 02:58:58 +01:00
|
|
|
Oid tgconstraint;
|
2000-01-11 06:37:11 +01:00
|
|
|
bool tgdeferrable;
|
|
|
|
bool tginitdeferred;
|
|
|
|
int16 tgnargs;
|
2005-03-29 02:17:27 +02:00
|
|
|
int16 tgnattr;
|
|
|
|
int16 *tgattr;
|
2000-01-11 06:37:11 +01:00
|
|
|
char **tgargs;
|
2009-11-20 21:38:12 +01:00
|
|
|
char *tgqual;
|
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,
|
2009-07-28 04:56:31 +02:00
|
|
|
<structfield>tgnargs</> is the 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>
|
2004-10-30 22:53:06 +02:00
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term><structfield>tg_trigtuplebuf</></term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2004-12-13 19:05:10 +01:00
|
|
|
The buffer containing <structfield>tg_trigtuple</structfield>, or <symbol>InvalidBuffer</symbol> if there
|
2004-10-30 22:53:06 +02:00
|
|
|
is no such tuple or it is not stored in a disk buffer.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
|
|
|
<varlistentry>
|
|
|
|
<term><structfield>tg_newtuplebuf</></term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2004-12-13 19:05:10 +01:00
|
|
|
The buffer containing <structfield>tg_newtuple</structfield>, or <symbol>InvalidBuffer</symbol> if there
|
2004-10-30 22:53:06 +02:00
|
|
|
is no such tuple or it is not stored in a disk buffer.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
|
2000-05-02 22:02:03 +02:00
|
|
|
</variablelist>
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2004-08-13 18:17:19 +02:00
|
|
|
A trigger function must return either a
|
|
|
|
<structname>HeapTuple</> pointer or a <symbol>NULL</> pointer
|
2004-12-13 19:05:10 +01:00
|
|
|
(<emphasis>not</> an SQL null value, that is, do not set <parameter>isNull</parameter> true).
|
2004-08-13 18:17:19 +02:00
|
|
|
Be careful to return either
|
2003-10-23 00:28:10 +02:00
|
|
|
<structfield>tg_trigtuple</> or <structfield>tg_newtuple</>,
|
|
|
|
as appropriate, if you don't want to modify the row being operated on.
|
2000-05-02 22:02:03 +02:00
|
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
<sect1 id="trigger-example">
|
2009-08-05 00:04:37 +02:00
|
|
|
<title>A Complete Trigger 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.
|
Update documentation on may/can/might:
Standard English uses "may", "can", and "might" in different ways:
may - permission, "You may borrow my rake."
can - ability, "I can lift that log."
might - possibility, "It might rain today."
Unfortunately, in conversational English, their use is often mixed, as
in, "You may use this variable to do X", when in fact, "can" is a better
choice. Similarly, "It may crash" is better stated, "It might crash".
Also update two error messages mentioned in the documenation to match.
2007-01-31 21:56:20 +01:00
|
|
|
(Examples of triggers written in procedural languages can be found
|
2003-10-23 00:28:10 +02:00
|
|
|
in the documentation of the procedural languages.)
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2003-04-11 20:41:20 +02:00
|
|
|
The function <function>trigf</> reports the number of rows in the
|
2004-03-03 23:22:24 +01:00
|
|
|
table <structname>ttest</> and skips the actual operation if the
|
2003-04-11 20:41:20 +02:00
|
|
|
command attempts to insert a null value into the column
|
2004-03-03 23:22:24 +01:00
|
|
|
<structfield>x</>. (So the trigger acts as a not-null constraint but
|
2003-04-11 20:41:20 +02:00
|
|
|
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:
|
2008-12-08 00:46:39 +01:00
|
|
|
<programlisting><![CDATA[
|
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
|
|
|
|
2009-11-23 22:41:20 +01:00
|
|
|
#ifdef PG_MODULE_MAGIC
|
|
|
|
PG_MODULE_MAGIC;
|
|
|
|
#endif
|
|
|
|
|
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
|
|
|
{
|
2008-12-08 00:46:39 +01:00
|
|
|
TriggerData *trigdata = (TriggerData *) fcinfo->context;
|
2002-09-21 20:32:54 +02:00
|
|
|
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 */
|
2008-12-08 00:46:39 +01:00
|
|
|
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
|
|
|
|
rettuple = trigdata->tg_newtuple;
|
2002-09-21 20:32:54 +02:00
|
|
|
else
|
2008-12-08 00:46:39 +01:00
|
|
|
rettuple = trigdata->tg_trigtuple;
|
2002-09-21 20:32:54 +02:00
|
|
|
|
|
|
|
/* check for null values */
|
2008-12-08 00:46:39 +01:00
|
|
|
if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)
|
|
|
|
&& TRIGGER_FIRED_BEFORE(trigdata->tg_event))
|
2002-09-21 20:32:54 +02:00
|
|
|
checknull = true;
|
|
|
|
|
2008-12-08 00:46:39 +01:00
|
|
|
if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
|
2002-09-21 20:32:54 +02:00
|
|
|
when = "before";
|
|
|
|
else
|
|
|
|
when = "after ";
|
|
|
|
|
2008-12-08 00:46:39 +01:00
|
|
|
tupdesc = trigdata->tg_relation->rd_att;
|
2002-09-21 20:32:54 +02:00
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
/* connect to SPI manager */
|
2008-12-08 00:46:39 +01:00
|
|
|
if ((ret = SPI_connect()) < 0)
|
2009-01-06 17:39:52 +01:00
|
|
|
elog(ERROR, "trigf (fired %s): SPI_connect returned %d", when, ret);
|
2002-09-21 20:32:54 +02:00
|
|
|
|
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);
|
|
|
|
|
2008-12-08 00:46:39 +01:00
|
|
|
if (ret < 0)
|
2009-01-06 17:39:52 +01:00
|
|
|
elog(ERROR, "trigf (fired %s): SPI_exec returned %d", when, ret);
|
2002-09-21 20:32:54 +02:00
|
|
|
|
2003-04-11 20:41:20 +02:00
|
|
|
/* count(*) returns int8, so be careful to convert */
|
2008-12-08 00:46:39 +01:00
|
|
|
i = DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[0],
|
|
|
|
SPI_tuptable->tupdesc,
|
2003-04-11 20:41:20 +02:00
|
|
|
1,
|
2008-12-08 00:46:39 +01:00
|
|
|
&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)
|
|
|
|
{
|
2008-12-08 00:46:39 +01: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
|
|
|
}
|
2008-12-08 00:46:39 +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>
|
2009-04-07 06:02:41 +02:00
|
|
|
After you have compiled the source code (see <xref
|
|
|
|
linkend="dfunc">), 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;
|
|
|
|
|
2008-03-28 01:21:56 +01:00
|
|
|
CREATE TRIGGER tbefore BEFORE INSERT OR UPDATE OR DELETE ON ttest
|
2003-04-11 20:41:20 +02:00
|
|
|
FOR EACH ROW EXECUTE PROCEDURE trigf();
|
2000-08-25 01:59:38 +02:00
|
|
|
|
2008-03-28 01:21:56 +01:00
|
|
|
CREATE TRIGGER tafter AFTER INSERT OR UPDATE OR DELETE ON ttest
|
2003-04-11 20:41:20 +02:00
|
|
|
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>
|
2005-01-22 23:56:36 +01:00
|
|
|
=> INSERT INTO ttest VALUES (NULL);
|
2003-04-11 20:41:20 +02:00
|
|
|
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
|
|
|
|
|
2005-01-22 23:56:36 +01:00
|
|
|
=> SELECT * FROM ttest;
|
2002-09-21 20:32:54 +02:00
|
|
|
x
|
|
|
|
---
|
1998-03-01 09:16:16 +01:00
|
|
|
(0 rows)
|
|
|
|
|
2005-01-22 23:56:36 +01:00
|
|
|
=> INSERT INTO ttest VALUES (1);
|
2003-04-11 20:41:20 +02:00
|
|
|
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
|
2005-01-22 23:56:36 +01: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)
|
|
|
|
|
2005-01-22 23:56:36 +01:00
|
|
|
=> INSERT INTO ttest SELECT x * 2 FROM ttest;
|
2003-04-11 20:41:20 +02:00
|
|
|
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
|
2005-01-22 23:56:36 +01: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)
|
|
|
|
|
2005-01-22 23:56:36 +01:00
|
|
|
=> UPDATE ttest SET x = NULL WHERE x = 2;
|
2003-04-11 20:41:20 +02:00
|
|
|
INFO: trigf (fired before): there are 2 rows in ttest
|
1998-03-01 09:16:16 +01:00
|
|
|
UPDATE 0
|
2005-01-22 23:56:36 +01:00
|
|
|
=> UPDATE ttest SET x = 4 WHERE x = 2;
|
2003-04-11 20:41:20 +02:00
|
|
|
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
|
2005-01-22 23:56:36 +01: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)
|
|
|
|
|
2005-01-22 23:56:36 +01:00
|
|
|
=> DELETE FROM ttest;
|
2003-04-11 20:41:20 +02:00
|
|
|
INFO: trigf (fired before): there are 2 rows in ttest
|
|
|
|
INFO: trigf (fired before): there are 1 rows in ttest
|
2005-10-13 04:23:12 +02:00
|
|
|
INFO: trigf (fired after ): there are 0 rows in ttest
|
2003-04-11 20:41:20 +02:00
|
|
|
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
|
2005-01-22 23:56:36 +01: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>
|