PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
tcn.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "executor/spi.h"
#include "commands/async.h"
#include "commands/trigger.h"
#include "lib/stringinfo.h"
#include "utils/rel.h"
#include "utils/syscache.h"
Include dependency graph for tcn.c:

Go to the source code of this file.

Functions

static void strcpy_quoted (StringInfo r, const char *s, const char q)
 
 PG_FUNCTION_INFO_V1 (triggered_change_notification)
 
Datum triggered_change_notification (PG_FUNCTION_ARGS)
 

Variables

 PG_MODULE_MAGIC
 

Function Documentation

PG_FUNCTION_INFO_V1 ( triggered_change_notification  )
static void strcpy_quoted ( StringInfo  r,
const char *  s,
const char  q 
)
static

Definition at line 33 of file tcn.c.

References appendStringInfoCharMacro.

Referenced by triggered_change_notification().

34 {
36  while (*s)
37  {
38  if (*s == q)
41  s++;
42  }
44 }
#define appendStringInfoCharMacro(str, ch)
Definition: stringinfo.h:135
Datum triggered_change_notification ( PG_FUNCTION_ARGS  )

Definition at line 56 of file tcn.c.

References appendStringInfoCharMacro, Async_Notify(), CALLED_AS_TRIGGER, elog, ereport, errcode(), errmsg(), ERROR, GETSTRUCT, HeapTupleIsValid, i, IndexIsValid, INDEXRELID, lfirst_oid, list_free(), makeStringInfo(), NameStr, NULL, ObjectIdGetDatum, PointerGetDatum, RelationData::rd_att, RelationGetIndexList(), RelationGetRelationName, ReleaseSysCache(), SearchSysCache1, SPI_getvalue(), strcpy_quoted(), TriggerData::tg_event, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, Trigger::tgnargs, TRIGGER_FIRED_AFTER, TRIGGER_FIRED_BY_DELETE, TRIGGER_FIRED_BY_INSERT, TRIGGER_FIRED_BY_UPDATE, and TRIGGER_FIRED_FOR_ROW.

57 {
58  TriggerData *trigdata = (TriggerData *) fcinfo->context;
59  Trigger *trigger;
60  int nargs;
61  HeapTuple trigtuple;
62  Relation rel;
63  TupleDesc tupdesc;
64  char *channel;
65  char operation;
66  StringInfo payload = makeStringInfo();
67  bool foundPK;
68 
69  List *indexoidlist;
70  ListCell *indexoidscan;
71 
72  /* make sure it's called as a trigger */
73  if (!CALLED_AS_TRIGGER(fcinfo))
74  ereport(ERROR,
75  (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
76  errmsg("triggered_change_notification: must be called as trigger")));
77 
78  /* and that it's called after the change */
79  if (!TRIGGER_FIRED_AFTER(trigdata->tg_event))
80  ereport(ERROR,
81  (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
82  errmsg("triggered_change_notification: must be called after the change")));
83 
84  /* and that it's called for each row */
85  if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
86  ereport(ERROR,
87  (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
88  errmsg("triggered_change_notification: must be called for each row")));
89 
90  if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
91  operation = 'I';
92  else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
93  operation = 'U';
94  else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
95  operation = 'D';
96  else
97  {
98  elog(ERROR, "triggered_change_notification: trigger fired by unrecognized operation");
99  operation = 'X'; /* silence compiler warning */
100  }
101 
102  trigger = trigdata->tg_trigger;
103  nargs = trigger->tgnargs;
104  if (nargs > 1)
105  ereport(ERROR,
106  (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
107  errmsg("triggered_change_notification: must not be called with more than one parameter")));
108 
109  if (nargs == 0)
110  channel = "tcn";
111  else
112  channel = trigger->tgargs[0];
113 
114  /* get tuple data */
115  trigtuple = trigdata->tg_trigtuple;
116  rel = trigdata->tg_relation;
117  tupdesc = rel->rd_att;
118 
119  foundPK = false;
120 
121  /*
122  * Get the list of index OIDs for the table from the relcache, and look up
123  * each one in the pg_index syscache until we find one marked primary key
124  * (hopefully there isn't more than one such).
125  */
126  indexoidlist = RelationGetIndexList(rel);
127 
128  foreach(indexoidscan, indexoidlist)
129  {
130  Oid indexoid = lfirst_oid(indexoidscan);
131  HeapTuple indexTuple;
133 
134  indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
135  if (!HeapTupleIsValid(indexTuple)) /* should not happen */
136  elog(ERROR, "cache lookup failed for index %u", indexoid);
137  index = (Form_pg_index) GETSTRUCT(indexTuple);
138  /* we're only interested if it is the primary key and valid */
139  if (index->indisprimary && IndexIsValid(index))
140  {
141  int numatts = index->indnatts;
142 
143  if (numatts > 0)
144  {
145  int i;
146 
147  foundPK = true;
148 
149  strcpy_quoted(payload, RelationGetRelationName(rel), '"');
150  appendStringInfoCharMacro(payload, ',');
151  appendStringInfoCharMacro(payload, operation);
152 
153  for (i = 0; i < numatts; i++)
154  {
155  int colno = index->indkey.values[i];
156 
157  appendStringInfoCharMacro(payload, ',');
158  strcpy_quoted(payload, NameStr((tupdesc->attrs[colno - 1])->attname), '"');
159  appendStringInfoCharMacro(payload, '=');
160  strcpy_quoted(payload, SPI_getvalue(trigtuple, tupdesc, colno), '\'');
161  }
162 
163  Async_Notify(channel, payload->data);
164  }
165  ReleaseSysCache(indexTuple);
166  break;
167  }
168  ReleaseSysCache(indexTuple);
169  }
170 
171  list_free(indexoidlist);
172 
173  if (!foundPK)
174  ereport(ERROR,
175  (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
176  errmsg("triggered_change_notification: must be called on a table with a primary key")));
177 
178  return PointerGetDatum(NULL); /* after trigger; value doesn't matter */
179 }
#define GETSTRUCT(TUP)
Definition: htup_details.h:656
#define IndexIsValid(indexForm)
Definition: pg_index.h:107
#define PointerGetDatum(X)
Definition: postgres.h:564
StringInfo makeStringInfo(void)
Definition: stringinfo.c:29
int errcode(int sqlerrcode)
Definition: elog.c:575
unsigned int Oid
Definition: postgres_ext.h:31
#define TRIGGER_FIRED_AFTER(event)
Definition: trigger.h:91
#define SearchSysCache1(cacheId, key1)
Definition: syscache.h:149
HeapTuple tg_trigtuple
Definition: trigger.h:35
Definition: type.h:90
char * SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
Definition: spi.c:803
#define appendStringInfoCharMacro(str, ch)
Definition: stringinfo.h:135
#define ObjectIdGetDatum(X)
Definition: postgres.h:515
#define ERROR
Definition: elog.h:43
static void strcpy_quoted(StringInfo r, const char *s, const char q)
Definition: tcn.c:33
#define RelationGetRelationName(relation)
Definition: rel.h:433
#define ereport(elevel, rest)
Definition: elog.h:122
FormData_pg_index * Form_pg_index
Definition: pg_index.h:67
#define TRIGGER_FIRED_BY_DELETE(event)
Definition: trigger.h:73
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1083
Trigger * tg_trigger
Definition: trigger.h:37
TupleDesc rd_att
Definition: rel.h:114
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#define NULL
Definition: c.h:226
#define CALLED_AS_TRIGGER(fcinfo)
Definition: trigger.h:25
TriggerEvent tg_event
Definition: trigger.h:33
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4336
void Async_Notify(const char *channel, const char *payload)
Definition: async.c:540
#define TRIGGER_FIRED_BY_INSERT(event)
Definition: trigger.h:70
int errmsg(const char *fmt,...)
Definition: elog.c:797
void list_free(List *list)
Definition: list.c:1133
int i
int16 tgnargs
Definition: reltrigger.h:37
#define NameStr(name)
Definition: c.h:495
#define elog
Definition: elog.h:219
#define TRIGGER_FIRED_FOR_ROW(event)
Definition: trigger.h:82
Definition: pg_list.h:45
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition: trigger.h:76
#define lfirst_oid(lc)
Definition: pg_list.h:108
Relation tg_relation
Definition: trigger.h:34

Variable Documentation

PG_MODULE_MAGIC

Definition at line 26 of file tcn.c.