]> begriffs open source - ai-pg/blob - full-docs/txt/trigger-example.txt
Convert HTML docs to more streamlined TXT
[ai-pg] / full-docs / txt / trigger-example.txt
1
2 37.4. A Complete Trigger Example #
3
4    Here is a very simple example of a trigger function written in C.
5    (Examples of triggers written in procedural languages can be found in
6    the documentation of the procedural languages.)
7
8    The function trigf reports the number of rows in the table ttest and
9    skips the actual operation if the command attempts to insert a null
10    value into the column x. (So the trigger acts as a not-null constraint
11    but doesn't abort the transaction.)
12
13    First, the table definition:
14 CREATE TABLE ttest (
15     x integer
16 );
17
18    This is the source code of the trigger function:
19 #include "postgres.h"
20 #include "fmgr.h"
21 #include "executor/spi.h"       /* this is what you need to work with SPI */
22 #include "commands/trigger.h"   /* ... triggers ... */
23 #include "utils/rel.h"          /* ... and relations */
24
25 PG_MODULE_MAGIC;
26
27 PG_FUNCTION_INFO_V1(trigf);
28
29 Datum
30 trigf(PG_FUNCTION_ARGS)
31 {
32     TriggerData *trigdata = (TriggerData *) fcinfo->context;
33     TupleDesc   tupdesc;
34     HeapTuple   rettuple;
35     char       *when;
36     bool        checknull = false;
37     bool        isnull;
38     int         ret, i;
39
40     /* make sure it's called as a trigger at all */
41     if (!CALLED_AS_TRIGGER(fcinfo))
42         elog(ERROR, "trigf: not called by trigger manager");
43
44     /* tuple to return to executor */
45     if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
46         rettuple = trigdata->tg_newtuple;
47     else
48         rettuple = trigdata->tg_trigtuple;
49
50     /* check for null values */
51     if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)
52         && TRIGGER_FIRED_BEFORE(trigdata->tg_event))
53         checknull = true;
54
55     if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
56         when = "before";
57     else
58         when = "after ";
59
60     tupdesc = trigdata->tg_relation->rd_att;
61
62     /* connect to SPI manager */
63     SPI_connect();
64
65     /* get number of rows in table */
66     ret = SPI_exec("SELECT count(*) FROM ttest", 0);
67
68     if (ret < 0)
69         elog(ERROR, "trigf (fired %s): SPI_exec returned %d", when, ret);
70
71     /* count(*) returns int8, so be careful to convert */
72     i = DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[0],
73                                     SPI_tuptable->tupdesc,
74                                     1,
75                                     &isnull));
76
77     elog (INFO, "trigf (fired %s): there are %d rows in ttest", when, i);
78
79     SPI_finish();
80
81     if (checknull)
82     {
83         SPI_getbinval(rettuple, tupdesc, 1, &isnull);
84         if (isnull)
85             rettuple = NULL;
86     }
87
88     return PointerGetDatum(rettuple);
89 }
90
91
92    After you have compiled the source code (see Section 36.10.5), declare
93    the function and the triggers:
94 CREATE FUNCTION trigf() RETURNS trigger
95     AS 'filename'
96     LANGUAGE C;
97
98 CREATE TRIGGER tbefore BEFORE INSERT OR UPDATE OR DELETE ON ttest
99     FOR EACH ROW EXECUTE FUNCTION trigf();
100
101 CREATE TRIGGER tafter AFTER INSERT OR UPDATE OR DELETE ON ttest
102     FOR EACH ROW EXECUTE FUNCTION trigf();
103
104    Now you can test the operation of the trigger:
105 => INSERT INTO ttest VALUES (NULL);
106 INFO:  trigf (fired before): there are 0 rows in ttest
107 INSERT 0 0
108
109 -- Insertion skipped and AFTER trigger is not fired
110
111 => SELECT * FROM ttest;
112  x
113 ---
114 (0 rows)
115
116 => INSERT INTO ttest VALUES (1);
117 INFO:  trigf (fired before): there are 0 rows in ttest
118 INFO:  trigf (fired after ): there are 1 rows in ttest
119                                        ^^^^^^^^
120                              remember what we said about visibility.
121 INSERT 167793 1
122 vac=> SELECT * FROM ttest;
123  x
124 ---
125  1
126 (1 row)
127
128 => INSERT INTO ttest SELECT x * 2 FROM ttest;
129 INFO:  trigf (fired before): there are 1 rows in ttest
130 INFO:  trigf (fired after ): there are 2 rows in ttest
131                                        ^^^^^^
132                              remember what we said about visibility.
133 INSERT 167794 1
134 => SELECT * FROM ttest;
135  x
136 ---
137  1
138  2
139 (2 rows)
140
141 => UPDATE ttest SET x = NULL WHERE x = 2;
142 INFO:  trigf (fired before): there are 2 rows in ttest
143 UPDATE 0
144 => UPDATE ttest SET x = 4 WHERE x = 2;
145 INFO:  trigf (fired before): there are 2 rows in ttest
146 INFO:  trigf (fired after ): there are 2 rows in ttest
147 UPDATE 1
148 vac=> SELECT * FROM ttest;
149  x
150 ---
151  1
152  4
153 (2 rows)
154
155 => DELETE FROM ttest;
156 INFO:  trigf (fired before): there are 2 rows in ttest
157 INFO:  trigf (fired before): there are 1 rows in ttest
158 INFO:  trigf (fired after ): there are 0 rows in ttest
159 INFO:  trigf (fired after ): there are 0 rows in ttest
160                                        ^^^^^^
161                              remember what we said about visibility.
162 DELETE 2
163 => SELECT * FROM ttest;
164  x
165 ---
166 (0 rows)
167
168    There are more complex examples in src/test/regress/regress.c and in
169    spi.