#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"
Go to the source code of this file.
Functions | |
Datum | triggered_change_notification (PG_FUNCTION_ARGS) |
static void | strcpy_quoted (StringInfo r, const char *s, const char q) |
PG_FUNCTION_INFO_V1 (triggered_change_notification) | |
Variables | |
PG_MODULE_MAGIC |
PG_FUNCTION_INFO_V1 | ( | triggered_change_notification | ) |
static void strcpy_quoted | ( | StringInfo | r, | |
const char * | s, | |||
const char | q | |||
) | [static] |
Definition at line 39 of file tcn.c.
References appendStringInfoCharMacro.
Referenced by triggered_change_notification().
{ appendStringInfoCharMacro(r, q); while (*s) { if (*s == q) appendStringInfoCharMacro(r, q); appendStringInfoCharMacro(r, *s); s++; } appendStringInfoCharMacro(r, q); }
Datum triggered_change_notification | ( | PG_FUNCTION_ARGS | ) |
Definition at line 62 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.
{ TriggerData *trigdata = (TriggerData *) fcinfo->context; Trigger *trigger; int nargs; HeapTuple trigtuple; Relation rel; TupleDesc tupdesc; char *channel; char operation; StringInfo payload = makeStringInfo(); bool foundPK; List *indexoidlist; ListCell *indexoidscan; /* make sure it's called as a trigger */ if (!CALLED_AS_TRIGGER(fcinfo)) ereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), errmsg("triggered_change_notification: must be called as trigger"))); /* and that it's called after the change */ if (!TRIGGER_FIRED_AFTER(trigdata->tg_event)) ereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), errmsg("triggered_change_notification: must be called after the change"))); /* and that it's called for each row */ if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) ereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), errmsg("triggered_change_notification: must be called for each row"))); if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) operation = 'I'; else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) operation = 'U'; else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) operation = 'D'; else { elog(ERROR, "triggered_change_notification: trigger fired by unrecognized operation"); operation = 'X'; /* silence compiler warning */ } trigger = trigdata->tg_trigger; nargs = trigger->tgnargs; if (nargs > 1) ereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), errmsg("triggered_change_notification: must not be called with more than one parameter"))); if (nargs == 0) channel = "tcn"; else channel = trigger->tgargs[0]; /* get tuple data */ trigtuple = trigdata->tg_trigtuple; rel = trigdata->tg_relation; tupdesc = rel->rd_att; foundPK = false; /* * Get the list of index OIDs for the table from the relcache, and look up * each one in the pg_index syscache until we find one marked primary key * (hopefully there isn't more than one such). */ indexoidlist = RelationGetIndexList(rel); foreach(indexoidscan, indexoidlist) { Oid indexoid = lfirst_oid(indexoidscan); HeapTuple indexTuple; Form_pg_index index; indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid)); if (!HeapTupleIsValid(indexTuple)) /* should not happen */ elog(ERROR, "cache lookup failed for index %u", indexoid); index = (Form_pg_index) GETSTRUCT(indexTuple); /* we're only interested if it is the primary key and valid */ if (index->indisprimary && IndexIsValid(index)) { int numatts = index->indnatts; if (numatts > 0) { int i; foundPK = true; strcpy_quoted(payload, RelationGetRelationName(rel), '"'); appendStringInfoCharMacro(payload, ','); appendStringInfoCharMacro(payload, operation); for (i = 0; i < numatts; i++) { int colno = index->indkey.values[i]; appendStringInfoCharMacro(payload, ','); strcpy_quoted(payload, NameStr((tupdesc->attrs[colno - 1])->attname), '"'); appendStringInfoCharMacro(payload, '='); strcpy_quoted(payload, SPI_getvalue(trigtuple, tupdesc, colno), '\''); } Async_Notify(channel, payload->data); } ReleaseSysCache(indexTuple); break; } ReleaseSysCache(indexTuple); } list_free(indexoidlist); if (!foundPK) ereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), errmsg("triggered_change_notification: must be called on a table with a primary key"))); return PointerGetDatum(NULL); /* after trigger; value doesn't matter */ }