Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
Postgres FD Implementation
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Abuhujair Javed
Postgres FD Implementation
Commits
d8521b9b
Commit
d8521b9b
authored
Apr 11, 2003
by
Peter Eisentraut
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Revision
parent
dcb00495
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
199 additions
and
186 deletions
+199
-186
doc/src/sgml/trigger.sgml
doc/src/sgml/trigger.sgml
+199
-186
No files found.
doc/src/sgml/trigger.sgml
View file @
d8521b9b
<!--
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/trigger.sgml,v 1.2
7 2003/03/25 16:15:38
petere Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/trigger.sgml,v 1.2
8 2003/04/11 18:41:20
petere Exp $
-->
-->
<chapter id="triggers">
<chapter id="triggers">
<title>Triggers</title>
<title>Triggers</title>
<para>
<para>
<productname>PostgreSQL</productname> has various server-side
This chapter describes how to write trigger functions. In
function interfaces. Server-side functions can be written in
particular, it describes the C-language interface for trigger
<acronym>SQL</acronym>, C, or any defined procedural
functions. The trigger interfaces in most procedural languages
language. Trigger functions can be written in C and most procedural
work analogously. (Trigger functions cannot be written in SQL.)
languages, but not in <acronym>SQL</acronym>. Both per-row and
</para>
per-statement triggers are supported. A trigger procedure can
execute BEFORE or AFTER a <command>INSERT</command>,
<para>
<command>DELETE</command> or <command>UPDATE</command>, either once
A trigger function can execute before or after a
per modified row, or once per <acronym>SQL</acronym> statement.
<command>INSERT</command>, <command>UPDATE</command>, or
<command>DELETE</command>, either once per modified row, or once
per <acronym>SQL</acronym> statement.
</para>
</para>
<sect1 id="trigger-definition">
<sect1 id="trigger-definition">
<title>Trigger Definition</title>
<title>Trigger Definition</title>
<para>
<para>
If a trigger event occurs, the trigger manager
(
called by the
If a trigger event occurs, the trigger manager
is
called by the
Executor) sets up a <structname>TriggerData</> information
executor. It sets up an information structure of type
structure (described below) and calls the trigger function to
<structname>TriggerData</> (described below) and calls the trigger
handle the event.
function to
handle the event.
</para>
</para>
<para>
<para>
...
@@ -42,15 +44,16 @@ $Header: /cvsroot/pgsql/doc/src/sgml/trigger.sgml,v 1.27 2003/03/25 16:15:38 pet
...
@@ -42,15 +44,16 @@ $Header: /cvsroot/pgsql/doc/src/sgml/trigger.sgml,v 1.27 2003/03/25 16:15:38 pet
</para>
</para>
<para>
<para>
Trigger functions return a <structname>HeapTuple</> to the calling
Trigger functions return a value of type <structname>HeapTuple</>,
executor. The return value is ignored for triggers fired AFTER an
which represents a table row, to the calling executor. The return
operation, but it allows BEFORE triggers to:
value is ignored for triggers fired after an operation, but a
triggers fired before an operation has the following choices:
<itemizedlist>
<itemizedlist>
<listitem>
<listitem>
<para>
<para>
Return a <symbol>NULL</> pointer to skip the operation for the
It can return a <symbol>NULL</> pointer to skip the operation
current tuple (and so the tuple
will not be
for the current row (and so the row
will not be
inserted/updated/deleted).
inserted/updated/deleted).
</para>
</para>
</listitem>
</listitem>
...
@@ -58,60 +61,54 @@ $Header: /cvsroot/pgsql/doc/src/sgml/trigger.sgml,v 1.27 2003/03/25 16:15:38 pet
...
@@ -58,60 +61,54 @@ $Header: /cvsroot/pgsql/doc/src/sgml/trigger.sgml,v 1.27 2003/03/25 16:15:38 pet
<listitem>
<listitem>
<para>
<para>
For <command>INSERT</command> and <command>UPDATE</command>
For <command>INSERT</command> and <command>UPDATE</command>
triggers only, the returned
tuple becomes the tuple which
will
triggers only, the returned
row becomes the row that
will
be inserted or will replace the
tuple
being updated. This
be inserted or will replace the
row
being updated. This
allows the trigger function to modify the row being inserted or
allows the trigger function to modify the row being inserted or
updated.
updated.
</para>
</para>
</listitem>
</listitem>
</itemizedlist>
</itemizedlist>
A BEFORE trigger that does not intend to cause either of these behaviors
A before trigger that does not intend to cause either of these
must be careful to return the same NEW tuple it is passed.
behaviors must be careful to return the same row that was passed
</para>
in as the new row (see below).
<para>
Note that there is no initialization performed by the
<command>CREATE TRIGGER</command> handler. This may be changed in
the future.
</para>
</para>
<para>
<para>
If more than one trigger is defined for the same event on the same
If more than one trigger is defined for the same event on the same
relation, the triggers will be fired in alphabetical order by
relation, the triggers will be fired in alphabetical order by
name. In the case of
BEFORE triggers, the possibly-modified tuple
name. In the case of
before triggers, the possibly-modified row
returned by each trigger becomes the input to the next trigger.
returned by each trigger becomes the input to the next trigger.
If any
BEFORE trigger returns <symbol>NULL</>, the operation is
If any
before trigger returns a <symbol>NULL</> pointer, the
abandoned and subsequent triggers are not fired.
operation is
abandoned and subsequent triggers are not fired.
</para>
</para>
<para>
<para>
If a trigger function executes SQL
-querie
s (using SPI) then these
If a trigger function executes SQL
command
s (using SPI) then these
querie
s may fire triggers again. This is known as cascading
command
s may fire triggers again. This is known as cascading
triggers. There is no direct limitation on the number of cascade
triggers. There is no direct limitation on the number of cascade
levels. It is possible for cascades to cause recursive invocation
levels. It is possible for cascades to cause
a
recursive invocation
of the same trigger
---
for example, an <command>INSERT</command>
of the same trigger
;
for example, an <command>INSERT</command>
trigger might execute a
query that inserts an additional tuple
trigger might execute a
command that inserts an additional row
into the same table, causing the <command>INSERT</command> trigger
into the same table, causing the <command>INSERT</command> trigger
to be fired again. It is the trigger programmer's responsibility
to be fired again. It is the trigger programmer's responsibility
to avoid infinite recursion in such scenarios.
to avoid infinite recursion in such scenarios.
</para>
</para>
<para>
<para>
When a trigger is defined, a number of arguments can be
When a trigger is being defined, arguments can be specified for
specified. The purpose of including arguments in the trigger
it. The purpose of including arguments in the trigger definition
definition is to allow different triggers with similar
is to allow different triggers with similar requirements to call
requirements to call the same function. As an example, there
the same function. As an example, there could be a generalized
could be a generalized trigger function that takes as its
trigger function that takes as its arguments two column names and
arguments two field names and puts the current user in one and the
puts the current user in one and the current time stamp in the
current time stamp in the other. Properly written, this trigger
other. Properly written, this trigger function would be
function would be independent of the specific table it is
independent of the specific table it is triggering on. So the
triggering on. So the same function could be used for
same function could be used for <command>INSERT</command> events
<command>INSERT</command> events on any table with suitable
on any table with suitable columns, to automatically track creation
fields, to automatically track creation of records in a
of records in a transaction table for example. It could also be
transaction table for example. It could also be used to track
used to track last-update events if defined as an
last-update events if defined as an <command>UPDATE</command>
<command>UPDATE</command> trigger.
trigger.
</para>
</para>
</sect1>
</sect1>
...
@@ -122,26 +119,20 @@ $Header: /cvsroot/pgsql/doc/src/sgml/trigger.sgml,v 1.27 2003/03/25 16:15:38 pet
...
@@ -122,26 +119,20 @@ $Header: /cvsroot/pgsql/doc/src/sgml/trigger.sgml,v 1.27 2003/03/25 16:15:38 pet
<para>
<para>
This section describes the low-level details of the interface to a
This section describes the low-level details of the interface to a
trigger function. This information is only needed when writing a
trigger function. This information is only needed when writing a
trigger function in C. If you are using a higher-level
function
trigger function in C. If you are using a higher-level
language then these details are handled for you.
language then these details are handled for you.
</para>
</para>
<note>
<para>
The interface described here applies for
<productname>PostgreSQL</productname> 7.1 and later.
Earlier versions passed the <structname>TriggerData</> pointer in a global
variable <varname>CurrentTriggerData</>.
</para>
</note>
<para>
<para>
When a function is called by the trigger manager, it is not passed
When a function is called by the trigger manager, it is not passed
any normal
parameter
s, but it is passed a <quote>context</>
any normal
argument
s, but it is passed a <quote>context</>
pointer pointing to a <structname>TriggerData</> structure. C
pointer pointing to a <structname>TriggerData</> structure. C
functions can check whether they were called from the trigger
functions can check whether they were called from the trigger
manager or not by executing the macro
manager or not by executing the macro
<literal>CALLED_AS_TRIGGER(fcinfo)</literal>, which expands to
<programlisting>
CALLED_AS_TRIGGER(fcinfo)
</programlisting>
which expands to
<programlisting>
<programlisting>
((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))
((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))
</programlisting>
</programlisting>
...
@@ -176,7 +167,7 @@ typedef struct TriggerData
...
@@ -176,7 +167,7 @@ typedef struct TriggerData
<term><structfield>type</></term>
<term><structfield>type</></term>
<listitem>
<listitem>
<para>
<para>
Always <literal>T_TriggerData</literal>
if this is a trigger event
.
Always <literal>T_TriggerData</literal>.
</para>
</para>
</listitem>
</listitem>
</varlistentry>
</varlistentry>
...
@@ -185,69 +176,69 @@ typedef struct TriggerData
...
@@ -185,69 +176,69 @@ typedef struct TriggerData
<term><structfield>tg_event</></term>
<term><structfield>tg_event</></term>
<listitem>
<listitem>
<para>
<para>
d
escribes the event for which the function is called. You may use the
D
escribes the event for which the function is called. You may use the
following macros to examine <literal>tg_event</literal>:
following macros to examine <literal>tg_event</literal>:
<variablelist>
<variablelist>
<varlistentry>
<varlistentry>
<term>
TRIGGER_FIRED_BEFORE(tg_event)
</term>
<term>
<literal>TRIGGER_FIRED_BEFORE(tg_event)</literal>
</term>
<listitem>
<listitem>
<para>
<para>
returns TRUE if trigger fired BEFORE
.
Returns true if the trigger fired before the operation
.
</para>
</para>
</listitem>
</listitem>
</varlistentry>
</varlistentry>
<varlistentry>
<varlistentry>
<term>
TRIGGER_FIRED_AFTER(tg_event)
</term>
<term>
<literal>TRIGGER_FIRED_AFTER(tg_event)</literal>
</term>
<listitem>
<listitem>
<para>
<para>
Returns
TRUE if trigger fired AFTER
.
Returns
true if the trigger fired after the operation
.
</para>
</para>
</listitem>
</listitem>
</varlistentry>
</varlistentry>
<varlistentry>
<varlistentry>
<term>
TRIGGER_FIRED_FOR_ROW(event)
</term>
<term>
<literal>TRIGGER_FIRED_FOR_ROW(tg_event)</literal>
</term>
<listitem>
<listitem>
<para>
<para>
Returns
TRUE if trigger fired for a ROW
-level event.
Returns
true if the trigger fired for a row
-level event.
</para>
</para>
</listitem>
</listitem>
</varlistentry>
</varlistentry>
<varlistentry>
<varlistentry>
<term>
TRIGGER_FIRED_FOR_STATEMENT(event)
</term>
<term>
<literal>TRIGGER_FIRED_FOR_STATEMENT(tg_event)</literal>
</term>
<listitem>
<listitem>
<para>
<para>
Returns
TRUE if trigger fired for STATEMENT
-level event.
Returns
true if the trigger fired for a statement
-level event.
</para>
</para>
</listitem>
</listitem>
</varlistentry>
</varlistentry>
<varlistentry>
<varlistentry>
<term>
TRIGGER_FIRED_BY_INSERT(event)
</term>
<term>
<literal>TRIGGER_FIRED_BY_INSERT(tg_event)</literal>
</term>
<listitem>
<listitem>
<para>
<para>
Returns
TRUE if trigger fired by <command>INSERT</command>
.
Returns
true if the trigger was fired by an <command>INSERT</command> command
.
</para>
</para>
</listitem>
</listitem>
</varlistentry>
</varlistentry>
<varlistentry>
<varlistentry>
<term>
TRIGGER_FIRED_BY_DELETE(event)
</term>
<term>
<literal>TRIGGER_FIRED_BY_UPDATE(tg_event)</literal>
</term>
<listitem>
<listitem>
<para>
<para>
Returns
TRUE if trigger fired by <command>DELETE</command>
.
Returns
true if the trigger was fired by an <command>UPDATE</command> command
.
</para>
</para>
</listitem>
</listitem>
</varlistentry>
</varlistentry>
<varlistentry>
<varlistentry>
<term>
TRIGGER_FIRED_BY_UPDATE(event)
</term>
<term>
<literal>TRIGGER_FIRED_BY_DELETE(tg_event)</literal>
</term>
<listitem>
<listitem>
<para>
<para>
Returns
TRUE if trigger fired by <command>UPDATE</command>
.
Returns
true if the trigger was fired by a <command>DELETE</command> command
.
</para>
</para>
</listitem>
</listitem>
</varlistentry>
</varlistentry>
...
@@ -260,14 +251,14 @@ typedef struct TriggerData
...
@@ -260,14 +251,14 @@ typedef struct TriggerData
<term><structfield>tg_relation</></term>
<term><structfield>tg_relation</></term>
<listitem>
<listitem>
<para>
<para>
is a pointer to structure describing the triggered
A pointer to a structure describing the relation that the trigger fired for.
relation.
Look at <filename>utils/rel.h</> for details about
Look at <filename>utils/rel.h</> for details about
this structure. The most interesting things are
this structure. The most interesting things are
<literal>tg_relation->rd_att</> (descriptor of the relation
<literal>tg_relation->rd_att</> (descriptor of the relation
tuples) and <literal>tg_relation->rd_rel->relname</>
tuples) and <literal>tg_relation->rd_rel->relname</>
(relation
's name. This is not <type>char*</>,
but
(relation
name; the type is not <type>char*</>
but
<type>NameData</>
. U
se
<type>NameData</>
; u
se
<literal>SPI_getrelname(tg_relation)</> to get <type>char*</> if you
<literal>SPI_getrelname(tg_relation)</> to get
a
<type>char*</> if you
need a copy of the name).
need a copy of the name).
</para>
</para>
</listitem>
</listitem>
...
@@ -277,15 +268,13 @@ typedef struct TriggerData
...
@@ -277,15 +268,13 @@ typedef struct TriggerData
<term><structfield>tg_trigtuple</></term>
<term><structfield>tg_trigtuple</></term>
<listitem>
<listitem>
<para>
<para>
is a pointer to the tuple for which the trigger is fired. This is
A pointer to the row for which the trigger was fired. This is
the tuple being inserted (if <command>INSERT</command>), deleted
the row being inserted, updated, or deleted. If this trigger
(if <command>DELETE</command>) or updated (if
was fired for an <command>INSERT</command> or
<command>UPDATE</command>). If this trigger was fired for an
<command>DELETE</command> then this is what you should return
<command>INSERT</command> or <command>DELETE</command> then this
to from the function if you don't want to replace the row with
is what you should return to the Executor if you don't want to
a different one (in the case of <command>INSERT</command>) or
replace the tuple with a different one (in the case of
skip the operation.
<command>INSERT</command>) or skip the operation (in the case of
<command>DELETE</command>).
</para>
</para>
</listitem>
</listitem>
</varlistentry>
</varlistentry>
...
@@ -294,12 +283,13 @@ typedef struct TriggerData
...
@@ -294,12 +283,13 @@ typedef struct TriggerData
<term><structfield>tg_newtuple</></term>
<term><structfield>tg_newtuple</></term>
<listitem>
<listitem>
<para>
<para>
is a pointer to the new version of tuple if
A pointer to the new version of the row, if the trigger was
<command>UPDATE</command> and <symbol>NULL</> if this is for an
fired for an <command>UPDATE</command>, and <symbol>NULL</> if
<command>INSERT</command> or a <command>DELETE</command>. This is
it is for an <command>INSERT</command> or a
what you are to return to Executor if <command>UPDATE</command>
<command>DELETE</command>. This is what you have to return
and you don't want to replace this tuple with another one or skip
from the function if the event is an <command>UPDATE</command>
the operation.
and you don't want to replace this row by a different one or
skip the operation.
</para>
</para>
</listitem>
</listitem>
</varlistentry>
</varlistentry>
...
@@ -308,7 +298,8 @@ typedef struct TriggerData
...
@@ -308,7 +298,8 @@ typedef struct TriggerData
<term><structfield>tg_trigger</></term>
<term><structfield>tg_trigger</></term>
<listitem>
<listitem>
<para>
<para>
is pointer to structure <structname>Trigger</> defined in <filename>utils/rel.h</>:
A pointer to a structure of type <structname>Trigger</>,
defined in <filename>utils/rel.h</>:
<programlisting>
<programlisting>
typedef struct Trigger
typedef struct Trigger
...
@@ -330,9 +321,9 @@ typedef struct Trigger
...
@@ -330,9 +321,9 @@ typedef struct Trigger
where <structfield>tgname</> is the trigger's name,
where <structfield>tgname</> is the trigger's name,
<structfield>tgnargs</> is number of arguments in
<structfield>tgnargs</> is number of arguments in
<structfield>tgargs</>, <structfield>tgargs</> is an array of
<structfield>tgargs</>,
and
<structfield>tgargs</> is an array of
pointers to the arguments specified in the <command>CREATE
pointers to the arguments specified in the <command>CREATE
TRIGGER</command> statement.
O
ther members are for internal use
TRIGGER</command> statement.
The o
ther members are for internal use
only.
only.
</para>
</para>
</listitem>
</listitem>
...
@@ -345,59 +336,73 @@ typedef struct Trigger
...
@@ -345,59 +336,73 @@ typedef struct Trigger
<title>Visibility of Data Changes</title>
<title>Visibility of Data Changes</title>
<para>
<para>
<productname>PostgreSQL</productname> data changes visibility rule: during a query execution, data
If you are using the SPI interface to execute SQL commands in your
changes made by the query itself (via SQL-function, SPI-function, triggers)
trigger functions written in C (or you are using a different
are invisible to the query scan. For example, in query
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:
<programlisting>
<itemizedlist>
INSERT INTO a SELECT * FROM a;
<listitem>
</programlisting>
<para>
The row being inserted (<structfield>tg_trigtuple</>) is
<emphasis>not</emphasis> visible to SQL commands executed in a
before trigger.
</para>
</listitem>
tuples inserted are invisible for SELECT scan. In effect, this
<listitem>
duplicates the database table within itself (subject to unique index
<para>
rules, of course) without recursing.
The row being inserted (<structfield>tg_trigtuple</>)
</para>
<emphasis>is</emphasis> visible to SQL commands executed in an
after trigger (because it was just inserted).
</para>
</listitem>
<para>
<listitem>
But keep in mind this notice about visibility in the SPI documentation:
<para>
A just-inserted row is visible to all SQL commands executed
<blockquote>
within any trigger that is fired later in the execution of the
<para>
outer command (e.g., for the next row).
Changes made by query Q are visible by queries that are started after
</para>
query Q, no matter whether they are started inside Q (during the
</listitem>
execution of Q) or after Q is done.
</itemizedlist>
</para>
</blockquote>
</para>
</para>
<para>
<para>
This is true for triggers as well so, though a tuple being inserted
The next section contains a demonstration of these rules applied.
(<structfield>tg_trigtuple</>) is not visible to queries in a BEFORE trigger, this tuple
(just inserted) is visible to queries in an AFTER trigger, and to queries
in BEFORE/AFTER triggers fired after this!
</para>
</para>
</sect1>
</sect1>
<sect1 id="trigger-example
s
">
<sect1 id="trigger-example">
<title>
Examples
</title>
<title>
A Complete Example
</title>
<para>
<para>
There are more complex examples in
Here is a very simple example of a trigger function written in C.
<filename>src/test/regress/regress.c</filename> and
The function <function>trigf</> reports the number of rows in the
in <filename>contrib/spi</filename>.
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.)
</para>
</para>
<para>
<para>
Here is a very simple example of trigger usage. Function
First, the table definition:
<function>trigf</> reports the number of tuples in the triggered
<programlisting>
relation <literal>ttest</> and skips the operation if the query
CREATE TABLE ttest (
attempts to insert a null value into x (i.e - it acts as a
x integer
<literal>NOT NULL</literal> constraint but doesn't abort the
);
transaction).
</programlisting>
</para>
<para>
This is the source code of the trigger function:
<programlisting>
<programlisting>
#include "postgres.h"
#include "executor/spi.h" /* this is what you need to work with SPI */
#include "executor/spi.h" /* this is what you need to work with SPI */
#include "commands/trigger.h" /*
-"-
and triggers */
#include "commands/trigger.h" /*
...
and triggers */
extern Datum trigf(PG_FUNCTION_ARGS);
extern Datum trigf(PG_FUNCTION_ARGS);
...
@@ -414,11 +419,11 @@ trigf(PG_FUNCTION_ARGS)
...
@@ -414,11 +419,11 @@ trigf(PG_FUNCTION_ARGS)
bool isnull;
bool isnull;
int ret, i;
int ret, i;
/*
Make sure trigdata is pointing at what I expect
*/
/*
make sure it's called as a trigger at all
*/
if (!CALLED_AS_TRIGGER(fcinfo))
if (!CALLED_AS_TRIGGER(fcinfo))
elog(ERROR, "trigf: not
fir
ed by trigger manager");
elog(ERROR, "trigf: not
call
ed by trigger manager");
/* tuple to return to
E
xecutor */
/* tuple to return to
e
xecutor */
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
rettuple = trigdata->tg_newtuple;
rettuple = trigdata->tg_newtuple;
else
else
...
@@ -436,29 +441,29 @@ trigf(PG_FUNCTION_ARGS)
...
@@ -436,29 +441,29 @@ trigf(PG_FUNCTION_ARGS)
tupdesc = trigdata->tg_relation->rd_att;
tupdesc = trigdata->tg_relation->rd_att;
/*
C
onnect to SPI manager */
/*
c
onnect to SPI manager */
if ((ret = SPI_connect()) < 0)
if ((ret = SPI_connect()) < 0)
elog(INFO, "trigf (fired %s): SPI_connect returned %d", when, ret);
elog(INFO, "trigf (fired %s): SPI_connect returned %d", when, ret);
/*
Get number of tuples in relation
*/
/*
get number of rows in table
*/
ret = SPI_exec("SELECT count(*) FROM ttest", 0);
ret = SPI_exec("SELECT count(*) FROM ttest", 0);
if (ret < 0)
if (ret < 0)
elog(NOTICE, "trigf (fired %s): SPI_exec returned %d", when, ret);
elog(NOTICE, "trigf (fired %s): SPI_exec returned %d", when, ret);
/* count(*) returns int8
as of PG 7.2
, so be careful to convert */
/* count(*) returns int8, so be careful to convert */
i =
(int)
DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[0],
i = DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[0],
SPI_tuptable->tupdesc,
SPI_tuptable->tupdesc,
1,
1,
&isnull));
&isnull));
elog (
NOTICE, "trigf (fired %s): there are %d tuple
s in ttest", when, i);
elog (
INFO, "trigf (fired %s): there are %d row
s in ttest", when, i);
SPI_finish();
SPI_finish();
if (checknull)
if (checknull)
{
{
(void)
SPI_getbinval(rettuple, tupdesc, 1, &isnull);
SPI_getbinval(rettuple, tupdesc, 1, &isnull);
if (isnull)
if (isnull)
rettuple = NULL;
rettuple = NULL;
}
}
...
@@ -469,36 +474,38 @@ trigf(PG_FUNCTION_ARGS)
...
@@ -469,36 +474,38 @@ trigf(PG_FUNCTION_ARGS)
</para>
</para>
<para>
<para>
Now, compile and create the trigger function:
After you have compiled the source code, declare the function and
the triggers:
<programlisting>
<programlisting>
CREATE FUNCTION trigf () RETURNS TRIGGER AS
CREATE FUNCTION trigf() RETURNS trigger
'...path_to_so' LANGUAGE C;
AS '<replaceable>filename</>'
LANGUAGE C;
CREATE TRIGGER tbefore BEFORE INSERT OR UPDATE OR DELETE ON ttest
FOR EACH ROW EXECUTE PROCEDURE trigf();
CREATE TABLE ttest (x int4);
CREATE TRIGGER tafter AFTER INSERT OR UPDATE OR DELETE ON ttest
FOR EACH ROW EXECUTE PROCEDURE trigf();
</programlisting>
</programlisting>
</para>
<programlisting>
<para>
vac=> CREATE TRIGGER tbefore BEFORE INSERT OR UPDATE OR DELETE ON ttest
Now you can test the operation of the trigger:
FOR EACH ROW EXECUTE PROCEDURE trigf();
<screen>
CREATE
=> INSERT INTO ttest VALUES (NULL);
vac=> CREATE TRIGGER tafter AFTER INSERT OR UPDATE OR DELETE ON ttest
INFO: trigf (fired before): there are 0 rows in ttest
FOR EACH ROW EXECUTE PROCEDURE trigf();
CREATE
vac=> INSERT INTO ttest VALUES (NULL);
WARNING: trigf (fired before): there are 0 tuples in ttest
INSERT 0 0
INSERT 0 0
-- Insertion skipped and AFTER trigger is not fired
-- Insertion skipped and AFTER trigger is not fired
vac
=> SELECT * FROM ttest;
=> SELECT * FROM ttest;
x
x
---
---
(0 rows)
(0 rows)
vac
=> INSERT INTO ttest VALUES (1);
=> INSERT INTO ttest VALUES (1);
INFO: trigf (fired before): there are 0
tuple
s in ttest
INFO: trigf (fired before): there are 0
row
s in ttest
INFO: trigf (fired after ): there are 1
tuple
s in ttest
INFO: trigf (fired after ): there are 1
row
s in ttest
^^^^^^^^
^^^^^^^^
remember what we said about visibility.
remember what we said about visibility.
INSERT 167793 1
INSERT 167793 1
...
@@ -508,25 +515,25 @@ vac=> SELECT * FROM ttest;
...
@@ -508,25 +515,25 @@ vac=> SELECT * FROM ttest;
1
1
(1 row)
(1 row)
vac
=> INSERT INTO ttest SELECT x * 2 FROM ttest;
=> INSERT INTO ttest SELECT x * 2 FROM ttest;
INFO: trigf (fired before): there are 1
tuple
s in ttest
INFO: trigf (fired before): there are 1
row
s in ttest
INFO: trigf (fired after ): there are 2
tuple
s in ttest
INFO: trigf (fired after ): there are 2
row
s in ttest
^^^^^^
^^
^^^^^^
remember what we said about visibility.
remember what we said about visibility.
INSERT 167794 1
INSERT 167794 1
vac
=> SELECT * FROM ttest;
=> SELECT * FROM ttest;
x
x
---
---
1
1
2
2
(2 rows)
(2 rows)
vac
=> UPDATE ttest SET x = NULL WHERE x = 2;
=> UPDATE ttest SET x = NULL WHERE x = 2;
INFO: trigf (fired before): there are 2
tuple
s in ttest
INFO: trigf (fired before): there are 2
row
s in ttest
UPDATE 0
UPDATE 0
vac
=> UPDATE ttest SET x = 4 WHERE x = 2;
=> UPDATE ttest SET x = 4 WHERE x = 2;
INFO: trigf (fired before): there are 2
tuple
s in ttest
INFO: trigf (fired before): there are 2
row
s in ttest
INFO: trigf (fired after ): there are 2
tuple
s in ttest
INFO: trigf (fired after ): there are 2
row
s in ttest
UPDATE 1
UPDATE 1
vac=> SELECT * FROM ttest;
vac=> SELECT * FROM ttest;
x
x
...
@@ -535,21 +542,27 @@ vac=> SELECT * FROM ttest;
...
@@ -535,21 +542,27 @@ vac=> SELECT * FROM ttest;
4
4
(2 rows)
(2 rows)
vac
=> DELETE FROM ttest;
=> DELETE FROM ttest;
INFO: trigf (fired before): there are 2
tuple
s in ttest
INFO: trigf (fired before): there are 2
row
s in ttest
INFO: trigf (fired after ): there are 1
tuple
s in ttest
INFO: trigf (fired after ): there are 1
row
s in ttest
INFO: trigf (fired before): there are 1
tuple
s in ttest
INFO: trigf (fired before): there are 1
row
s in ttest
INFO: trigf (fired after ): there are 0
tuple
s in ttest
INFO: trigf (fired after ): there are 0
row
s in ttest
^^^^^^
^^
^^^^^^
remember what we said about visibility.
remember what we said about visibility.
DELETE 2
DELETE 2
vac
=> SELECT * FROM ttest;
=> SELECT * FROM ttest;
x
x
---
---
(0 rows)
(0 rows)
</
programlisting
>
</
screen
>
</para>
</para>
<para>
There are more complex examples in
<filename>src/test/regress/regress.c</filename> and
in <filename>contrib/spi</filename>.
</para>
</sect1>
</sect1>
</chapter>
</chapter>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment