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

Commit6e3323d

Browse files
committed
Triggered change notifications.
Kevin Grittner, reviewed (in earlier versions) by Álvaro Herrera
1 parentc8397bd commit6e3323d

File tree

8 files changed

+290
-0
lines changed

8 files changed

+290
-0
lines changed

‎contrib/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ SUBDIRS = \
4545
seg\
4646
spi\
4747
tablefunc\
48+
tcn\
4849
test_parser\
4950
tsearch2\
5051
unaccent\

‎contrib/tcn/Makefile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# contrib/tcn/Makefile
2+
3+
MODULES = tcn
4+
5+
EXTENSION = tcn
6+
DATA = tcn--1.0.sql
7+
8+
ifdefUSE_PGXS
9+
PG_CONFIG = pg_config
10+
PGXS :=$(shell$(PG_CONFIG) --pgxs)
11+
include$(PGXS)
12+
else
13+
subdir = contrib/tcn
14+
top_builddir = ../..
15+
include$(top_builddir)/src/Makefile.global
16+
include$(top_srcdir)/contrib/contrib-global.mk
17+
endif

‎contrib/tcn/tcn--1.0.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/* contrib/tcn/tcn--1.0.sql*/
2+
3+
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
4+
\echo Use"CREATE EXTENSION tcn" to load this file. \quit
5+
6+
CREATEFUNCTIONtriggered_change_notification()
7+
RETURNSpg_catalog.trigger
8+
AS'MODULE_PATHNAME'
9+
LANGUAGE C;

‎contrib/tcn/tcn.c

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/*-------------------------------------------------------------------------
2+
*
3+
* tcn.c
4+
* triggered change notification support for PostgreSQL
5+
*
6+
* Portions Copyright (c) 2011-2012, PostgreSQL Global Development Group
7+
* Portions Copyright (c) 1994, Regents of the University of California
8+
*
9+
*
10+
* IDENTIFICATION
11+
* contrib/tcn/tcn.c
12+
*
13+
*-------------------------------------------------------------------------
14+
*/
15+
16+
#include"postgres.h"
17+
18+
#include"executor/spi.h"
19+
#include"commands/async.h"
20+
#include"commands/trigger.h"
21+
#include"lib/stringinfo.h"
22+
#include"utils/rel.h"
23+
#include"utils/syscache.h"
24+
25+
26+
PG_MODULE_MAGIC;
27+
28+
29+
/* forward declarations */
30+
Datumtriggered_change_notification(PG_FUNCTION_ARGS);
31+
32+
33+
/*
34+
* Copy from s (for source) to r (for result), wrapping with q (quote)
35+
* characters and doubling any quote characters found.
36+
*/
37+
staticvoid
38+
strcpy_quoted(StringInfor,constchar*s,constcharq)
39+
{
40+
appendStringInfoCharMacro(r,q);
41+
while (*s)
42+
{
43+
if (*s==q)
44+
appendStringInfoCharMacro(r,q);
45+
appendStringInfoCharMacro(r,*s);
46+
s++;
47+
}
48+
appendStringInfoCharMacro(r,q);
49+
}
50+
51+
/*
52+
* triggered_change_notification
53+
*
54+
* This trigger function will send a notification of data modification with
55+
* primary key values.The channel will be "tcn" unless the trigger is
56+
* created with a parameter, in which case that parameter will be used.
57+
*/
58+
PG_FUNCTION_INFO_V1(triggered_change_notification);
59+
60+
Datum
61+
triggered_change_notification(PG_FUNCTION_ARGS)
62+
{
63+
TriggerData*trigdata= (TriggerData*)fcinfo->context;
64+
Trigger*trigger;
65+
intnargs;
66+
HeapTupletrigtuple;
67+
Relationrel;
68+
TupleDesctupdesc;
69+
char*channel;
70+
charoperation;
71+
StringInfopayload=makeStringInfo();
72+
boolfoundPK;
73+
74+
List*indexoidlist;
75+
ListCell*indexoidscan;
76+
77+
/* make sure it's called as a trigger */
78+
if (!CALLED_AS_TRIGGER(fcinfo))
79+
ereport(ERROR,
80+
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
81+
errmsg("triggered_change_notification: must be called as trigger")));
82+
83+
/* and that it's called after the change */
84+
if (!TRIGGER_FIRED_AFTER(trigdata->tg_event))
85+
ereport(ERROR,
86+
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
87+
errmsg("triggered_change_notification: must be called after the change")));
88+
89+
/* and that it's called for each row */
90+
if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
91+
ereport(ERROR,
92+
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
93+
errmsg("triggered_change_notification: must be called for each row")));
94+
95+
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
96+
operation='I';
97+
elseif (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
98+
operation='U';
99+
elseif (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
100+
operation='D';
101+
else
102+
{
103+
elog(ERROR,"triggered_change_notification: trigger fired by unrecognized operation");
104+
operation='X';/* silence compiler warning */
105+
}
106+
107+
trigger=trigdata->tg_trigger;
108+
nargs=trigger->tgnargs;
109+
if (nargs>1)
110+
ereport(ERROR,
111+
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
112+
errmsg("triggered_change_notification: must not be called with more than one parameter")));
113+
114+
if (nargs==0)
115+
channel="tcn";
116+
else
117+
channel=trigger->tgargs[0];
118+
119+
/* get tuple data */
120+
trigtuple=trigdata->tg_trigtuple;
121+
rel=trigdata->tg_relation;
122+
tupdesc=rel->rd_att;
123+
124+
foundPK= false;
125+
126+
/*
127+
* Get the list of index OIDs for the table from the relcache, and look up
128+
* each one in the pg_index syscache until we find one marked primary key
129+
* (hopefully there isn't more than one such).
130+
*/
131+
indexoidlist=RelationGetIndexList(rel);
132+
133+
foreach(indexoidscan,indexoidlist)
134+
{
135+
Oidindexoid=lfirst_oid(indexoidscan);
136+
HeapTupleindexTuple;
137+
Form_pg_indexindex;
138+
139+
indexTuple=SearchSysCache1(INDEXRELID,ObjectIdGetDatum(indexoid));
140+
if (!HeapTupleIsValid(indexTuple))/* should not happen */
141+
elog(ERROR,"cache lookup failed for index %u",indexoid);
142+
index= (Form_pg_index)GETSTRUCT(indexTuple);
143+
/* we're only interested if it is the primary key */
144+
if (index->indisprimary)
145+
{
146+
intnumatts=index->indnatts;
147+
148+
if (numatts>0)
149+
{
150+
inti;
151+
152+
foundPK= true;
153+
154+
strcpy_quoted(payload,RelationGetRelationName(rel),'"');
155+
appendStringInfoCharMacro(payload,',');
156+
appendStringInfoCharMacro(payload,operation);
157+
158+
for (i=0;i<numatts;i++)
159+
{
160+
intcolno=index->indkey.values[i];
161+
162+
appendStringInfoCharMacro(payload,',');
163+
strcpy_quoted(payload,NameStr((tupdesc->attrs[colno-1])->attname),'"');
164+
appendStringInfoCharMacro(payload,'=');
165+
strcpy_quoted(payload,SPI_getvalue(trigtuple,tupdesc,colno),'\'');
166+
}
167+
168+
Async_Notify(channel,payload->data);
169+
}
170+
ReleaseSysCache(indexTuple);
171+
break;
172+
}
173+
ReleaseSysCache(indexTuple);
174+
}
175+
176+
list_free(indexoidlist);
177+
178+
if (!foundPK)
179+
ereport(ERROR,
180+
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
181+
errmsg("triggered_change_notification: must be called on a table with a primary key")));
182+
183+
returnPointerGetDatum(NULL);/* after trigger; value doesn't matter */
184+
}

‎contrib/tcn/tcn.control

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# tcn extension
2+
comment = 'Triggered change notifications'
3+
default_version = '1.0'
4+
module_pathname = '$libdir/tcn'
5+
relocatable = true

‎doc/src/sgml/contrib.sgml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ CREATE EXTENSION <replaceable>module_name</> FROM unpackaged;
128128
&contrib-spi;
129129
&sslinfo;
130130
&tablefunc;
131+
&tcn;
131132
&test-parser;
132133
&tsearch2;
133134
&unaccent;

‎doc/src/sgml/filelist.sgml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@
136136
<!ENTITY sepgsql SYSTEM "sepgsql.sgml">
137137
<!ENTITY sslinfo SYSTEM "sslinfo.sgml">
138138
<!ENTITY tablefunc SYSTEM "tablefunc.sgml">
139+
<!ENTITY tcn SYSTEM "tcn.sgml">
139140
<!ENTITY test-parser SYSTEM "test-parser.sgml">
140141
<!ENTITY tsearch2 SYSTEM "tsearch2.sgml">
141142
<!ENTITY unaccent SYSTEM "unaccent.sgml">

‎doc/src/sgml/tcn.sgml

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<!-- doc/src/sgml/tcn.sgml -->
2+
3+
<sect1 id="tcn" xreflabel="tcn">
4+
<title>tcn</title>
5+
6+
<indexterm zone="tcn">
7+
<primary>tcn</primary>
8+
</indexterm>
9+
10+
<indexterm zone="tcn">
11+
<primary>triggered_change_notification</primary>
12+
</indexterm>
13+
14+
<para>
15+
The <filename>tcn</> module provides a trigger function that notifies
16+
listeners of changes to any table on which it is attached. It must be
17+
used as an <literal>AFTER</> trigger <literal>FOR EACH ROW</>.
18+
</para>
19+
20+
<para>
21+
Only one parameter may be suupplied to the function in a
22+
<literal>CREATE TRIGGER</> statement, and that is optional. If supplied
23+
it will be used for the channel name for the notifications. If omitted
24+
<literal>tcn</> will be used for the channel name.
25+
</para>
26+
27+
<para>
28+
The payload of the notifications consists of the table name, a letter to
29+
indicate which type of operation was performed, and column name/value pairs
30+
for primary key columns. Each part is separated from the next by a comma.
31+
For ease of parsing using regular expressions, table and column names are
32+
always wrapped in double quotes, and data values are always wrapped in
33+
single quotes. Embeded quotes are doubled.
34+
</para>
35+
36+
<para>
37+
A brief example of using the extension follows.
38+
39+
<programlisting>
40+
test=# create table tcndata
41+
test-# (
42+
test(# a int not null,
43+
test(# b date not null,
44+
test(# c text,
45+
test(# primary key (a, b)
46+
test(# );
47+
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "tcndata_pkey" for table "tcndata"
48+
CREATE TABLE
49+
test=# create trigger tcndata_tcn_trigger
50+
test-# after insert or update or delete on tcndata
51+
test-# for each row execute procedure triggered_change_notification();
52+
CREATE TRIGGER
53+
test=# listen tcn;
54+
LISTEN
55+
test=# insert into tcndata values (1, date '2012-12-22', 'one'),
56+
test-# (1, date '2012-12-23', 'another'),
57+
test-# (2, date '2012-12-23', 'two');
58+
INSERT 0 3
59+
Asynchronous notification "tcn" with payload ""tcndata",I,"a"='1',"b"='2012-12-22'" received from server process with PID 22770.
60+
Asynchronous notification "tcn" with payload ""tcndata",I,"a"='1',"b"='2012-12-23'" received from server process with PID 22770.
61+
Asynchronous notification "tcn" with payload ""tcndata",I,"a"='2',"b"='2012-12-23'" received from server process with PID 22770.
62+
test=# update tcndata set c = 'uno' where a = 1;
63+
UPDATE 2
64+
Asynchronous notification "tcn" with payload ""tcndata",U,"a"='1',"b"='2012-12-22'" received from server process with PID 22770.
65+
Asynchronous notification "tcn" with payload ""tcndata",U,"a"='1',"b"='2012-12-23'" received from server process with PID 22770.
66+
test=# delete from tcndata where a = 1 and b = date '2012-12-22';
67+
DELETE 1
68+
Asynchronous notification "tcn" with payload ""tcndata",D,"a"='1',"b"='2012-12-22'" received from server process with PID 22770.
69+
</programlisting>
70+
</para>
71+
72+
</sect1>

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp