SPI to reference transition tables, see
<xref linkend="spi-spi-register-trigger-data"/>.
</para>
<para>
A trigger function must return either a
<structname>HeapTuple</structname> pointer or a <symbol>NULL</symbol> pointer
(<emphasis>not</emphasis> an SQL null value, that is, do not set <parameter>isNull</parameter> true).
Be careful to return either
<structfield>tg_trigtuple</structfield> or <structfield>tg_newtuple</structfield>,
as appropriate, if you don't want to modify the row being operated on.
</para>
</sect1>
<sect1 id="trigger-example">
<title>A Complete Trigger Example</title>
<para>
Here is a very simple example of a trigger function written in C.
(Examples of triggers written in procedural languages can be found
in the documentation of the procedural languages.)
</para>
<para>
The function <function>trigf</function> reports the number of rows in the
table <structname>ttest</structname> and skips the actual operation if the
command attempts to insert a null value into the column
<structfield>x</structfield>. (So the trigger acts as a not-null constraint but
doesn't abort the transaction.)
</para>
<para>
First, the table definition:
<programlisting>
CREATE TABLE ttest (
x integer
);
</programlisting>
</para>
<para>
This is the source code of the trigger function:
<programlisting><![CDATA[
#include "postgres.h"
#include "fmgr.h"
#include "executor/spi.h" /* this is what you need to work with SPI */
#include "commands/trigger.h" /* ... triggers ... */
#include "utils/rel.h" /* ... and relations */
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(trigf);
Datum
trigf(PG_FUNCTION_ARGS)
{
TriggerData *trigdata = (TriggerData *) fcinfo->context;
TupleDesc tupdesc;
HeapTuple rettuple;
char *when;
bool checknull = false;
bool isnull;
int ret, i;
/* make sure it's called as a trigger at all */
if (!CALLED_AS_TRIGGER(fcinfo))
elog(ERROR, "trigf: not called by trigger manager");
/* tuple to return to executor */
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
rettuple = trigdata->tg_newtuple;
else
rettuple = trigdata->tg_trigtuple;
/* check for null values */
if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)
&& TRIGGER_FIRED_BEFORE(trigdata->tg_event))
checknull = true;
if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
when = "before";
else
when = "after ";
tupdesc = trigdata->tg_relation->rd_att;
/* connect to SPI manager */
SPI_connect();
/* get number of rows in table */
ret = SPI_exec("SELECT count(*) FROM ttest", 0);
if (ret < 0)
elog(ERROR, "trigf (fired %s): SPI_exec returned %d", when, ret);
/* count(*) returns int8, so be careful