Header And Logo

PostgreSQL
| The world's most advanced open source database.

Functions | Variables

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

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

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 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 */
}


Variable Documentation

Definition at line 27 of file tcn.c.