Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit01a8948

Browse files
committed
Trigger programming guide.
Description of data changes visibility added.
1 parentda707e4 commit01a8948

File tree

2 files changed

+360
-1
lines changed

2 files changed

+360
-1
lines changed

‎doc/spi.txt

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ int SPI_finish(void)
7171
with this - it means that nothing was made by SPI manager.
7272

7373
NOTE! SPI_finish() MUST be called by connected procedure or you may get
74-
unpredictable results!
74+
unpredictable results! But you are able to don't call SPI_finish() if you
75+
abort transaction (via elog(WARN)).
7576

7677

7778
int SPI_exec(char *query, int tcount)
@@ -354,6 +355,27 @@ allocate memory for this in upper context!
354355
this query is done!
355356

356357

358+
Data changes visibility
359+
360+
PostgreSQL data changes visibility rule: during query execution data
361+
changes made by query itself (via SQL-function, SPI-function, triggers)
362+
are invisible to the query scan.
363+
364+
For example, in query
365+
366+
INSERT INTO a SELECT * FROM a
367+
368+
tuples inserted are invisible for SELECT' scan.
369+
370+
But also note that
371+
372+
changes made by query Q are visible by queries which are started after
373+
query Q, no matter - are they started inside Q (during execution of Q) or
374+
after Q is done.
375+
376+
Last example of usage SPI function below demonstrates visibility rule.
377+
378+
357379
Examples
358380

359381
There are complex examples in contrib/spi and in
@@ -446,3 +468,43 @@ execq
446468
-----
447469
3 <<< 10 is max value only, 3 is real # of tuples
448470
(1 row)
471+
472+
vac=> delete from a;
473+
DELETE 3
474+
vac=> insert into a values (execq('select * from a', 0) + 1);
475+
INSERT 167712 1
476+
vac=> select * from a;
477+
x
478+
-
479+
1 <<< no tuples in a (0) + 1
480+
(1 row)
481+
482+
vac=> insert into a values (execq('select * from a', 0) + 1);
483+
NOTICE:EXECQ: 0
484+
INSERT 167713 1
485+
vac=> select * from a;
486+
x
487+
-
488+
1
489+
2 <<< there was single tuple in a + 1
490+
(2 rows)
491+
492+
-- This demonstrates data changes visibility rule:
493+
494+
vac=> insert into a select execq('select * from a', 0) * x from a;
495+
NOTICE:EXECQ: 1
496+
NOTICE:EXECQ: 2
497+
NOTICE:EXECQ: 1
498+
NOTICE:EXECQ: 2
499+
NOTICE:EXECQ: 2
500+
INSERT 0 2
501+
vac=> select * from a;
502+
x
503+
-
504+
1
505+
2
506+
2 <<< 2 tuples * 1 (x in first tuple)
507+
6 <<< 3 tuples (2 + 1 just inserted) * 2 (x in second tuple)
508+
(4 rows) ^^^^^^^^
509+
tuples visible to execq() in different invocations
510+

‎doc/trigger.txt

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
2+
PostgreSQL Trigger Programming Guide
3+
4+
For the lack of Procedural Language (PL) in current version of
5+
PostgreSQL, there is only ability to specify call to a C-function as trigger
6+
action.
7+
Also, STATEMENT-level trigger events are not supported in current
8+
version, and so you are only able to specify BEFORE | AFTER
9+
INSERT|DELETE|UPDATE of a tuple as trigger event.
10+
11+
If trigger event occures, trigger manager (called by Executor)
12+
initializes global structure TriggerData *CurrentTriggerData (described
13+
below) and calls trigger function to handle event.
14+
15+
Trigger function must be created before trigger creation as function
16+
not accepting any arguments and returns opaque.
17+
Actually, there are two specific features in triggers handling.
18+
19+
First, in CREATE TRIGGER one may specify arguments for trigger
20+
function (EXECUTE PROCEDURE tfunc (aa,'bb', 1)), and these arguments
21+
will be passed to trigger function in CurrentTriggerData.
22+
It allows to use single function for many triggers and process events in
23+
different ways.
24+
Also, function may be used for triggering different relations (these
25+
functions are named as "general trigger functions").
26+
27+
Second, trigger function has to return HeapTuple to upper Executor.
28+
No matter for triggers fired AFTER operation (INSERT, DELETE, UPDATE),
29+
but it allows to BEFORE triggers:
30+
- return NULL to skip operation for current tuple (and so tuple
31+
will not be inserted/updated/deleted);
32+
- return pointer to another tuple (INSERT and UPDATE only) which will be
33+
inserted (as new version of updated tuple if UPDATE) instead of
34+
original tuple.
35+
36+
Note, that there is no initialization performed by CREATE TRIGGER
37+
handler. It will be changed in the future.
38+
39+
Also, if more than one trigger defined for the same event on the same
40+
relation then order of trigger firing is unpredictable. It may be changed in
41+
the future.
42+
43+
Also, if a trigger function executes SQL-queries (using SPI) then these
44+
queries may fire triggers again. This is known as cascading of triggers.
45+
There is no explicit limitation for number of cascade levels.
46+
If a trigger is fired by INSERT and inserts new tuple in the same
47+
relation then this trigger will be fired again. Currently, there is nothing
48+
provided for synchronization (etc) of these cases. It may be changed. At
49+
the moment, there is function funny_dup17() in the regress tests which uses
50+
some technics to stop recursion (cascading) of itself...
51+
52+
53+
Interaction with trigger manager
54+
55+
As it's mentioned above when function is called by trigger manager
56+
structure TriggerData *CurrentTriggerData is NOT NULL and initialized. And
57+
so, it's better to check CurrentTriggerData against being NULL in the
58+
begining and set it to NULL just after fetching information - to prevent
59+
calls to trigger function not from trigger manager.
60+
61+
struct TriggerData is defined in src/include/commands/trigger.h:
62+
63+
typedef struct TriggerData
64+
{
65+
TriggerEventtg_event;
66+
Relationtg_relation;
67+
HeapTupletg_trigtuple;
68+
HeapTupletg_newtuple;
69+
Trigger*tg_trigger;
70+
} TriggerData;
71+
72+
tg_event
73+
describes event for what function is called. You may use macros
74+
to deal with tg_event:
75+
76+
TRIGGER_FIRED_BEFORE(event) returns TRUE if trigger fired BEFORE;
77+
TRIGGER_FIRED_AFTER(event) returns TRUE if trigger fired AFTER;
78+
TRIGGER_FIRED_FOR_ROW(event) returns TRUE if trigger fired for
79+
ROW-level event;
80+
TRIGGER_FIRED_FOR_STATEMENT(event) returns TRUE if trigger fired for
81+
STATEMENT-level event;
82+
TRIGGER_FIRED_BY_INSERT(event) returns TRUE if trigger fired by INSERT;
83+
TRIGGER_FIRED_BY_DELETE(event) returns TRUE if trigger fired by DELETE;
84+
TRIGGER_FIRED_BY_UPDATE(event) returns TRUE if trigger fired by UPDATE.
85+
86+
tg_relation
87+
is pointer to structure describing triggered relation. Look @
88+
src/include/utils/rel.h about this structure. The most interest things
89+
are tg_relation->rd_att (descriptor of relation tuples) and
90+
tg_relation->rd_rel->relname (relation' name. This is not char*, but
91+
NameData - use SPI_getrelname(tg_relation) to get char* to copy of name).
92+
93+
tg_trigtuple
94+
is tuple (pointer) for which trigger is fired. This is tuple to being
95+
inserted (if INSERT), deleted (if DELETE) or updated (if UPDATE).
96+
If INSERT/DELETE then this is what you are to return to Executor if
97+
you don't want to replace tuple with another one (INSERT) or skip
98+
operation.
99+
100+
tg_newtuple
101+
is pointer to new version of tuple if UPDATE and NULL if INSERT/DELETE.
102+
This is what you are to return to Executor if UPDATE and you don't want
103+
to replace tuple with another one or skip operation.
104+
105+
tg_trigger
106+
is pointer to structure Trigger defined in src/include/utils/rel.h:
107+
108+
typedef struct Trigger
109+
{
110+
char*tgname;
111+
Oidtgfoid;
112+
func_ptrtgfunc;
113+
int16tgtype;
114+
int16tgnargs;
115+
int16tgattr[8];
116+
char**tgargs;
117+
} Trigger;
118+
119+
tgname is trigger' name, tgnargs is number of arguments in tgargs, tgargs
120+
is array of pointers to arguments specified in CREATE TRIGGER. Other
121+
members are for internal use.
122+
123+
124+
Data changes visibility
125+
126+
PostgreSQL data changes visibility rule: during query execution data
127+
changes made by query itself (via SQL-function, SPI-function, triggers)
128+
are invisible to the query scan.
129+
130+
For example, in query
131+
132+
INSERT INTO a SELECT * FROM a
133+
134+
tuples inserted are invisible for SELECT' scan.
135+
136+
But keep in mind notices about visibility in SPI documentation:
137+
138+
changes made by query Q are visible by queries which are started after
139+
query Q, no matter - are they started inside Q (during execution of Q) or
140+
after Q is done.
141+
142+
This is true for triggers as well. And so, though tuple being inserted
143+
(tg_trigtuple) is not visible to queries in BEFORE trigger, this tuple (just
144+
inserted) is visible to queries in AFTER trigger, and to queries in
145+
BEFORE/AFTER triggers fired after this!
146+
147+
148+
Examples
149+
150+
There are complex examples in contrib/spi and in
151+
src/test/regress/regress.c.
152+
153+
This is very simple example of trigger usage. Function trigf reports
154+
about number of tuples in triggered relation ttest and in trigger fired
155+
BEFORE INSERT/UPDATE checks against is attribute x NULL and skips operations
156+
for NULLs (ala NOT NULL implementation using triggers without aborting
157+
transaction if NULL).
158+
159+
----------------------------------------------------------------------------
160+
#include "executor/spi.h"/* this is what you need to work with SPI */
161+
#include "commands/trigger.h"/* -"- and triggers */
162+
163+
HeapTupletrigf(void);
164+
165+
HeapTuple
166+
trigf()
167+
{
168+
TupleDesctupdesc;
169+
HeapTuplerettuple;
170+
char*when;
171+
boolchecknull = false;
172+
boolisnull;
173+
intret, i;
174+
175+
if (!CurrentTriggerData)
176+
elog(WARN, "trigf: triggers are not initialized");
177+
178+
/* tuple to return to Executor */
179+
if (TRIGGER_FIRED_BY_UPDATE(CurrentTriggerData->tg_event))
180+
rettuple = CurrentTriggerData->tg_newtuple;
181+
else
182+
rettuple = CurrentTriggerData->tg_trigtuple;
183+
184+
/* check for NULLs ? */
185+
if (!TRIGGER_FIRED_BY_DELETE(CurrentTriggerData->tg_event) &&
186+
TRIGGER_FIRED_BEFORE(CurrentTriggerData->tg_event))
187+
checknull = true;
188+
189+
if (TRIGGER_FIRED_BEFORE(CurrentTriggerData->tg_event))
190+
when = "before";
191+
else
192+
when = "after ";
193+
194+
tupdesc = CurrentTriggerData->tg_relation->rd_att;
195+
CurrentTriggerData = NULL;
196+
197+
/* Connect to SPI manager */
198+
if ((ret = SPI_connect()) < 0)
199+
elog(WARN, "trigf (fired %s): SPI_connect returned %d", when, ret);
200+
201+
/* Get number of tuples in relation */
202+
ret = SPI_exec("select count(*) from ttest", 0);
203+
204+
if (ret < 0)
205+
elog(WARN, "trigf (fired %s): SPI_exec returned %d", when, ret);
206+
207+
i = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull);
208+
209+
elog (NOTICE, "trigf (fired %s): there are %d tuples in ttest", when, i);
210+
211+
SPI_finish();
212+
213+
if (checknull)
214+
{
215+
i = SPI_getbinval(rettuple, tupdesc, 1, &isnull);
216+
if (isnull)
217+
rettuple = NULL;
218+
}
219+
220+
return (rettuple);
221+
}
222+
----------------------------------------------------------------------------
223+
224+
Now, compile and
225+
create table ttest (x int4);
226+
create function trigf () returns opaque as
227+
'...path_to_so' language 'c';
228+
229+
vac=> create trigger tbefore before insert or update or delete on ttest
230+
for each row execute procedure trigf();
231+
CREATE
232+
vac=> create trigger tafter after insert or update or delete on ttest
233+
for each row execute procedure trigf();
234+
CREATE
235+
vac=> insert into ttest values (null);
236+
NOTICE:trigf (fired before): there are 0 tuples in ttest
237+
INSERT 0 0
238+
239+
-- Insertion skipped and AFTER trigger is not fired
240+
241+
vac=> select * from ttest;
242+
x
243+
-
244+
(0 rows)
245+
246+
vac=> insert into ttest values (1);
247+
NOTICE:trigf (fired before): there are 0 tuples in ttest
248+
NOTICE:trigf (fired after ): there are 1 tuples in ttest
249+
^^^^^^^^
250+
remember about visibility
251+
INSERT 167793 1
252+
vac=> select * from ttest;
253+
x
254+
-
255+
1
256+
(1 row)
257+
258+
vac=> insert into ttest select x * 2 from ttest;
259+
NOTICE:trigf (fired before): there are 1 tuples in ttest
260+
NOTICE:trigf (fired after ): there are 2 tuples in ttest
261+
^^^^^^^^
262+
remember about visibility
263+
INSERT 167794 1
264+
vac=> select * from ttest;
265+
x
266+
-
267+
1
268+
2
269+
(2 rows)
270+
271+
vac=> update ttest set x = null where x = 2;
272+
NOTICE:trigf (fired before): there are 2 tuples in ttest
273+
UPDATE 0
274+
vac=> update ttest set x = 4 where x = 2;
275+
NOTICE:trigf (fired before): there are 2 tuples in ttest
276+
NOTICE:trigf (fired after ): there are 2 tuples in ttest
277+
UPDATE 1
278+
vac=> select * from ttest;
279+
x
280+
-
281+
1
282+
4
283+
(2 rows)
284+
285+
vac=> delete from ttest;
286+
NOTICE:trigf (fired before): there are 2 tuples in ttest
287+
NOTICE:trigf (fired after ): there are 1 tuples in ttest
288+
NOTICE:trigf (fired before): there are 1 tuples in ttest
289+
NOTICE:trigf (fired after ): there are 0 tuples in ttest
290+
^^^^^^^^
291+
remember about visibility
292+
DELETE 2
293+
vac=> select * from ttest;
294+
x
295+
-
296+
(0 rows)
297+

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp