Header And Logo

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

Data Structures | Defines | Typedefs | Functions | Variables

trigger.h File Reference

#include "nodes/execnodes.h"
#include "nodes/parsenodes.h"
Include dependency graph for trigger.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  TriggerData

Defines

#define CALLED_AS_TRIGGER(fcinfo)   ((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))
#define TRIGGER_EVENT_INSERT   0x00000000
#define TRIGGER_EVENT_DELETE   0x00000001
#define TRIGGER_EVENT_UPDATE   0x00000002
#define TRIGGER_EVENT_TRUNCATE   0x00000003
#define TRIGGER_EVENT_OPMASK   0x00000003
#define TRIGGER_EVENT_ROW   0x00000004
#define TRIGGER_EVENT_BEFORE   0x00000008
#define TRIGGER_EVENT_AFTER   0x00000000
#define TRIGGER_EVENT_INSTEAD   0x00000010
#define TRIGGER_EVENT_TIMINGMASK   0x00000018
#define AFTER_TRIGGER_DEFERRABLE   0x00000020
#define AFTER_TRIGGER_INITDEFERRED   0x00000040
#define TRIGGER_FIRED_BY_INSERT(event)   (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_INSERT)
#define TRIGGER_FIRED_BY_DELETE(event)   (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_DELETE)
#define TRIGGER_FIRED_BY_UPDATE(event)   (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_UPDATE)
#define TRIGGER_FIRED_BY_TRUNCATE(event)   (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_TRUNCATE)
#define TRIGGER_FIRED_FOR_ROW(event)   ((event) & TRIGGER_EVENT_ROW)
#define TRIGGER_FIRED_FOR_STATEMENT(event)   (!TRIGGER_FIRED_FOR_ROW(event))
#define TRIGGER_FIRED_BEFORE(event)   (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_BEFORE)
#define TRIGGER_FIRED_AFTER(event)   (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_AFTER)
#define TRIGGER_FIRED_INSTEAD(event)   (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_INSTEAD)
#define SESSION_REPLICATION_ROLE_ORIGIN   0
#define SESSION_REPLICATION_ROLE_REPLICA   1
#define SESSION_REPLICATION_ROLE_LOCAL   2
#define TRIGGER_FIRES_ON_ORIGIN   'O'
#define TRIGGER_FIRES_ALWAYS   'A'
#define TRIGGER_FIRES_ON_REPLICA   'R'
#define TRIGGER_DISABLED   'D'
#define RI_TRIGGER_PK   1
#define RI_TRIGGER_FK   2
#define RI_TRIGGER_NONE   0

Typedefs

typedef uint32 TriggerEvent
typedef struct TriggerData TriggerData

Functions

Oid CreateTrigger (CreateTrigStmt *stmt, const char *queryString, Oid constraintOid, Oid indexOid, bool isInternal)
void RemoveTriggerById (Oid trigOid)
Oid get_trigger_oid (Oid relid, const char *name, bool missing_ok)
Oid renametrig (RenameStmt *stmt)
void EnableDisableTrigger (Relation rel, const char *tgname, char fires_when, bool skip_system)
void RelationBuildTriggers (Relation relation)
TriggerDescCopyTriggerDesc (TriggerDesc *trigdesc)
void FreeTriggerDesc (TriggerDesc *trigdesc)
void ExecBSInsertTriggers (EState *estate, ResultRelInfo *relinfo)
void ExecASInsertTriggers (EState *estate, ResultRelInfo *relinfo)
TupleTableSlotExecBRInsertTriggers (EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
void ExecARInsertTriggers (EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, List *recheckIndexes)
TupleTableSlotExecIRInsertTriggers (EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
void ExecBSDeleteTriggers (EState *estate, ResultRelInfo *relinfo)
void ExecASDeleteTriggers (EState *estate, ResultRelInfo *relinfo)
bool ExecBRDeleteTriggers (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid)
void ExecARDeleteTriggers (EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid)
bool ExecIRDeleteTriggers (EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple)
void ExecBSUpdateTriggers (EState *estate, ResultRelInfo *relinfo)
void ExecASUpdateTriggers (EState *estate, ResultRelInfo *relinfo)
TupleTableSlotExecBRUpdateTriggers (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, TupleTableSlot *slot)
void ExecARUpdateTriggers (EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple newtuple, List *recheckIndexes)
TupleTableSlotExecIRUpdateTriggers (EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, TupleTableSlot *slot)
void ExecBSTruncateTriggers (EState *estate, ResultRelInfo *relinfo)
void ExecASTruncateTriggers (EState *estate, ResultRelInfo *relinfo)
void AfterTriggerBeginXact (void)
void AfterTriggerBeginQuery (void)
void AfterTriggerEndQuery (EState *estate)
void AfterTriggerFireDeferred (void)
void AfterTriggerEndXact (bool isCommit)
void AfterTriggerBeginSubXact (void)
void AfterTriggerEndSubXact (bool isCommit)
void AfterTriggerSetState (ConstraintsSetStmt *stmt)
bool AfterTriggerPendingOnRel (Oid relid)
bool RI_FKey_pk_upd_check_required (Trigger *trigger, Relation pk_rel, HeapTuple old_row, HeapTuple new_row)
bool RI_FKey_fk_upd_check_required (Trigger *trigger, Relation fk_rel, HeapTuple old_row, HeapTuple new_row)
bool RI_Initial_Check (Trigger *trigger, Relation fk_rel, Relation pk_rel)
int RI_FKey_trigger_type (Oid tgfoid)
Datum pg_trigger_depth (PG_FUNCTION_ARGS)

Variables

PGDLLIMPORT int SessionReplicationRole

Define Documentation

#define AFTER_TRIGGER_DEFERRABLE   0x00000020

Definition at line 64 of file trigger.h.

Referenced by afterTriggerCheckState(), and AfterTriggerSaveEvent().

#define AFTER_TRIGGER_INITDEFERRED   0x00000040

Definition at line 65 of file trigger.h.

Referenced by afterTriggerCheckState(), and AfterTriggerSaveEvent().

#define CALLED_AS_TRIGGER (   fcinfo  )     ((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))
#define RI_TRIGGER_FK   2

Definition at line 203 of file trigger.h.

Referenced by AfterTriggerSaveEvent().

#define RI_TRIGGER_NONE   0

Definition at line 204 of file trigger.h.

Referenced by AfterTriggerSaveEvent(), and CreateTrigger().

#define RI_TRIGGER_PK   1

Definition at line 202 of file trigger.h.

Referenced by AfterTriggerSaveEvent().

#define SESSION_REPLICATION_ROLE_LOCAL   2

Definition at line 99 of file trigger.h.

#define SESSION_REPLICATION_ROLE_ORIGIN   0

Definition at line 97 of file trigger.h.

#define SESSION_REPLICATION_ROLE_REPLICA   1

Definition at line 98 of file trigger.h.

Referenced by filter_event_trigger(), matchLocks(), and TriggerEnabled().

#define TRIGGER_DISABLED   'D'

Definition at line 109 of file trigger.h.

Referenced by ATExecCmd(), BuildEventTriggerCache(), and TriggerEnabled().

#define TRIGGER_EVENT_AFTER   0x00000000

Definition at line 58 of file trigger.h.

#define TRIGGER_EVENT_BEFORE   0x00000008

Definition at line 57 of file trigger.h.

#define TRIGGER_EVENT_DELETE   0x00000001
#define TRIGGER_EVENT_INSERT   0x00000000
#define TRIGGER_EVENT_INSTEAD   0x00000010

Definition at line 59 of file trigger.h.

#define TRIGGER_EVENT_OPMASK   0x00000003

Definition at line 53 of file trigger.h.

Referenced by AfterTriggerExecute().

#define TRIGGER_EVENT_ROW   0x00000004
#define TRIGGER_EVENT_TIMINGMASK   0x00000018

Definition at line 60 of file trigger.h.

#define TRIGGER_EVENT_TRUNCATE   0x00000003

Definition at line 52 of file trigger.h.

Referenced by AfterTriggerSaveEvent(), ExecASTruncateTriggers(), and ExecBSTruncateTriggers().

#define TRIGGER_EVENT_UPDATE   0x00000002
#define TRIGGER_FIRED_AFTER (   event  )     (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_AFTER)
#define TRIGGER_FIRED_BEFORE (   event  )     (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_BEFORE)
#define TRIGGER_FIRED_BY_DELETE (   event  )     (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_DELETE)
#define TRIGGER_FIRED_BY_INSERT (   event  )     (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_INSERT)
#define TRIGGER_FIRED_BY_TRUNCATE (   event  )     (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_TRUNCATE)
#define TRIGGER_FIRED_BY_UPDATE (   event  )     (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_UPDATE)
#define TRIGGER_FIRED_FOR_ROW (   event  )     ((event) & TRIGGER_EVENT_ROW)
#define TRIGGER_FIRED_FOR_STATEMENT (   event  )     (!TRIGGER_FIRED_FOR_ROW(event))
#define TRIGGER_FIRED_INSTEAD (   event  )     (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_INSTEAD)
#define TRIGGER_FIRES_ALWAYS   'A'

Definition at line 107 of file trigger.h.

Referenced by ATExecCmd().

#define TRIGGER_FIRES_ON_ORIGIN   'O'
#define TRIGGER_FIRES_ON_REPLICA   'R'

Definition at line 108 of file trigger.h.

Referenced by ATExecCmd(), filter_event_trigger(), and TriggerEnabled().


Typedef Documentation

typedef struct TriggerData TriggerData

Definition at line 27 of file trigger.h.


Function Documentation

void AfterTriggerBeginQuery ( void   ) 

Definition at line 3716 of file trigger.c.

References Assert, AfterTriggerEventList::head, AfterTriggersData::maxquerydepth, NULL, AfterTriggersData::query_depth, AfterTriggersData::query_stack, repalloc(), AfterTriggerEventList::tail, and AfterTriggerEventList::tailfree.

Referenced by CopyFrom(), ExecuteTruncate(), and standard_ExecutorStart().

{
    AfterTriggerEventList *events;

    /* Must be inside a transaction */
    Assert(afterTriggers != NULL);

    /* Increase the query stack depth */
    afterTriggers->query_depth++;

    /*
     * Allocate more space in the query stack if needed.
     */
    if (afterTriggers->query_depth >= afterTriggers->maxquerydepth)
    {
        /* repalloc will keep the stack in the same context */
        int         new_alloc = afterTriggers->maxquerydepth * 2;

        afterTriggers->query_stack = (AfterTriggerEventList *)
            repalloc(afterTriggers->query_stack,
                     new_alloc * sizeof(AfterTriggerEventList));
        afterTriggers->maxquerydepth = new_alloc;
    }

    /* Initialize this query's list to empty */
    events = &afterTriggers->query_stack[afterTriggers->query_depth];
    events->head = NULL;
    events->tail = NULL;
    events->tailfree = NULL;
}

void AfterTriggerBeginSubXact ( void   ) 

Definition at line 3905 of file trigger.c.

References DEFTRIG_INITALLOC, AfterTriggersData::depth_stack, AfterTriggersData::events, AfterTriggersData::events_stack, AfterTriggersData::firing_counter, AfterTriggersData::firing_stack, GetCurrentTransactionNestLevel(), AfterTriggersData::maxtransdepth, MemoryContextSwitchTo(), NULL, palloc(), AfterTriggersData::query_depth, repalloc(), AfterTriggersData::state_stack, and TopTransactionContext.

Referenced by StartSubTransaction().

{
    int         my_level = GetCurrentTransactionNestLevel();

    /*
     * Ignore call if the transaction is in aborted state.  (Probably
     * shouldn't happen?)
     */
    if (afterTriggers == NULL)
        return;

    /*
     * Allocate more space in the stacks if needed.  (Note: because the
     * minimum nest level of a subtransaction is 2, we waste the first couple
     * entries of each array; not worth the notational effort to avoid it.)
     */
    while (my_level >= afterTriggers->maxtransdepth)
    {
        if (afterTriggers->maxtransdepth == 0)
        {
            MemoryContext old_cxt;

            old_cxt = MemoryContextSwitchTo(TopTransactionContext);

#define DEFTRIG_INITALLOC 8
            afterTriggers->state_stack = (SetConstraintState *)
                palloc(DEFTRIG_INITALLOC * sizeof(SetConstraintState));
            afterTriggers->events_stack = (AfterTriggerEventList *)
                palloc(DEFTRIG_INITALLOC * sizeof(AfterTriggerEventList));
            afterTriggers->depth_stack = (int *)
                palloc(DEFTRIG_INITALLOC * sizeof(int));
            afterTriggers->firing_stack = (CommandId *)
                palloc(DEFTRIG_INITALLOC * sizeof(CommandId));
            afterTriggers->maxtransdepth = DEFTRIG_INITALLOC;

            MemoryContextSwitchTo(old_cxt);
        }
        else
        {
            /* repalloc will keep the stacks in the same context */
            int         new_alloc = afterTriggers->maxtransdepth * 2;

            afterTriggers->state_stack = (SetConstraintState *)
                repalloc(afterTriggers->state_stack,
                         new_alloc * sizeof(SetConstraintState));
            afterTriggers->events_stack = (AfterTriggerEventList *)
                repalloc(afterTriggers->events_stack,
                         new_alloc * sizeof(AfterTriggerEventList));
            afterTriggers->depth_stack = (int *)
                repalloc(afterTriggers->depth_stack,
                         new_alloc * sizeof(int));
            afterTriggers->firing_stack = (CommandId *)
                repalloc(afterTriggers->firing_stack,
                         new_alloc * sizeof(CommandId));
            afterTriggers->maxtransdepth = new_alloc;
        }
    }

    /*
     * Push the current information into the stack.  The SET CONSTRAINTS state
     * is not saved until/unless changed.  Likewise, we don't make a
     * per-subtransaction event context until needed.
     */
    afterTriggers->state_stack[my_level] = NULL;
    afterTriggers->events_stack[my_level] = afterTriggers->events;
    afterTriggers->depth_stack[my_level] = afterTriggers->query_depth;
    afterTriggers->firing_stack[my_level] = afterTriggers->firing_counter;
}

void AfterTriggerBeginXact ( void   ) 

Definition at line 3670 of file trigger.c.

References Assert, AfterTriggersData::depth_stack, AfterTriggersData::event_cxt, AfterTriggersData::events, AfterTriggersData::events_stack, AfterTriggersData::firing_counter, AfterTriggersData::firing_stack, AfterTriggerEventList::head, AfterTriggersData::maxquerydepth, AfterTriggersData::maxtransdepth, MemoryContextAlloc(), NULL, AfterTriggersData::query_depth, AfterTriggersData::query_stack, SetConstraintStateCreate(), AfterTriggersData::state, AfterTriggersData::state_stack, AfterTriggerEventList::tail, AfterTriggerEventList::tailfree, and TopTransactionContext.

Referenced by StartTransaction().

{
    Assert(afterTriggers == NULL);

    /*
     * Build empty after-trigger state structure
     */
    afterTriggers = (AfterTriggers)
        MemoryContextAlloc(TopTransactionContext,
                           sizeof(AfterTriggersData));

    afterTriggers->firing_counter = (CommandId) 1;      /* mustn't be 0 */
    afterTriggers->state = SetConstraintStateCreate(8);
    afterTriggers->events.head = NULL;
    afterTriggers->events.tail = NULL;
    afterTriggers->events.tailfree = NULL;
    afterTriggers->query_depth = -1;

    /* We initialize the query stack to a reasonable size */
    afterTriggers->query_stack = (AfterTriggerEventList *)
        MemoryContextAlloc(TopTransactionContext,
                           8 * sizeof(AfterTriggerEventList));
    afterTriggers->maxquerydepth = 8;

    /* Context for events is created only when needed */
    afterTriggers->event_cxt = NULL;

    /* Subtransaction stack is empty until/unless needed */
    afterTriggers->state_stack = NULL;
    afterTriggers->events_stack = NULL;
    afterTriggers->depth_stack = NULL;
    afterTriggers->firing_stack = NULL;
    afterTriggers->maxtransdepth = 0;
}

void AfterTriggerEndQuery ( EState estate  ) 

Definition at line 3761 of file trigger.c.

References afterTriggerFreeEventList(), afterTriggerInvokeEvents(), afterTriggerMarkEvents(), Assert, AfterTriggersData::events, AfterTriggersData::firing_counter, NULL, AfterTriggersData::query_depth, and AfterTriggersData::query_stack.

Referenced by CopyFrom(), ExecuteTruncate(), and standard_ExecutorFinish().

{
    AfterTriggerEventList *events;

    /* Must be inside a transaction */
    Assert(afterTriggers != NULL);

    /* Must be inside a query, too */
    Assert(afterTriggers->query_depth >= 0);

    /*
     * Process all immediate-mode triggers queued by the query, and move the
     * deferred ones to the main list of deferred events.
     *
     * Notice that we decide which ones will be fired, and put the deferred
     * ones on the main list, before anything is actually fired.  This ensures
     * reasonably sane behavior if a trigger function does SET CONSTRAINTS ...
     * IMMEDIATE: all events we have decided to defer will be available for it
     * to fire.
     *
     * We loop in case a trigger queues more events at the same query level
     * (is that even possible?).  Be careful here: firing a trigger could
     * result in query_stack being repalloc'd, so we can't save its address
     * across afterTriggerInvokeEvents calls.
     *
     * If we find no firable events, we don't have to increment
     * firing_counter.
     */
    for (;;)
    {
        events = &afterTriggers->query_stack[afterTriggers->query_depth];
        if (afterTriggerMarkEvents(events, &afterTriggers->events, true))
        {
            CommandId   firing_id = afterTriggers->firing_counter++;

            /* OK to delete the immediate events after processing them */
            if (afterTriggerInvokeEvents(events, firing_id, estate, true))
                break;          /* all fired */
        }
        else
            break;
    }

    /* Release query-local storage for events */
    afterTriggerFreeEventList(&afterTriggers->query_stack[afterTriggers->query_depth]);

    afterTriggers->query_depth--;
}

void AfterTriggerEndSubXact ( bool  isCommit  ) 

Definition at line 3980 of file trigger.c.

References AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, afterTriggerFreeEventList(), afterTriggerRestoreEventList(), Assert, AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_firing_id, AfterTriggersData::depth_stack, AfterTriggersData::events, AfterTriggersData::events_stack, AfterTriggersData::firing_stack, for_each_event_chunk, GetCurrentTransactionNestLevel(), GetTriggerSharedData, AfterTriggersData::maxtransdepth, NULL, pfree(), AfterTriggersData::query_depth, AfterTriggersData::query_stack, AfterTriggersData::state, and AfterTriggersData::state_stack.

Referenced by AbortSubTransaction(), and CommitSubTransaction().

{
    int         my_level = GetCurrentTransactionNestLevel();
    SetConstraintState state;
    AfterTriggerEvent event;
    AfterTriggerEventChunk *chunk;
    CommandId   subxact_firing_id;

    /*
     * Ignore call if the transaction is in aborted state.  (Probably
     * unneeded)
     */
    if (afterTriggers == NULL)
        return;

    /*
     * Pop the prior state if needed.
     */
    if (isCommit)
    {
        Assert(my_level < afterTriggers->maxtransdepth);
        /* If we saved a prior state, we don't need it anymore */
        state = afterTriggers->state_stack[my_level];
        if (state != NULL)
            pfree(state);
        /* this avoids double pfree if error later: */
        afterTriggers->state_stack[my_level] = NULL;
        Assert(afterTriggers->query_depth ==
               afterTriggers->depth_stack[my_level]);
    }
    else
    {
        /*
         * Aborting.  It is possible subxact start failed before calling
         * AfterTriggerBeginSubXact, in which case we mustn't risk touching
         * stack levels that aren't there.
         */
        if (my_level >= afterTriggers->maxtransdepth)
            return;

        /*
         * Release any event lists from queries being aborted, and restore
         * query_depth to its pre-subxact value.
         */
        while (afterTriggers->query_depth > afterTriggers->depth_stack[my_level])
        {
            afterTriggerFreeEventList(&afterTriggers->query_stack[afterTriggers->query_depth]);
            afterTriggers->query_depth--;
        }
        Assert(afterTriggers->query_depth ==
               afterTriggers->depth_stack[my_level]);

        /*
         * Restore the global deferred-event list to its former length,
         * discarding any events queued by the subxact.
         */
        afterTriggerRestoreEventList(&afterTriggers->events,
                                     &afterTriggers->events_stack[my_level]);

        /*
         * Restore the trigger state.  If the saved state is NULL, then this
         * subxact didn't save it, so it doesn't need restoring.
         */
        state = afterTriggers->state_stack[my_level];
        if (state != NULL)
        {
            pfree(afterTriggers->state);
            afterTriggers->state = state;
        }
        /* this avoids double pfree if error later: */
        afterTriggers->state_stack[my_level] = NULL;

        /*
         * Scan for any remaining deferred events that were marked DONE or IN
         * PROGRESS by this subxact or a child, and un-mark them. We can
         * recognize such events because they have a firing ID greater than or
         * equal to the firing_counter value we saved at subtransaction start.
         * (This essentially assumes that the current subxact includes all
         * subxacts started after it.)
         */
        subxact_firing_id = afterTriggers->firing_stack[my_level];
        for_each_event_chunk(event, chunk, afterTriggers->events)
        {
            AfterTriggerShared evtshared = GetTriggerSharedData(event);

            if (event->ate_flags &
                (AFTER_TRIGGER_DONE | AFTER_TRIGGER_IN_PROGRESS))
            {
                if (evtshared->ats_firing_id >= subxact_firing_id)
                    event->ate_flags &=
                        ~(AFTER_TRIGGER_DONE | AFTER_TRIGGER_IN_PROGRESS);
            }
        }
    }
}

void AfterTriggerEndXact ( bool  isCommit  ) 

Definition at line 3882 of file trigger.c.

References AfterTriggersData::event_cxt, and MemoryContextDelete().

Referenced by AbortTransaction(), CommitTransaction(), and PrepareTransaction().

{
    /*
     * Forget everything we know about AFTER triggers.
     *
     * Since all the info is in TopTransactionContext or children thereof, we
     * don't really need to do anything to reclaim memory.  However, the
     * pending-events list could be large, and so it's useful to discard it as
     * soon as possible --- especially if we are aborting because we ran out
     * of memory for the list!
     */
    if (afterTriggers && afterTriggers->event_cxt)
        MemoryContextDelete(afterTriggers->event_cxt);

    afterTriggers = NULL;
}

void AfterTriggerFireDeferred ( void   ) 

Definition at line 3823 of file trigger.c.

References afterTriggerInvokeEvents(), afterTriggerMarkEvents(), Assert, AfterTriggersData::events, AfterTriggersData::firing_counter, GetTransactionSnapshot(), AfterTriggerEventList::head, NULL, PopActiveSnapshot(), PushActiveSnapshot(), and AfterTriggersData::query_depth.

Referenced by CommitTransaction(), and PrepareTransaction().

{
    AfterTriggerEventList *events;
    bool        snap_pushed = false;

    /* Must be inside a transaction */
    Assert(afterTriggers != NULL);

    /* ... but not inside a query */
    Assert(afterTriggers->query_depth == -1);

    /*
     * If there are any triggers to fire, make sure we have set a snapshot for
     * them to use.  (Since PortalRunUtility doesn't set a snap for COMMIT, we
     * can't assume ActiveSnapshot is valid on entry.)
     */
    events = &afterTriggers->events;
    if (events->head != NULL)
    {
        PushActiveSnapshot(GetTransactionSnapshot());
        snap_pushed = true;
    }

    /*
     * Run all the remaining triggers.  Loop until they are all gone, in case
     * some trigger queues more for us to do.
     */
    while (afterTriggerMarkEvents(events, NULL, false))
    {
        CommandId   firing_id = afterTriggers->firing_counter++;

        if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
            break;              /* all fired */
    }

    /*
     * We don't bother freeing the event list, since it will go away anyway
     * (and more efficiently than via pfree) in AfterTriggerEndXact.
     */

    if (snap_pushed)
        PopActiveSnapshot();
}

bool AfterTriggerPendingOnRel ( Oid  relid  ) 

Definition at line 4451 of file trigger.c.

References AFTER_TRIGGER_DONE, AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_relid, AfterTriggersData::events, for_each_event_chunk, GetTriggerSharedData, NULL, AfterTriggersData::query_depth, and AfterTriggersData::query_stack.

Referenced by CheckTableNotInUse().

{
    AfterTriggerEvent event;
    AfterTriggerEventChunk *chunk;
    int         depth;

    /* No-op if we aren't in a transaction.  (Shouldn't happen?) */
    if (afterTriggers == NULL)
        return false;

    /* Scan queued events */
    for_each_event_chunk(event, chunk, afterTriggers->events)
    {
        AfterTriggerShared evtshared = GetTriggerSharedData(event);

        /*
         * We can ignore completed events.  (Even if a DONE flag is rolled
         * back by subxact abort, it's OK because the effects of the TRUNCATE
         * or whatever must get rolled back too.)
         */
        if (event->ate_flags & AFTER_TRIGGER_DONE)
            continue;

        if (evtshared->ats_relid == relid)
            return true;
    }

    /*
     * Also scan events queued by incomplete queries.  This could only matter
     * if TRUNCATE/etc is executed by a function or trigger within an updating
     * query on the same relation, which is pretty perverse, but let's check.
     */
    for (depth = 0; depth <= afterTriggers->query_depth; depth++)
    {
        for_each_event_chunk(event, chunk, afterTriggers->query_stack[depth])
        {
            AfterTriggerShared evtshared = GetTriggerSharedData(event);

            if (event->ate_flags & AFTER_TRIGGER_DONE)
                continue;

            if (evtshared->ats_relid == relid)
                return true;
        }
    }

    return false;
}

void AfterTriggerSetState ( ConstraintsSetStmt stmt  ) 

Definition at line 4155 of file trigger.c.

References AccessShareLock, afterTriggerInvokeEvents(), afterTriggerMarkEvents(), SetConstraintStateData::all_isdeferred, SetConstraintStateData::all_isset, Anum_pg_constraint_conname, Anum_pg_constraint_connamespace, Anum_pg_trigger_tgconstraint, BTEqualStrategyNumber, RangeVar::catalogname, ConstraintNameNspIndexId, ConstraintRelationId, ConstraintsSetStmt::constraints, CStringGetDatum, ConstraintsSetStmt::deferred, elog, ereport, errcode(), errmsg(), ERROR, AfterTriggersData::events, fetch_search_path(), AfterTriggersData::firing_counter, get_database_name(), GetCurrentTransactionNestLevel(), GETSTRUCT, GetTransactionSnapshot(), heap_close, heap_open(), HeapTupleGetOid, HeapTupleIsValid, IsSubTransaction(), lappend_oid(), lfirst, lfirst_oid, list_free(), list_make1_oid, LookupExplicitNamespace(), MyDatabaseId, NIL, NULL, SetConstraintStateData::numstates, ObjectIdGetDatum, PopActiveSnapshot(), PushActiveSnapshot(), RangeVar::relname, ScanKeyInit(), RangeVar::schemaname, SetConstraintTriggerData::sct_tgisdeferred, SetConstraintTriggerData::sct_tgoid, SetConstraintStateAddItem(), SetConstraintStateCopy(), SnapshotNow, AfterTriggersData::state, AfterTriggersData::state_stack, systable_beginscan(), systable_endscan(), systable_getnext(), TriggerConstraintIndexId, TriggerRelationId, and SetConstraintStateData::trigstates.

Referenced by standard_ProcessUtility().

{
    int         my_level = GetCurrentTransactionNestLevel();

    /*
     * Ignore call if we aren't in a transaction.  (Shouldn't happen?)
     */
    if (afterTriggers == NULL)
        return;

    /*
     * If in a subtransaction, and we didn't save the current state already,
     * save it so it can be restored if the subtransaction aborts.
     */
    if (my_level > 1 &&
        afterTriggers->state_stack[my_level] == NULL)
    {
        afterTriggers->state_stack[my_level] =
            SetConstraintStateCopy(afterTriggers->state);
    }

    /*
     * Handle SET CONSTRAINTS ALL ...
     */
    if (stmt->constraints == NIL)
    {
        /*
         * Forget any previous SET CONSTRAINTS commands in this transaction.
         */
        afterTriggers->state->numstates = 0;

        /*
         * Set the per-transaction ALL state to known.
         */
        afterTriggers->state->all_isset = true;
        afterTriggers->state->all_isdeferred = stmt->deferred;
    }
    else
    {
        Relation    conrel;
        Relation    tgrel;
        List       *conoidlist = NIL;
        List       *tgoidlist = NIL;
        ListCell   *lc;

        /*
         * Handle SET CONSTRAINTS constraint-name [, ...]
         *
         * First, identify all the named constraints and make a list of their
         * OIDs.  Since, unlike the SQL spec, we allow multiple constraints of
         * the same name within a schema, the specifications are not
         * necessarily unique.  Our strategy is to target all matching
         * constraints within the first search-path schema that has any
         * matches, but disregard matches in schemas beyond the first match.
         * (This is a bit odd but it's the historical behavior.)
         */
        conrel = heap_open(ConstraintRelationId, AccessShareLock);

        foreach(lc, stmt->constraints)
        {
            RangeVar   *constraint = lfirst(lc);
            bool        found;
            List       *namespacelist;
            ListCell   *nslc;

            if (constraint->catalogname)
            {
                if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
                    ereport(ERROR,
                            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                             errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
                             constraint->catalogname, constraint->schemaname,
                                    constraint->relname)));
            }

            /*
             * If we're given the schema name with the constraint, look only
             * in that schema.  If given a bare constraint name, use the
             * search path to find the first matching constraint.
             */
            if (constraint->schemaname)
            {
                Oid         namespaceId = LookupExplicitNamespace(constraint->schemaname,
                                                                  false);

                namespacelist = list_make1_oid(namespaceId);
            }
            else
            {
                namespacelist = fetch_search_path(true);
            }

            found = false;
            foreach(nslc, namespacelist)
            {
                Oid         namespaceId = lfirst_oid(nslc);
                SysScanDesc conscan;
                ScanKeyData skey[2];
                HeapTuple   tup;

                ScanKeyInit(&skey[0],
                            Anum_pg_constraint_conname,
                            BTEqualStrategyNumber, F_NAMEEQ,
                            CStringGetDatum(constraint->relname));
                ScanKeyInit(&skey[1],
                            Anum_pg_constraint_connamespace,
                            BTEqualStrategyNumber, F_OIDEQ,
                            ObjectIdGetDatum(namespaceId));

                conscan = systable_beginscan(conrel, ConstraintNameNspIndexId,
                                             true, SnapshotNow, 2, skey);

                while (HeapTupleIsValid(tup = systable_getnext(conscan)))
                {
                    Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);

                    if (con->condeferrable)
                        conoidlist = lappend_oid(conoidlist,
                                                 HeapTupleGetOid(tup));
                    else if (stmt->deferred)
                        ereport(ERROR,
                                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                 errmsg("constraint \"%s\" is not deferrable",
                                        constraint->relname)));
                    found = true;
                }

                systable_endscan(conscan);

                /*
                 * Once we've found a matching constraint we do not search
                 * later parts of the search path.
                 */
                if (found)
                    break;
            }

            list_free(namespacelist);

            /*
             * Not found ?
             */
            if (!found)
                ereport(ERROR,
                        (errcode(ERRCODE_UNDEFINED_OBJECT),
                         errmsg("constraint \"%s\" does not exist",
                                constraint->relname)));
        }

        heap_close(conrel, AccessShareLock);

        /*
         * Now, locate the trigger(s) implementing each of these constraints,
         * and make a list of their OIDs.
         */
        tgrel = heap_open(TriggerRelationId, AccessShareLock);

        foreach(lc, conoidlist)
        {
            Oid         conoid = lfirst_oid(lc);
            bool        found;
            ScanKeyData skey;
            SysScanDesc tgscan;
            HeapTuple   htup;

            found = false;

            ScanKeyInit(&skey,
                        Anum_pg_trigger_tgconstraint,
                        BTEqualStrategyNumber, F_OIDEQ,
                        ObjectIdGetDatum(conoid));

            tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
                                        SnapshotNow, 1, &skey);

            while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
            {
                Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);

                /*
                 * Silently skip triggers that are marked as non-deferrable in
                 * pg_trigger.  This is not an error condition, since a
                 * deferrable RI constraint may have some non-deferrable
                 * actions.
                 */
                if (pg_trigger->tgdeferrable)
                    tgoidlist = lappend_oid(tgoidlist,
                                            HeapTupleGetOid(htup));

                found = true;
            }

            systable_endscan(tgscan);

            /* Safety check: a deferrable constraint should have triggers */
            if (!found)
                elog(ERROR, "no triggers found for constraint with OID %u",
                     conoid);
        }

        heap_close(tgrel, AccessShareLock);

        /*
         * Now we can set the trigger states of individual triggers for this
         * xact.
         */
        foreach(lc, tgoidlist)
        {
            Oid         tgoid = lfirst_oid(lc);
            SetConstraintState state = afterTriggers->state;
            bool        found = false;
            int         i;

            for (i = 0; i < state->numstates; i++)
            {
                if (state->trigstates[i].sct_tgoid == tgoid)
                {
                    state->trigstates[i].sct_tgisdeferred = stmt->deferred;
                    found = true;
                    break;
                }
            }
            if (!found)
            {
                afterTriggers->state =
                    SetConstraintStateAddItem(state, tgoid, stmt->deferred);
            }
        }
    }

    /*
     * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
     * checks against that constraint must be made when the SET CONSTRAINTS
     * command is executed -- i.e. the effects of the SET CONSTRAINTS command
     * apply retroactively.  We've updated the constraints state, so scan the
     * list of previously deferred events to fire any that have now become
     * immediate.
     *
     * Obviously, if this was SET ... DEFERRED then it can't have converted
     * any unfired events to immediate, so we need do nothing in that case.
     */
    if (!stmt->deferred)
    {
        AfterTriggerEventList *events = &afterTriggers->events;
        bool        snapshot_set = false;

        while (afterTriggerMarkEvents(events, NULL, true))
        {
            CommandId   firing_id = afterTriggers->firing_counter++;

            /*
             * Make sure a snapshot has been established in case trigger
             * functions need one.  Note that we avoid setting a snapshot if
             * we don't find at least one trigger that has to be fired now.
             * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION
             * ISOLATION LEVEL SERIALIZABLE; ... works properly.  (If we are
             * at the start of a transaction it's not possible for any trigger
             * events to be queued yet.)
             */
            if (!snapshot_set)
            {
                PushActiveSnapshot(GetTransactionSnapshot());
                snapshot_set = true;
            }

            /*
             * We can delete fired events if we are at top transaction level,
             * but we'd better not if inside a subtransaction, since the
             * subtransaction could later get rolled back.
             */
            if (afterTriggerInvokeEvents(events, firing_id, NULL,
                                         !IsSubTransaction()))
                break;          /* all fired */
        }

        if (snapshot_set)
            PopActiveSnapshot();
    }
}

TriggerDesc* CopyTriggerDesc ( TriggerDesc trigdesc  ) 

Definition at line 1634 of file trigger.c.

References i, NULL, TriggerDesc::numtriggers, palloc(), pstrdup(), Trigger::tgargs, Trigger::tgattr, Trigger::tgname, Trigger::tgnargs, Trigger::tgnattr, Trigger::tgqual, and TriggerDesc::triggers.

Referenced by InitResultRelInfo(), and RelationBuildTriggers().

{
    TriggerDesc *newdesc;
    Trigger    *trigger;
    int         i;

    if (trigdesc == NULL || trigdesc->numtriggers <= 0)
        return NULL;

    newdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc));
    memcpy(newdesc, trigdesc, sizeof(TriggerDesc));

    trigger = (Trigger *) palloc(trigdesc->numtriggers * sizeof(Trigger));
    memcpy(trigger, trigdesc->triggers,
           trigdesc->numtriggers * sizeof(Trigger));
    newdesc->triggers = trigger;

    for (i = 0; i < trigdesc->numtriggers; i++)
    {
        trigger->tgname = pstrdup(trigger->tgname);
        if (trigger->tgnattr > 0)
        {
            int16      *newattr;

            newattr = (int16 *) palloc(trigger->tgnattr * sizeof(int16));
            memcpy(newattr, trigger->tgattr,
                   trigger->tgnattr * sizeof(int16));
            trigger->tgattr = newattr;
        }
        if (trigger->tgnargs > 0)
        {
            char      **newargs;
            int16       j;

            newargs = (char **) palloc(trigger->tgnargs * sizeof(char *));
            for (j = 0; j < trigger->tgnargs; j++)
                newargs[j] = pstrdup(trigger->tgargs[j]);
            trigger->tgargs = newargs;
        }
        if (trigger->tgqual)
            trigger->tgqual = pstrdup(trigger->tgqual);
        trigger++;
    }

    return newdesc;
}

Oid CreateTrigger ( CreateTrigStmt stmt,
const char *  queryString,
Oid  constraintOid,
Oid  indexOid,
bool  isInternal 
)

Definition at line 120 of file trigger.c.

References AccessExclusiveLock, AccessShareLock, ACL_EXECUTE, ACL_KIND_CLASS, ACL_KIND_PROC, ACL_TRIGGER, aclcheck_error(), ACLCHECK_OK, addRangeTableEntryForRelation(), addRTEtoQuery(), allowSystemTableMods, Anum_pg_trigger_tgargs, Anum_pg_trigger_tgattr, Anum_pg_trigger_tgconstraint, Anum_pg_trigger_tgconstrindid, Anum_pg_trigger_tgconstrrelid, Anum_pg_trigger_tgdeferrable, Anum_pg_trigger_tgenabled, Anum_pg_trigger_tgfoid, Anum_pg_trigger_tginitdeferred, Anum_pg_trigger_tgisinternal, Anum_pg_trigger_tgname, Anum_pg_trigger_tgnargs, Anum_pg_trigger_tgqual, Anum_pg_trigger_tgrelid, Anum_pg_trigger_tgtype, CreateTrigStmt::args, Assert, assign_expr_collations(), attnameAttNum(), BoolGetDatum, BTEqualStrategyNumber, buildint2vector(), byteain(), CatalogUpdateIndexes(), CharGetDatum, ObjectAddress::classId, CreateTrigStmt::columns, CONSTRAINT_TRIGGER, CreateTrigStmt::constrrel, ConvertTriggerToFK(), copyObject(), CreateConstraintEntry(), CStringGetDatum, CStringGetTextDatum, DatumGetPointer, CreateTrigStmt::deferrable, DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, DEPENDENCY_NORMAL, DirectFunctionCall1, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, CreateTrigStmt::events, EXPR_KIND_TRIGGER_WHEN, free_parsestate(), CreateTrigStmt::funcname, get_func_rettype(), get_rel_name(), GetNewOid(), GETSTRUCT, GetUserId(), heap_close, heap_form_tuple(), heap_freetuple(), heap_open(), heap_openrv(), HeapTupleIsValid, HeapTupleSetOid, i, CreateTrigStmt::initdeferred, Int16GetDatum, InvalidAttrNumber, InvalidOid, InvokeObjectPostCreateHookArg, CreateTrigStmt::isconstraint, IsSystemRelation(), lfirst, list_length(), Var::location, LookupFuncName(), make_parsestate(), makeAlias(), name, namein(), NameListToString(), namestrcmp(), NIL, nodeToString(), NoLock, NULL, ObjectAddress::objectId, ObjectIdGetDatum, ObjectAddress::objectSubId, OidIsValid, OPAQUEOID, ParseState::p_rtable, ParseState::p_sourcetext, palloc(), parser_errposition(), pfree(), pg_class_aclcheck(), pg_proc_aclcheck(), PointerGetDatum, PRS2_NEW_VARNO, PRS2_OLD_VARNO, pull_var_clause(), PVC_REJECT_AGGREGATES, PVC_REJECT_PLACEHOLDERS, RangeVarGetRelid, RelationData::rd_att, RelationData::rd_rel, recordDependencyOn(), recordDependencyOnExpr(), CreateTrigStmt::relation, RelationGetNamespace, RelationGetRelationName, RelationGetRelid, RelationRelationId, RELKIND_RELATION, RELKIND_VIEW, RangeVar::relname, RELOID, RI_FKey_trigger_type(), RI_TRIGGER_NONE, CreateTrigStmt::row, RowExclusiveLock, ScanKeyInit(), SearchSysCacheCopy1, SetFunctionReturnType(), simple_heap_insert(), simple_heap_update(), SnapshotNow, snprintf(), strVal, systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, CreateTrigStmt::timing, transformWhereClause(), TRIGGER_CLEAR_TYPE, TRIGGER_FIRES_ON_ORIGIN, TRIGGER_FOR_BEFORE, TRIGGER_FOR_DELETE, TRIGGER_FOR_INSERT, TRIGGER_FOR_INSTEAD, TRIGGER_FOR_ROW, TRIGGER_FOR_TRUNCATE, TRIGGER_SETT_ROW, TRIGGER_TYPE_AFTER, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_INSTEAD, TRIGGEROID, TriggerRelationId, TriggerRelidNameIndexId, CreateTrigStmt::trigname, values, Var::varattno, Var::varno, WARNING, and CreateTrigStmt::whenClause.

Referenced by CreateFKCheckTrigger(), createForeignKeyTriggers(), index_constraint_create(), and ProcessUtilitySlow().

{
    int16       tgtype;
    int         ncolumns;
    int16      *columns;
    int2vector *tgattr;
    Node       *whenClause;
    List       *whenRtable;
    char       *qual;
    Datum       values[Natts_pg_trigger];
    bool        nulls[Natts_pg_trigger];
    Relation    rel;
    AclResult   aclresult;
    Relation    tgrel;
    SysScanDesc tgscan;
    ScanKeyData key;
    Relation    pgrel;
    HeapTuple   tuple;
    Oid         fargtypes[1];   /* dummy */
    Oid         funcoid;
    Oid         funcrettype;
    Oid         trigoid;
    char        internaltrigname[NAMEDATALEN];
    char       *trigname;
    Oid         constrrelid = InvalidOid;
    ObjectAddress myself,
                referenced;

    rel = heap_openrv(stmt->relation, AccessExclusiveLock);

    /*
     * Triggers must be on tables or views, and there are additional
     * relation-type-specific restrictions.
     */
    if (rel->rd_rel->relkind == RELKIND_RELATION)
    {
        /* Tables can't have INSTEAD OF triggers */
        if (stmt->timing != TRIGGER_TYPE_BEFORE &&
            stmt->timing != TRIGGER_TYPE_AFTER)
            ereport(ERROR,
                    (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                     errmsg("\"%s\" is a table",
                            RelationGetRelationName(rel)),
                     errdetail("Tables cannot have INSTEAD OF triggers.")));
    }
    else if (rel->rd_rel->relkind == RELKIND_VIEW)
    {
        /*
         * Views can have INSTEAD OF triggers (which we check below are
         * row-level), or statement-level BEFORE/AFTER triggers.
         */
        if (stmt->timing != TRIGGER_TYPE_INSTEAD && stmt->row)
            ereport(ERROR,
                    (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                     errmsg("\"%s\" is a view",
                            RelationGetRelationName(rel)),
                     errdetail("Views cannot have row-level BEFORE or AFTER triggers.")));
        /* Disallow TRUNCATE triggers on VIEWs */
        if (TRIGGER_FOR_TRUNCATE(stmt->events))
            ereport(ERROR,
                    (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                     errmsg("\"%s\" is a view",
                            RelationGetRelationName(rel)),
                     errdetail("Views cannot have TRUNCATE triggers.")));
    }
    else
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                 errmsg("\"%s\" is not a table or view",
                        RelationGetRelationName(rel))));

    if (!allowSystemTableMods && IsSystemRelation(rel))
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                 errmsg("permission denied: \"%s\" is a system catalog",
                        RelationGetRelationName(rel))));

    if (stmt->isconstraint && stmt->constrrel != NULL)
    {
        /*
         * We must take a lock on the target relation to protect against
         * concurrent drop.  It's not clear that AccessShareLock is strong
         * enough, but we certainly need at least that much... otherwise, we
         * might end up creating a pg_constraint entry referencing a
         * nonexistent table.
         */
        constrrelid = RangeVarGetRelid(stmt->constrrel, AccessShareLock, false);
    }

    /* permission checks */
    if (!isInternal)
    {
        aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
                                      ACL_TRIGGER);
        if (aclresult != ACLCHECK_OK)
            aclcheck_error(aclresult, ACL_KIND_CLASS,
                           RelationGetRelationName(rel));

        if (OidIsValid(constrrelid))
        {
            aclresult = pg_class_aclcheck(constrrelid, GetUserId(),
                                          ACL_TRIGGER);
            if (aclresult != ACLCHECK_OK)
                aclcheck_error(aclresult, ACL_KIND_CLASS,
                               get_rel_name(constrrelid));
        }
    }

    /* Compute tgtype */
    TRIGGER_CLEAR_TYPE(tgtype);
    if (stmt->row)
        TRIGGER_SETT_ROW(tgtype);
    tgtype |= stmt->timing;
    tgtype |= stmt->events;

    /* Disallow ROW-level TRUNCATE triggers */
    if (TRIGGER_FOR_ROW(tgtype) && TRIGGER_FOR_TRUNCATE(tgtype))
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("TRUNCATE FOR EACH ROW triggers are not supported")));

    /* INSTEAD triggers must be row-level, and can't have WHEN or columns */
    if (TRIGGER_FOR_INSTEAD(tgtype))
    {
        if (!TRIGGER_FOR_ROW(tgtype))
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                     errmsg("INSTEAD OF triggers must be FOR EACH ROW")));
        if (stmt->whenClause)
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("INSTEAD OF triggers cannot have WHEN conditions")));
        if (stmt->columns != NIL)
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                     errmsg("INSTEAD OF triggers cannot have column lists")));
    }

    /*
     * Parse the WHEN clause, if any
     */
    if (stmt->whenClause)
    {
        ParseState *pstate;
        RangeTblEntry *rte;
        List       *varList;
        ListCell   *lc;

        /* Set up a pstate to parse with */
        pstate = make_parsestate(NULL);
        pstate->p_sourcetext = queryString;

        /*
         * Set up RTEs for OLD and NEW references.
         *
         * 'OLD' must always have varno equal to 1 and 'NEW' equal to 2.
         */
        rte = addRangeTableEntryForRelation(pstate, rel,
                                            makeAlias("old", NIL),
                                            false, false);
        addRTEtoQuery(pstate, rte, false, true, true);
        rte = addRangeTableEntryForRelation(pstate, rel,
                                            makeAlias("new", NIL),
                                            false, false);
        addRTEtoQuery(pstate, rte, false, true, true);

        /* Transform expression.  Copy to be sure we don't modify original */
        whenClause = transformWhereClause(pstate,
                                          copyObject(stmt->whenClause),
                                          EXPR_KIND_TRIGGER_WHEN,
                                          "WHEN");
        /* we have to fix its collations too */
        assign_expr_collations(pstate, whenClause);

        /*
         * Check for disallowed references to OLD/NEW.
         *
         * NB: pull_var_clause is okay here only because we don't allow
         * subselects in WHEN clauses; it would fail to examine the contents
         * of subselects.
         */
        varList = pull_var_clause(whenClause,
                                  PVC_REJECT_AGGREGATES,
                                  PVC_REJECT_PLACEHOLDERS);
        foreach(lc, varList)
        {
            Var        *var = (Var *) lfirst(lc);

            switch (var->varno)
            {
                case PRS2_OLD_VARNO:
                    if (!TRIGGER_FOR_ROW(tgtype))
                        ereport(ERROR,
                                (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                                 errmsg("statement trigger's WHEN condition cannot reference column values"),
                                 parser_errposition(pstate, var->location)));
                    if (TRIGGER_FOR_INSERT(tgtype))
                        ereport(ERROR,
                                (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                                 errmsg("INSERT trigger's WHEN condition cannot reference OLD values"),
                                 parser_errposition(pstate, var->location)));
                    /* system columns are okay here */
                    break;
                case PRS2_NEW_VARNO:
                    if (!TRIGGER_FOR_ROW(tgtype))
                        ereport(ERROR,
                                (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                                 errmsg("statement trigger's WHEN condition cannot reference column values"),
                                 parser_errposition(pstate, var->location)));
                    if (TRIGGER_FOR_DELETE(tgtype))
                        ereport(ERROR,
                                (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                                 errmsg("DELETE trigger's WHEN condition cannot reference NEW values"),
                                 parser_errposition(pstate, var->location)));
                    if (var->varattno < 0 && TRIGGER_FOR_BEFORE(tgtype))
                        ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("BEFORE trigger's WHEN condition cannot reference NEW system columns"),
                                 parser_errposition(pstate, var->location)));
                    break;
                default:
                    /* can't happen without add_missing_from, so just elog */
                    elog(ERROR, "trigger WHEN condition cannot contain references to other relations");
                    break;
            }
        }

        /* we'll need the rtable for recordDependencyOnExpr */
        whenRtable = pstate->p_rtable;

        qual = nodeToString(whenClause);

        free_parsestate(pstate);
    }
    else
    {
        whenClause = NULL;
        whenRtable = NIL;
        qual = NULL;
    }

    /*
     * Find and validate the trigger function.
     */
    funcoid = LookupFuncName(stmt->funcname, 0, fargtypes, false);
    if (!isInternal)
    {
        aclresult = pg_proc_aclcheck(funcoid, GetUserId(), ACL_EXECUTE);
        if (aclresult != ACLCHECK_OK)
            aclcheck_error(aclresult, ACL_KIND_PROC,
                           NameListToString(stmt->funcname));
    }
    funcrettype = get_func_rettype(funcoid);
    if (funcrettype != TRIGGEROID)
    {
        /*
         * We allow OPAQUE just so we can load old dump files.  When we see a
         * trigger function declared OPAQUE, change it to TRIGGER.
         */
        if (funcrettype == OPAQUEOID)
        {
            ereport(WARNING,
                    (errmsg("changing return type of function %s from \"opaque\" to \"trigger\"",
                            NameListToString(stmt->funcname))));
            SetFunctionReturnType(funcoid, TRIGGEROID);
        }
        else
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                     errmsg("function %s must return type \"trigger\"",
                            NameListToString(stmt->funcname))));
    }

    /*
     * If the command is a user-entered CREATE CONSTRAINT TRIGGER command that
     * references one of the built-in RI_FKey trigger functions, assume it is
     * from a dump of a pre-7.3 foreign key constraint, and take steps to
     * convert this legacy representation into a regular foreign key
     * constraint.  Ugly, but necessary for loading old dump files.
     */
    if (stmt->isconstraint && !isInternal &&
        list_length(stmt->args) >= 6 &&
        (list_length(stmt->args) % 2) == 0 &&
        RI_FKey_trigger_type(funcoid) != RI_TRIGGER_NONE)
    {
        /* Keep lock on target rel until end of xact */
        heap_close(rel, NoLock);

        ConvertTriggerToFK(stmt, funcoid);

        return InvalidOid;
    }

    /*
     * If it's a user-entered CREATE CONSTRAINT TRIGGER command, make a
     * corresponding pg_constraint entry.
     */
    if (stmt->isconstraint && !OidIsValid(constraintOid))
    {
        /* Internal callers should have made their own constraints */
        Assert(!isInternal);
        constraintOid = CreateConstraintEntry(stmt->trigname,
                                              RelationGetNamespace(rel),
                                              CONSTRAINT_TRIGGER,
                                              stmt->deferrable,
                                              stmt->initdeferred,
                                              true,
                                              RelationGetRelid(rel),
                                              NULL,     /* no conkey */
                                              0,
                                              InvalidOid,       /* no domain */
                                              InvalidOid,       /* no index */
                                              InvalidOid,       /* no foreign key */
                                              NULL,
                                              NULL,
                                              NULL,
                                              NULL,
                                              0,
                                              ' ',
                                              ' ',
                                              ' ',
                                              NULL,     /* no exclusion */
                                              NULL,     /* no check constraint */
                                              NULL,
                                              NULL,
                                              true,     /* islocal */
                                              0,        /* inhcount */
                                              true,     /* isnoinherit */
                                              isInternal);  /* is_internal */
    }

    /*
     * Generate the trigger's OID now, so that we can use it in the name if
     * needed.
     */
    tgrel = heap_open(TriggerRelationId, RowExclusiveLock);

    trigoid = GetNewOid(tgrel);

    /*
     * If trigger is internally generated, modify the provided trigger name to
     * ensure uniqueness by appending the trigger OID.  (Callers will usually
     * supply a simple constant trigger name in these cases.)
     */
    if (isInternal)
    {
        snprintf(internaltrigname, sizeof(internaltrigname),
                 "%s_%u", stmt->trigname, trigoid);
        trigname = internaltrigname;
    }
    else
    {
        /* user-defined trigger; use the specified trigger name as-is */
        trigname = stmt->trigname;
    }

    /*
     * Scan pg_trigger for existing triggers on relation.  We do this only to
     * give a nice error message if there's already a trigger of the same
     * name.  (The unique index on tgrelid/tgname would complain anyway.) We
     * can skip this for internally generated triggers, since the name
     * modification above should be sufficient.
     *
     * NOTE that this is cool only because we have AccessExclusiveLock on the
     * relation, so the trigger set won't be changing underneath us.
     */
    if (!isInternal)
    {
        ScanKeyInit(&key,
                    Anum_pg_trigger_tgrelid,
                    BTEqualStrategyNumber, F_OIDEQ,
                    ObjectIdGetDatum(RelationGetRelid(rel)));
        tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
                                    SnapshotNow, 1, &key);
        while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
        {
            Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);

            if (namestrcmp(&(pg_trigger->tgname), trigname) == 0)
                ereport(ERROR,
                        (errcode(ERRCODE_DUPLICATE_OBJECT),
                  errmsg("trigger \"%s\" for relation \"%s\" already exists",
                         trigname, stmt->relation->relname)));
        }
        systable_endscan(tgscan);
    }

    /*
     * Build the new pg_trigger tuple.
     */
    memset(nulls, false, sizeof(nulls));

    values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
    values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein,
                                                  CStringGetDatum(trigname));
    values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid);
    values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype);
    values[Anum_pg_trigger_tgenabled - 1] = CharGetDatum(TRIGGER_FIRES_ON_ORIGIN);
    values[Anum_pg_trigger_tgisinternal - 1] = BoolGetDatum(isInternal);
    values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid);
    values[Anum_pg_trigger_tgconstrindid - 1] = ObjectIdGetDatum(indexOid);
    values[Anum_pg_trigger_tgconstraint - 1] = ObjectIdGetDatum(constraintOid);
    values[Anum_pg_trigger_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable);
    values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred);

    if (stmt->args)
    {
        ListCell   *le;
        char       *args;
        int16       nargs = list_length(stmt->args);
        int         len = 0;

        foreach(le, stmt->args)
        {
            char       *ar = strVal(lfirst(le));

            len += strlen(ar) + 4;
            for (; *ar; ar++)
            {
                if (*ar == '\\')
                    len++;
            }
        }
        args = (char *) palloc(len + 1);
        args[0] = '\0';
        foreach(le, stmt->args)
        {
            char       *s = strVal(lfirst(le));
            char       *d = args + strlen(args);

            while (*s)
            {
                if (*s == '\\')
                    *d++ = '\\';
                *d++ = *s++;
            }
            strcpy(d, "\\000");
        }
        values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(nargs);
        values[Anum_pg_trigger_tgargs - 1] = DirectFunctionCall1(byteain,
                                                      CStringGetDatum(args));
    }
    else
    {
        values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(0);
        values[Anum_pg_trigger_tgargs - 1] = DirectFunctionCall1(byteain,
                                                        CStringGetDatum(""));
    }

    /* build column number array if it's a column-specific trigger */
    ncolumns = list_length(stmt->columns);
    if (ncolumns == 0)
        columns = NULL;
    else
    {
        ListCell   *cell;
        int         i = 0;

        columns = (int16 *) palloc(ncolumns * sizeof(int16));
        foreach(cell, stmt->columns)
        {
            char       *name = strVal(lfirst(cell));
            int16       attnum;
            int         j;

            /* Lookup column name.  System columns are not allowed */
            attnum = attnameAttNum(rel, name, false);
            if (attnum == InvalidAttrNumber)
                ereport(ERROR,
                        (errcode(ERRCODE_UNDEFINED_COLUMN),
                    errmsg("column \"%s\" of relation \"%s\" does not exist",
                           name, RelationGetRelationName(rel))));

            /* Check for duplicates */
            for (j = i - 1; j >= 0; j--)
            {
                if (columns[j] == attnum)
                    ereport(ERROR,
                            (errcode(ERRCODE_DUPLICATE_COLUMN),
                             errmsg("column \"%s\" specified more than once",
                                    name)));
            }

            columns[i++] = attnum;
        }
    }
    tgattr = buildint2vector(columns, ncolumns);
    values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum(tgattr);

    /* set tgqual if trigger has WHEN clause */
    if (qual)
        values[Anum_pg_trigger_tgqual - 1] = CStringGetTextDatum(qual);
    else
        nulls[Anum_pg_trigger_tgqual - 1] = true;

    tuple = heap_form_tuple(tgrel->rd_att, values, nulls);

    /* force tuple to have the desired OID */
    HeapTupleSetOid(tuple, trigoid);

    /*
     * Insert tuple into pg_trigger.
     */
    simple_heap_insert(tgrel, tuple);

    CatalogUpdateIndexes(tgrel, tuple);

    heap_freetuple(tuple);
    heap_close(tgrel, RowExclusiveLock);

    pfree(DatumGetPointer(values[Anum_pg_trigger_tgname - 1]));
    pfree(DatumGetPointer(values[Anum_pg_trigger_tgargs - 1]));
    pfree(DatumGetPointer(values[Anum_pg_trigger_tgattr - 1]));

    /*
     * Update relation's pg_class entry.  Crucial side-effect: other backends
     * (and this one too!) are sent SI message to make them rebuild relcache
     * entries.
     */
    pgrel = heap_open(RelationRelationId, RowExclusiveLock);
    tuple = SearchSysCacheCopy1(RELOID,
                                ObjectIdGetDatum(RelationGetRelid(rel)));
    if (!HeapTupleIsValid(tuple))
        elog(ERROR, "cache lookup failed for relation %u",
             RelationGetRelid(rel));

    ((Form_pg_class) GETSTRUCT(tuple))->relhastriggers = true;

    simple_heap_update(pgrel, &tuple->t_self, tuple);

    CatalogUpdateIndexes(pgrel, tuple);

    heap_freetuple(tuple);
    heap_close(pgrel, RowExclusiveLock);

    /*
     * We used to try to update the rel's relcache entry here, but that's
     * fairly pointless since it will happen as a byproduct of the upcoming
     * CommandCounterIncrement...
     */

    /*
     * Record dependencies for trigger.  Always place a normal dependency on
     * the function.
     */
    myself.classId = TriggerRelationId;
    myself.objectId = trigoid;
    myself.objectSubId = 0;

    referenced.classId = ProcedureRelationId;
    referenced.objectId = funcoid;
    referenced.objectSubId = 0;
    recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);

    if (isInternal && OidIsValid(constraintOid))
    {
        /*
         * Internally-generated trigger for a constraint, so make it an
         * internal dependency of the constraint.  We can skip depending on
         * the relation(s), as there'll be an indirect dependency via the
         * constraint.
         */
        referenced.classId = ConstraintRelationId;
        referenced.objectId = constraintOid;
        referenced.objectSubId = 0;
        recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
    }
    else
    {
        /*
         * User CREATE TRIGGER, so place dependencies.  We make trigger be
         * auto-dropped if its relation is dropped or if the FK relation is
         * dropped.  (Auto drop is compatible with our pre-7.3 behavior.)
         */
        referenced.classId = RelationRelationId;
        referenced.objectId = RelationGetRelid(rel);
        referenced.objectSubId = 0;
        recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
        if (OidIsValid(constrrelid))
        {
            referenced.classId = RelationRelationId;
            referenced.objectId = constrrelid;
            referenced.objectSubId = 0;
            recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
        }
        /* Not possible to have an index dependency in this case */
        Assert(!OidIsValid(indexOid));

        /*
         * If it's a user-specified constraint trigger, make the constraint
         * internally dependent on the trigger instead of vice versa.
         */
        if (OidIsValid(constraintOid))
        {
            referenced.classId = ConstraintRelationId;
            referenced.objectId = constraintOid;
            referenced.objectSubId = 0;
            recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
        }
    }

    /* If column-specific trigger, add normal dependencies on columns */
    if (columns != NULL)
    {
        int         i;

        referenced.classId = RelationRelationId;
        referenced.objectId = RelationGetRelid(rel);
        for (i = 0; i < ncolumns; i++)
        {
            referenced.objectSubId = columns[i];
            recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
        }
    }

    /*
     * If it has a WHEN clause, add dependencies on objects mentioned in the
     * expression (eg, functions, as well as any columns used).
     */
    if (whenClause != NULL)
        recordDependencyOnExpr(&myself, whenClause, whenRtable,
                               DEPENDENCY_NORMAL);

    /* Post creation hook for new trigger */
    InvokeObjectPostCreateHookArg(TriggerRelationId, trigoid, 0,
                                  isInternal);

    /* Keep lock on target rel until end of xact */
    heap_close(rel, NoLock);

    return trigoid;
}

void EnableDisableTrigger ( Relation  rel,
const char *  tgname,
char  fires_when,
bool  skip_system 
)

Definition at line 1331 of file trigger.c.

References Anum_pg_trigger_tgname, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, CacheInvalidateRelcache(), CatalogUpdateIndexes(), CStringGetDatum, ereport, errcode(), errmsg(), ERROR, GETSTRUCT, heap_close, heap_copytuple(), heap_freetuple(), heap_open(), HeapTupleGetOid, HeapTupleIsValid, InvokeObjectPostAlterHook, NameStr, ObjectIdGetDatum, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), simple_heap_update(), SnapshotNow, superuser(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, TriggerRelationId, and TriggerRelidNameIndexId.

Referenced by ATExecEnableDisableTrigger().

{
    Relation    tgrel;
    int         nkeys;
    ScanKeyData keys[2];
    SysScanDesc tgscan;
    HeapTuple   tuple;
    bool        found;
    bool        changed;

    /* Scan the relevant entries in pg_triggers */
    tgrel = heap_open(TriggerRelationId, RowExclusiveLock);

    ScanKeyInit(&keys[0],
                Anum_pg_trigger_tgrelid,
                BTEqualStrategyNumber, F_OIDEQ,
                ObjectIdGetDatum(RelationGetRelid(rel)));
    if (tgname)
    {
        ScanKeyInit(&keys[1],
                    Anum_pg_trigger_tgname,
                    BTEqualStrategyNumber, F_NAMEEQ,
                    CStringGetDatum(tgname));
        nkeys = 2;
    }
    else
        nkeys = 1;

    tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
                                SnapshotNow, nkeys, keys);

    found = changed = false;

    while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
    {
        Form_pg_trigger oldtrig = (Form_pg_trigger) GETSTRUCT(tuple);

        if (oldtrig->tgisinternal)
        {
            /* system trigger ... ok to process? */
            if (skip_system)
                continue;
            if (!superuser())
                ereport(ERROR,
                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                      errmsg("permission denied: \"%s\" is a system trigger",
                             NameStr(oldtrig->tgname))));
        }

        found = true;

        if (oldtrig->tgenabled != fires_when)
        {
            /* need to change this one ... make a copy to scribble on */
            HeapTuple   newtup = heap_copytuple(tuple);
            Form_pg_trigger newtrig = (Form_pg_trigger) GETSTRUCT(newtup);

            newtrig->tgenabled = fires_when;

            simple_heap_update(tgrel, &newtup->t_self, newtup);

            /* Keep catalog indexes current */
            CatalogUpdateIndexes(tgrel, newtup);

            heap_freetuple(newtup);

            changed = true;
        }

        InvokeObjectPostAlterHook(TriggerRelationId,
                                  HeapTupleGetOid(tuple), 0);
    }

    systable_endscan(tgscan);

    heap_close(tgrel, RowExclusiveLock);

    if (tgname && !found)
        ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_OBJECT),
                 errmsg("trigger \"%s\" for table \"%s\" does not exist",
                        tgname, RelationGetRelationName(rel))));

    /*
     * If we changed anything, broadcast a SI inval message to force each
     * backend (including our own!) to rebuild relation's relcache entry.
     * Otherwise they will fail to apply the change promptly.
     */
    if (changed)
        CacheInvalidateRelcache(rel);
}

void ExecARDeleteTriggers ( EState estate,
ResultRelInfo relinfo,
ItemPointer  tupleid 
)

Definition at line 2205 of file trigger.c.

References AfterTriggerSaveEvent(), GetTupleForTrigger(), heap_freetuple(), LockTupleExclusive, NIL, NULL, ResultRelInfo::ri_TrigDesc, TriggerDesc::trig_delete_after_row, and TRIGGER_EVENT_DELETE.

Referenced by ExecDelete().

{
    TriggerDesc *trigdesc = relinfo->ri_TrigDesc;

    if (trigdesc && trigdesc->trig_delete_after_row)
    {
        HeapTuple   trigtuple = GetTupleForTrigger(estate, NULL, relinfo,
                                                   tupleid, LockTupleExclusive,
                                                   NULL);

        AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_DELETE,
                              true, trigtuple, NULL, NIL, NULL);
        heap_freetuple(trigtuple);
    }
}

void ExecARInsertTriggers ( EState estate,
ResultRelInfo relinfo,
HeapTuple  trigtuple,
List recheckIndexes 
)

Definition at line 2011 of file trigger.c.

References AfterTriggerSaveEvent(), NULL, ResultRelInfo::ri_TrigDesc, TriggerDesc::trig_insert_after_row, and TRIGGER_EVENT_INSERT.

Referenced by CopyFrom(), CopyFromInsertBatch(), and ExecInsert().

{
    TriggerDesc *trigdesc = relinfo->ri_TrigDesc;

    if (trigdesc && trigdesc->trig_insert_after_row)
        AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT,
                              true, NULL, trigtuple, recheckIndexes, NULL);
}

void ExecARUpdateTriggers ( EState estate,
ResultRelInfo relinfo,
ItemPointer  tupleid,
HeapTuple  newtuple,
List recheckIndexes 
)

Definition at line 2443 of file trigger.c.

References AfterTriggerSaveEvent(), GetModifiedColumns, GetTupleForTrigger(), heap_freetuple(), LockTupleExclusive, NULL, ResultRelInfo::ri_TrigDesc, TriggerDesc::trig_update_after_row, and TRIGGER_EVENT_UPDATE.

Referenced by ExecUpdate().

{
    TriggerDesc *trigdesc = relinfo->ri_TrigDesc;

    if (trigdesc && trigdesc->trig_update_after_row)
    {
        HeapTuple   trigtuple = GetTupleForTrigger(estate, NULL, relinfo,
                                                   tupleid, LockTupleExclusive,
                                                   NULL);

        AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE,
                              true, trigtuple, newtuple, recheckIndexes,
                              GetModifiedColumns(relinfo, estate));
        heap_freetuple(trigtuple);
    }
}

void ExecASDeleteTriggers ( EState estate,
ResultRelInfo relinfo 
)
void ExecASInsertTriggers ( EState estate,
ResultRelInfo relinfo 
)
void ExecASTruncateTriggers ( EState estate,
ResultRelInfo relinfo 
)
void ExecASUpdateTriggers ( EState estate,
ResultRelInfo relinfo 
)
bool ExecBRDeleteTriggers ( EState estate,
EPQState epqstate,
ResultRelInfo relinfo,
ItemPointer  tupleid 
)

Definition at line 2146 of file trigger.c.

References ExecCallTriggerFunc(), GetPerTupleMemoryContext, GetTupleForTrigger(), heap_freetuple(), LockTupleExclusive, NULL, TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgtype, TRIGGER_EVENT_DELETE, TRIGGER_EVENT_ROW, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_DELETE, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_ROW, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

Referenced by ExecDelete().

{
    TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
    bool        result = true;
    TriggerData LocTriggerData;
    HeapTuple   trigtuple;
    HeapTuple   newtuple;
    TupleTableSlot *newSlot;
    int         i;

    trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
                                   LockTupleExclusive, &newSlot);
    if (trigtuple == NULL)
        return false;

    LocTriggerData.type = T_TriggerData;
    LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
        TRIGGER_EVENT_ROW |
        TRIGGER_EVENT_BEFORE;
    LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
    LocTriggerData.tg_newtuple = NULL;
    LocTriggerData.tg_newtuplebuf = InvalidBuffer;
    for (i = 0; i < trigdesc->numtriggers; i++)
    {
        Trigger    *trigger = &trigdesc->triggers[i];

        if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
                                  TRIGGER_TYPE_ROW,
                                  TRIGGER_TYPE_BEFORE,
                                  TRIGGER_TYPE_DELETE))
            continue;
        if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
                            NULL, trigtuple, NULL))
            continue;

        LocTriggerData.tg_trigtuple = trigtuple;
        LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
        LocTriggerData.tg_trigger = trigger;
        newtuple = ExecCallTriggerFunc(&LocTriggerData,
                                       i,
                                       relinfo->ri_TrigFunctions,
                                       relinfo->ri_TrigInstrument,
                                       GetPerTupleMemoryContext(estate));
        if (newtuple == NULL)
        {
            result = false;     /* tell caller to suppress delete */
            break;
        }
        if (newtuple != trigtuple)
            heap_freetuple(newtuple);
    }
    heap_freetuple(trigtuple);

    return result;
}

TupleTableSlot* ExecBRInsertTriggers ( EState estate,
ResultRelInfo relinfo,
TupleTableSlot slot 
)

Definition at line 1947 of file trigger.c.

References EState::es_trig_tuple_slot, ExecCallTriggerFunc(), ExecMaterializeSlot(), ExecSetSlotDescriptor(), ExecStoreTuple(), GetPerTupleMemoryContext, heap_freetuple(), InvalidBuffer, NULL, TriggerDesc::numtriggers, RelationGetDescr, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgtype, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_ROW, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_INSERT, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_ROW, TriggerEnabled(), TriggerDesc::triggers, TupleTableSlot::tts_tupleDescriptor, and TriggerData::type.

Referenced by CopyFrom(), and ExecInsert().

{
    TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
    HeapTuple   slottuple = ExecMaterializeSlot(slot);
    HeapTuple   newtuple = slottuple;
    HeapTuple   oldtuple;
    TriggerData LocTriggerData;
    int         i;

    LocTriggerData.type = T_TriggerData;
    LocTriggerData.tg_event = TRIGGER_EVENT_INSERT |
        TRIGGER_EVENT_ROW |
        TRIGGER_EVENT_BEFORE;
    LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
    LocTriggerData.tg_newtuple = NULL;
    LocTriggerData.tg_newtuplebuf = InvalidBuffer;
    for (i = 0; i < trigdesc->numtriggers; i++)
    {
        Trigger    *trigger = &trigdesc->triggers[i];

        if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
                                  TRIGGER_TYPE_ROW,
                                  TRIGGER_TYPE_BEFORE,
                                  TRIGGER_TYPE_INSERT))
            continue;
        if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
                            NULL, NULL, newtuple))
            continue;

        LocTriggerData.tg_trigtuple = oldtuple = newtuple;
        LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
        LocTriggerData.tg_trigger = trigger;
        newtuple = ExecCallTriggerFunc(&LocTriggerData,
                                       i,
                                       relinfo->ri_TrigFunctions,
                                       relinfo->ri_TrigInstrument,
                                       GetPerTupleMemoryContext(estate));
        if (oldtuple != newtuple && oldtuple != slottuple)
            heap_freetuple(oldtuple);
        if (newtuple == NULL)
            return NULL;        /* "do nothing" */
    }

    if (newtuple != slottuple)
    {
        /*
         * Return the modified tuple using the es_trig_tuple_slot.  We assume
         * the tuple was allocated in per-tuple memory context, and therefore
         * will go away by itself. The tuple table slot should not try to
         * clear it.
         */
        TupleTableSlot *newslot = estate->es_trig_tuple_slot;
        TupleDesc   tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);

        if (newslot->tts_tupleDescriptor != tupdesc)
            ExecSetSlotDescriptor(newslot, tupdesc);
        ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
        slot = newslot;
    }
    return slot;
}

TupleTableSlot* ExecBRUpdateTriggers ( EState estate,
EPQState epqstate,
ResultRelInfo relinfo,
ItemPointer  tupleid,
TupleTableSlot slot 
)

Definition at line 2332 of file trigger.c.

References bms_overlap(), EState::es_trig_tuple_slot, ExecCallTriggerFunc(), ExecFilterJunk(), ExecMaterializeSlot(), ExecSetSlotDescriptor(), ExecStoreTuple(), GetModifiedColumns, GetPerTupleMemoryContext, GetTupleForTrigger(), heap_freetuple(), InvalidBuffer, NULL, TriggerDesc::numtriggers, RelationGetDescr, RelationGetIndexAttrBitmap(), ResultRelInfo::ri_junkFilter, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgtype, TRIGGER_EVENT_ROW, TRIGGER_EVENT_UPDATE, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_ROW, TRIGGER_TYPE_UPDATE, TriggerEnabled(), TriggerDesc::triggers, TupleTableSlot::tts_tupleDescriptor, and TriggerData::type.

Referenced by ExecUpdate().

{
    TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
    HeapTuple   slottuple = ExecMaterializeSlot(slot);
    HeapTuple   newtuple = slottuple;
    TriggerData LocTriggerData;
    HeapTuple   trigtuple;
    HeapTuple   oldtuple;
    TupleTableSlot *newSlot;
    int         i;
    Bitmapset  *modifiedCols;
    Bitmapset  *keyCols;
    LockTupleMode lockmode;

    /*
     * Compute lock mode to use.  If columns that are part of the key have not
     * been modified, then we can use a weaker lock, allowing for better
     * concurrency.
     */
    modifiedCols = GetModifiedColumns(relinfo, estate);
    keyCols = RelationGetIndexAttrBitmap(relinfo->ri_RelationDesc, true);
    if (bms_overlap(keyCols, modifiedCols))
        lockmode = LockTupleExclusive;
    else
        lockmode = LockTupleNoKeyExclusive;

    /* get a copy of the on-disk tuple we are planning to update */
    trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
                                   lockmode, &newSlot);
    if (trigtuple == NULL)
        return NULL;            /* cancel the update action */

    /*
     * In READ COMMITTED isolation level it's possible that target tuple was
     * changed due to concurrent update.  In that case we have a raw subplan
     * output tuple in newSlot, and need to run it through the junk filter to
     * produce an insertable tuple.
     *
     * Caution: more than likely, the passed-in slot is the same as the
     * junkfilter's output slot, so we are clobbering the original value of
     * slottuple by doing the filtering.  This is OK since neither we nor our
     * caller have any more interest in the prior contents of that slot.
     */
    if (newSlot != NULL)
    {
        slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot);
        slottuple = ExecMaterializeSlot(slot);
        newtuple = slottuple;
    }


    LocTriggerData.type = T_TriggerData;
    LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
        TRIGGER_EVENT_ROW |
        TRIGGER_EVENT_BEFORE;
    LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
    for (i = 0; i < trigdesc->numtriggers; i++)
    {
        Trigger    *trigger = &trigdesc->triggers[i];

        if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
                                  TRIGGER_TYPE_ROW,
                                  TRIGGER_TYPE_BEFORE,
                                  TRIGGER_TYPE_UPDATE))
            continue;
        if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
                            modifiedCols, trigtuple, newtuple))
            continue;

        LocTriggerData.tg_trigtuple = trigtuple;
        LocTriggerData.tg_newtuple = oldtuple = newtuple;
        LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
        LocTriggerData.tg_newtuplebuf = InvalidBuffer;
        LocTriggerData.tg_trigger = trigger;
        newtuple = ExecCallTriggerFunc(&LocTriggerData,
                                       i,
                                       relinfo->ri_TrigFunctions,
                                       relinfo->ri_TrigInstrument,
                                       GetPerTupleMemoryContext(estate));
        if (oldtuple != newtuple && oldtuple != slottuple)
            heap_freetuple(oldtuple);
        if (newtuple == NULL)
        {
            heap_freetuple(trigtuple);
            return NULL;        /* "do nothing" */
        }
    }
    heap_freetuple(trigtuple);

    if (newtuple != slottuple)
    {
        /*
         * Return the modified tuple using the es_trig_tuple_slot.  We assume
         * the tuple was allocated in per-tuple memory context, and therefore
         * will go away by itself. The tuple table slot should not try to
         * clear it.
         */
        TupleTableSlot *newslot = estate->es_trig_tuple_slot;
        TupleDesc   tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);

        if (newslot->tts_tupleDescriptor != tupdesc)
            ExecSetSlotDescriptor(newslot, tupdesc);
        ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
        slot = newslot;
    }
    return slot;
}

void ExecBSDeleteTriggers ( EState estate,
ResultRelInfo relinfo 
)

Definition at line 2086 of file trigger.c.

References ereport, errcode(), errmsg(), ERROR, ExecCallTriggerFunc(), GetPerTupleMemoryContext, NULL, TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgtype, TriggerDesc::trig_delete_before_statement, TRIGGER_EVENT_DELETE, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_DELETE, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_STATEMENT, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

Referenced by fireBSTriggers().

{
    TriggerDesc *trigdesc;
    int         i;
    TriggerData LocTriggerData;

    trigdesc = relinfo->ri_TrigDesc;

    if (trigdesc == NULL)
        return;
    if (!trigdesc->trig_delete_before_statement)
        return;

    LocTriggerData.type = T_TriggerData;
    LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
        TRIGGER_EVENT_BEFORE;
    LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
    LocTriggerData.tg_trigtuple = NULL;
    LocTriggerData.tg_newtuple = NULL;
    LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
    LocTriggerData.tg_newtuplebuf = InvalidBuffer;
    for (i = 0; i < trigdesc->numtriggers; i++)
    {
        Trigger    *trigger = &trigdesc->triggers[i];
        HeapTuple   newtuple;

        if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
                                  TRIGGER_TYPE_STATEMENT,
                                  TRIGGER_TYPE_BEFORE,
                                  TRIGGER_TYPE_DELETE))
            continue;
        if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
                            NULL, NULL, NULL))
            continue;

        LocTriggerData.tg_trigger = trigger;
        newtuple = ExecCallTriggerFunc(&LocTriggerData,
                                       i,
                                       relinfo->ri_TrigFunctions,
                                       relinfo->ri_TrigInstrument,
                                       GetPerTupleMemoryContext(estate));

        if (newtuple)
            ereport(ERROR,
                    (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
                  errmsg("BEFORE STATEMENT trigger cannot return a value")));
    }
}

void ExecBSInsertTriggers ( EState estate,
ResultRelInfo relinfo 
)

Definition at line 1887 of file trigger.c.

References ereport, errcode(), errmsg(), ERROR, ExecCallTriggerFunc(), GetPerTupleMemoryContext, NULL, TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgtype, TriggerDesc::trig_insert_before_statement, TRIGGER_EVENT_INSERT, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_INSERT, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_STATEMENT, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

Referenced by CopyFrom(), and fireBSTriggers().

{
    TriggerDesc *trigdesc;
    int         i;
    TriggerData LocTriggerData;

    trigdesc = relinfo->ri_TrigDesc;

    if (trigdesc == NULL)
        return;
    if (!trigdesc->trig_insert_before_statement)
        return;

    LocTriggerData.type = T_TriggerData;
    LocTriggerData.tg_event = TRIGGER_EVENT_INSERT |
        TRIGGER_EVENT_BEFORE;
    LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
    LocTriggerData.tg_trigtuple = NULL;
    LocTriggerData.tg_newtuple = NULL;
    LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
    LocTriggerData.tg_newtuplebuf = InvalidBuffer;
    for (i = 0; i < trigdesc->numtriggers; i++)
    {
        Trigger    *trigger = &trigdesc->triggers[i];
        HeapTuple   newtuple;

        if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
                                  TRIGGER_TYPE_STATEMENT,
                                  TRIGGER_TYPE_BEFORE,
                                  TRIGGER_TYPE_INSERT))
            continue;
        if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
                            NULL, NULL, NULL))
            continue;

        LocTriggerData.tg_trigger = trigger;
        newtuple = ExecCallTriggerFunc(&LocTriggerData,
                                       i,
                                       relinfo->ri_TrigFunctions,
                                       relinfo->ri_TrigInstrument,
                                       GetPerTupleMemoryContext(estate));

        if (newtuple)
            ereport(ERROR,
                    (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
                  errmsg("BEFORE STATEMENT trigger cannot return a value")));
    }
}

void ExecBSTruncateTriggers ( EState estate,
ResultRelInfo relinfo 
)

Definition at line 2527 of file trigger.c.

References ereport, errcode(), errmsg(), ERROR, ExecCallTriggerFunc(), GetPerTupleMemoryContext, NULL, TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgtype, TriggerDesc::trig_truncate_before_statement, TRIGGER_EVENT_TRUNCATE, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_STATEMENT, TRIGGER_TYPE_TRUNCATE, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

Referenced by ExecuteTruncate().

{
    TriggerDesc *trigdesc;
    int         i;
    TriggerData LocTriggerData;

    trigdesc = relinfo->ri_TrigDesc;

    if (trigdesc == NULL)
        return;
    if (!trigdesc->trig_truncate_before_statement)
        return;

    LocTriggerData.type = T_TriggerData;
    LocTriggerData.tg_event = TRIGGER_EVENT_TRUNCATE |
        TRIGGER_EVENT_BEFORE;
    LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
    LocTriggerData.tg_trigtuple = NULL;
    LocTriggerData.tg_newtuple = NULL;
    LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
    LocTriggerData.tg_newtuplebuf = InvalidBuffer;
    for (i = 0; i < trigdesc->numtriggers; i++)
    {
        Trigger    *trigger = &trigdesc->triggers[i];
        HeapTuple   newtuple;

        if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
                                  TRIGGER_TYPE_STATEMENT,
                                  TRIGGER_TYPE_BEFORE,
                                  TRIGGER_TYPE_TRUNCATE))
            continue;
        if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
                            NULL, NULL, NULL))
            continue;

        LocTriggerData.tg_trigger = trigger;
        newtuple = ExecCallTriggerFunc(&LocTriggerData,
                                       i,
                                       relinfo->ri_TrigFunctions,
                                       relinfo->ri_TrigInstrument,
                                       GetPerTupleMemoryContext(estate));

        if (newtuple)
            ereport(ERROR,
                    (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
                  errmsg("BEFORE STATEMENT trigger cannot return a value")));
    }
}

void ExecBSUpdateTriggers ( EState estate,
ResultRelInfo relinfo 
)

Definition at line 2268 of file trigger.c.

References ereport, errcode(), errmsg(), ERROR, ExecCallTriggerFunc(), GetModifiedColumns, GetPerTupleMemoryContext, NULL, TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgtype, TriggerDesc::trig_update_before_statement, TRIGGER_EVENT_UPDATE, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_STATEMENT, TRIGGER_TYPE_UPDATE, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

Referenced by fireBSTriggers().

{
    TriggerDesc *trigdesc;
    int         i;
    TriggerData LocTriggerData;
    Bitmapset  *modifiedCols;

    trigdesc = relinfo->ri_TrigDesc;

    if (trigdesc == NULL)
        return;
    if (!trigdesc->trig_update_before_statement)
        return;

    modifiedCols = GetModifiedColumns(relinfo, estate);

    LocTriggerData.type = T_TriggerData;
    LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
        TRIGGER_EVENT_BEFORE;
    LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
    LocTriggerData.tg_trigtuple = NULL;
    LocTriggerData.tg_newtuple = NULL;
    LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
    LocTriggerData.tg_newtuplebuf = InvalidBuffer;
    for (i = 0; i < trigdesc->numtriggers; i++)
    {
        Trigger    *trigger = &trigdesc->triggers[i];
        HeapTuple   newtuple;

        if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
                                  TRIGGER_TYPE_STATEMENT,
                                  TRIGGER_TYPE_BEFORE,
                                  TRIGGER_TYPE_UPDATE))
            continue;
        if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
                            modifiedCols, NULL, NULL))
            continue;

        LocTriggerData.tg_trigger = trigger;
        newtuple = ExecCallTriggerFunc(&LocTriggerData,
                                       i,
                                       relinfo->ri_TrigFunctions,
                                       relinfo->ri_TrigInstrument,
                                       GetPerTupleMemoryContext(estate));

        if (newtuple)
            ereport(ERROR,
                    (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
                  errmsg("BEFORE STATEMENT trigger cannot return a value")));
    }
}

bool ExecIRDeleteTriggers ( EState estate,
ResultRelInfo relinfo,
HeapTuple  trigtuple 
)

Definition at line 2223 of file trigger.c.

References ExecCallTriggerFunc(), GetPerTupleMemoryContext, heap_freetuple(), NULL, TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgtype, TRIGGER_EVENT_DELETE, TRIGGER_EVENT_ROW, TRIGGER_TYPE_DELETE, TRIGGER_TYPE_INSTEAD, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_ROW, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

Referenced by ExecDelete().

{
    TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
    TriggerData LocTriggerData;
    HeapTuple   rettuple;
    int         i;

    LocTriggerData.type = T_TriggerData;
    LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
        TRIGGER_EVENT_ROW |
        TRIGGER_EVENT_INSTEAD;
    LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
    LocTriggerData.tg_newtuple = NULL;
    LocTriggerData.tg_newtuplebuf = InvalidBuffer;
    for (i = 0; i < trigdesc->numtriggers; i++)
    {
        Trigger    *trigger = &trigdesc->triggers[i];

        if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
                                  TRIGGER_TYPE_ROW,
                                  TRIGGER_TYPE_INSTEAD,
                                  TRIGGER_TYPE_DELETE))
            continue;
        if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
                            NULL, trigtuple, NULL))
            continue;

        LocTriggerData.tg_trigtuple = trigtuple;
        LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
        LocTriggerData.tg_trigger = trigger;
        rettuple = ExecCallTriggerFunc(&LocTriggerData,
                                       i,
                                       relinfo->ri_TrigFunctions,
                                       relinfo->ri_TrigInstrument,
                                       GetPerTupleMemoryContext(estate));
        if (rettuple == NULL)
            return false;       /* Delete was suppressed */
        if (rettuple != trigtuple)
            heap_freetuple(rettuple);
    }
    return true;
}

TupleTableSlot* ExecIRInsertTriggers ( EState estate,
ResultRelInfo relinfo,
TupleTableSlot slot 
)

Definition at line 2022 of file trigger.c.

References EState::es_trig_tuple_slot, ExecCallTriggerFunc(), ExecMaterializeSlot(), ExecSetSlotDescriptor(), ExecStoreTuple(), GetPerTupleMemoryContext, heap_freetuple(), InvalidBuffer, NULL, TriggerDesc::numtriggers, RelationGetDescr, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgtype, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_ROW, TRIGGER_TYPE_INSERT, TRIGGER_TYPE_INSTEAD, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_ROW, TriggerEnabled(), TriggerDesc::triggers, TupleTableSlot::tts_tupleDescriptor, and TriggerData::type.

Referenced by ExecInsert().

{
    TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
    HeapTuple   slottuple = ExecMaterializeSlot(slot);
    HeapTuple   newtuple = slottuple;
    HeapTuple   oldtuple;
    TriggerData LocTriggerData;
    int         i;

    LocTriggerData.type = T_TriggerData;
    LocTriggerData.tg_event = TRIGGER_EVENT_INSERT |
        TRIGGER_EVENT_ROW |
        TRIGGER_EVENT_INSTEAD;
    LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
    LocTriggerData.tg_newtuple = NULL;
    LocTriggerData.tg_newtuplebuf = InvalidBuffer;
    for (i = 0; i < trigdesc->numtriggers; i++)
    {
        Trigger    *trigger = &trigdesc->triggers[i];

        if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
                                  TRIGGER_TYPE_ROW,
                                  TRIGGER_TYPE_INSTEAD,
                                  TRIGGER_TYPE_INSERT))
            continue;
        if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
                            NULL, NULL, newtuple))
            continue;

        LocTriggerData.tg_trigtuple = oldtuple = newtuple;
        LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
        LocTriggerData.tg_trigger = trigger;
        newtuple = ExecCallTriggerFunc(&LocTriggerData,
                                       i,
                                       relinfo->ri_TrigFunctions,
                                       relinfo->ri_TrigInstrument,
                                       GetPerTupleMemoryContext(estate));
        if (oldtuple != newtuple && oldtuple != slottuple)
            heap_freetuple(oldtuple);
        if (newtuple == NULL)
            return NULL;        /* "do nothing" */
    }

    if (newtuple != slottuple)
    {
        /*
         * Return the modified tuple using the es_trig_tuple_slot.  We assume
         * the tuple was allocated in per-tuple memory context, and therefore
         * will go away by itself. The tuple table slot should not try to
         * clear it.
         */
        TupleTableSlot *newslot = estate->es_trig_tuple_slot;
        TupleDesc   tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);

        if (newslot->tts_tupleDescriptor != tupdesc)
            ExecSetSlotDescriptor(newslot, tupdesc);
        ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
        slot = newslot;
    }
    return slot;
}

TupleTableSlot* ExecIRUpdateTriggers ( EState estate,
ResultRelInfo relinfo,
HeapTuple  trigtuple,
TupleTableSlot slot 
)

Definition at line 2463 of file trigger.c.

References EState::es_trig_tuple_slot, ExecCallTriggerFunc(), ExecMaterializeSlot(), ExecSetSlotDescriptor(), ExecStoreTuple(), GetPerTupleMemoryContext, heap_freetuple(), InvalidBuffer, NULL, TriggerDesc::numtriggers, RelationGetDescr, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgtype, TRIGGER_EVENT_ROW, TRIGGER_EVENT_UPDATE, TRIGGER_TYPE_INSTEAD, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_ROW, TRIGGER_TYPE_UPDATE, TriggerEnabled(), TriggerDesc::triggers, TupleTableSlot::tts_tupleDescriptor, and TriggerData::type.

Referenced by ExecUpdate().

{
    TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
    HeapTuple   slottuple = ExecMaterializeSlot(slot);
    HeapTuple   newtuple = slottuple;
    TriggerData LocTriggerData;
    HeapTuple   oldtuple;
    int         i;

    LocTriggerData.type = T_TriggerData;
    LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
        TRIGGER_EVENT_ROW |
        TRIGGER_EVENT_INSTEAD;
    LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
    for (i = 0; i < trigdesc->numtriggers; i++)
    {
        Trigger    *trigger = &trigdesc->triggers[i];

        if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
                                  TRIGGER_TYPE_ROW,
                                  TRIGGER_TYPE_INSTEAD,
                                  TRIGGER_TYPE_UPDATE))
            continue;
        if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
                            NULL, trigtuple, newtuple))
            continue;

        LocTriggerData.tg_trigtuple = trigtuple;
        LocTriggerData.tg_newtuple = oldtuple = newtuple;
        LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
        LocTriggerData.tg_newtuplebuf = InvalidBuffer;
        LocTriggerData.tg_trigger = trigger;
        newtuple = ExecCallTriggerFunc(&LocTriggerData,
                                       i,
                                       relinfo->ri_TrigFunctions,
                                       relinfo->ri_TrigInstrument,
                                       GetPerTupleMemoryContext(estate));
        if (oldtuple != newtuple && oldtuple != slottuple)
            heap_freetuple(oldtuple);
        if (newtuple == NULL)
            return NULL;        /* "do nothing" */
    }

    if (newtuple != slottuple)
    {
        /*
         * Return the modified tuple using the es_trig_tuple_slot.  We assume
         * the tuple was allocated in per-tuple memory context, and therefore
         * will go away by itself. The tuple table slot should not try to
         * clear it.
         */
        TupleTableSlot *newslot = estate->es_trig_tuple_slot;
        TupleDesc   tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);

        if (newslot->tts_tupleDescriptor != tupdesc)
            ExecSetSlotDescriptor(newslot, tupdesc);
        ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
        slot = newslot;
    }
    return slot;
}

void FreeTriggerDesc ( TriggerDesc trigdesc  ) 

Definition at line 1685 of file trigger.c.

References i, NULL, TriggerDesc::numtriggers, pfree(), Trigger::tgargs, Trigger::tgattr, Trigger::tgname, Trigger::tgnargs, Trigger::tgnattr, Trigger::tgqual, and TriggerDesc::triggers.

Referenced by RelationBuildTriggers(), and RelationDestroyRelation().

{
    Trigger    *trigger;
    int         i;

    if (trigdesc == NULL)
        return;

    trigger = trigdesc->triggers;
    for (i = 0; i < trigdesc->numtriggers; i++)
    {
        pfree(trigger->tgname);
        if (trigger->tgnattr > 0)
            pfree(trigger->tgattr);
        if (trigger->tgnargs > 0)
        {
            while (--(trigger->tgnargs) >= 0)
                pfree(trigger->tgargs[trigger->tgnargs]);
            pfree(trigger->tgargs);
        }
        if (trigger->tgqual)
            pfree(trigger->tgqual);
        trigger++;
    }
    pfree(trigdesc->triggers);
    pfree(trigdesc);
}

Oid get_trigger_oid ( Oid  relid,
const char *  name,
bool  missing_ok 
)

Definition at line 1107 of file trigger.c.

References AccessShareLock, Anum_pg_trigger_tgname, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, CStringGetDatum, ereport, errcode(), errmsg(), ERROR, get_rel_name(), heap_close, heap_open(), HeapTupleGetOid, HeapTupleIsValid, ObjectIdGetDatum, ScanKeyInit(), SnapshotNow, systable_beginscan(), systable_endscan(), systable_getnext(), TriggerRelationId, and TriggerRelidNameIndexId.

Referenced by get_object_address_relobject().

{
    Relation    tgrel;
    ScanKeyData skey[2];
    SysScanDesc tgscan;
    HeapTuple   tup;
    Oid         oid;

    /*
     * Find the trigger, verify permissions, set up object address
     */
    tgrel = heap_open(TriggerRelationId, AccessShareLock);

    ScanKeyInit(&skey[0],
                Anum_pg_trigger_tgrelid,
                BTEqualStrategyNumber, F_OIDEQ,
                ObjectIdGetDatum(relid));
    ScanKeyInit(&skey[1],
                Anum_pg_trigger_tgname,
                BTEqualStrategyNumber, F_NAMEEQ,
                CStringGetDatum(trigname));

    tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
                                SnapshotNow, 2, skey);

    tup = systable_getnext(tgscan);

    if (!HeapTupleIsValid(tup))
    {
        if (!missing_ok)
            ereport(ERROR,
                    (errcode(ERRCODE_UNDEFINED_OBJECT),
                     errmsg("trigger \"%s\" for table \"%s\" does not exist",
                            trigname, get_rel_name(relid))));
        oid = InvalidOid;
    }
    else
    {
        oid = HeapTupleGetOid(tup);
    }

    systable_endscan(tgscan);
    heap_close(tgrel, AccessShareLock);
    return oid;
}

Datum pg_trigger_depth ( PG_FUNCTION_ARGS   ) 

Definition at line 4690 of file trigger.c.

References MyTriggerDepth, and PG_RETURN_INT32.

void RelationBuildTriggers ( Relation  relation  ) 

Definition at line 1436 of file trigger.c.

References AccessShareLock, Anum_pg_trigger_tgargs, Anum_pg_trigger_tgqual, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, CacheMemoryContext, CopyTriggerDesc(), DatumGetByteaP, DatumGetCString, DirectFunctionCall1, elog, ERROR, fastgetattr, FreeTriggerDesc(), GETSTRUCT, heap_close, heap_open(), HeapTupleGetOid, HeapTupleIsValid, i, MemoryContextSwitchTo(), NameGetDatum, nameout(), TriggerDesc::numtriggers, ObjectIdGetDatum, palloc(), palloc0(), pfree(), pstrdup(), RelationData::rd_att, RelationGetRelationName, RelationGetRelid, repalloc(), ScanKeyInit(), SetTriggerFlags(), SnapshotNow, systable_beginscan(), systable_endscan(), systable_getnext(), TextDatumGetCString, Trigger::tgargs, Trigger::tgattr, Trigger::tgconstraint, Trigger::tgconstrindid, Trigger::tgconstrrelid, Trigger::tgdeferrable, Trigger::tgenabled, Trigger::tgfoid, Trigger::tginitdeferred, Trigger::tgisinternal, Trigger::tgname, Trigger::tgnargs, Trigger::tgnattr, Trigger::tgoid, Trigger::tgqual, Trigger::tgtype, RelationData::trigdesc, TriggerRelationId, TriggerRelidNameIndexId, TriggerDesc::triggers, val, and VARDATA.

Referenced by RelationBuildDesc(), and RelationCacheInitializePhase3().

{
    TriggerDesc *trigdesc;
    int         numtrigs;
    int         maxtrigs;
    Trigger    *triggers;
    Relation    tgrel;
    ScanKeyData skey;
    SysScanDesc tgscan;
    HeapTuple   htup;
    MemoryContext oldContext;
    int         i;

    /*
     * Allocate a working array to hold the triggers (the array is extended if
     * necessary)
     */
    maxtrigs = 16;
    triggers = (Trigger *) palloc(maxtrigs * sizeof(Trigger));
    numtrigs = 0;

    /*
     * Note: since we scan the triggers using TriggerRelidNameIndexId, we will
     * be reading the triggers in name order, except possibly during
     * emergency-recovery operations (ie, IgnoreSystemIndexes). This in turn
     * ensures that triggers will be fired in name order.
     */
    ScanKeyInit(&skey,
                Anum_pg_trigger_tgrelid,
                BTEqualStrategyNumber, F_OIDEQ,
                ObjectIdGetDatum(RelationGetRelid(relation)));

    tgrel = heap_open(TriggerRelationId, AccessShareLock);
    tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
                                SnapshotNow, 1, &skey);

    while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
    {
        Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
        Trigger    *build;
        Datum       datum;
        bool        isnull;

        if (numtrigs >= maxtrigs)
        {
            maxtrigs *= 2;
            triggers = (Trigger *) repalloc(triggers, maxtrigs * sizeof(Trigger));
        }
        build = &(triggers[numtrigs]);

        build->tgoid = HeapTupleGetOid(htup);
        build->tgname = DatumGetCString(DirectFunctionCall1(nameout,
                                         NameGetDatum(&pg_trigger->tgname)));
        build->tgfoid = pg_trigger->tgfoid;
        build->tgtype = pg_trigger->tgtype;
        build->tgenabled = pg_trigger->tgenabled;
        build->tgisinternal = pg_trigger->tgisinternal;
        build->tgconstrrelid = pg_trigger->tgconstrrelid;
        build->tgconstrindid = pg_trigger->tgconstrindid;
        build->tgconstraint = pg_trigger->tgconstraint;
        build->tgdeferrable = pg_trigger->tgdeferrable;
        build->tginitdeferred = pg_trigger->tginitdeferred;
        build->tgnargs = pg_trigger->tgnargs;
        /* tgattr is first var-width field, so OK to access directly */
        build->tgnattr = pg_trigger->tgattr.dim1;
        if (build->tgnattr > 0)
        {
            build->tgattr = (int16 *) palloc(build->tgnattr * sizeof(int16));
            memcpy(build->tgattr, &(pg_trigger->tgattr.values),
                   build->tgnattr * sizeof(int16));
        }
        else
            build->tgattr = NULL;
        if (build->tgnargs > 0)
        {
            bytea      *val;
            char       *p;

            val = DatumGetByteaP(fastgetattr(htup,
                                             Anum_pg_trigger_tgargs,
                                             tgrel->rd_att, &isnull));
            if (isnull)
                elog(ERROR, "tgargs is null in trigger for relation \"%s\"",
                     RelationGetRelationName(relation));
            p = (char *) VARDATA(val);
            build->tgargs = (char **) palloc(build->tgnargs * sizeof(char *));
            for (i = 0; i < build->tgnargs; i++)
            {
                build->tgargs[i] = pstrdup(p);
                p += strlen(p) + 1;
            }
        }
        else
            build->tgargs = NULL;
        datum = fastgetattr(htup, Anum_pg_trigger_tgqual,
                            tgrel->rd_att, &isnull);
        if (!isnull)
            build->tgqual = TextDatumGetCString(datum);
        else
            build->tgqual = NULL;

        numtrigs++;
    }

    systable_endscan(tgscan);
    heap_close(tgrel, AccessShareLock);

    /* There might not be any triggers */
    if (numtrigs == 0)
    {
        pfree(triggers);
        return;
    }

    /* Build trigdesc */
    trigdesc = (TriggerDesc *) palloc0(sizeof(TriggerDesc));
    trigdesc->triggers = triggers;
    trigdesc->numtriggers = numtrigs;
    for (i = 0; i < numtrigs; i++)
        SetTriggerFlags(trigdesc, &(triggers[i]));

    /* Copy completed trigdesc into cache storage */
    oldContext = MemoryContextSwitchTo(CacheMemoryContext);
    relation->trigdesc = CopyTriggerDesc(trigdesc);
    MemoryContextSwitchTo(oldContext);

    /* Release working memory */
    FreeTriggerDesc(trigdesc);
}

void RemoveTriggerById ( Oid  trigOid  ) 

Definition at line 1031 of file trigger.c.

References AccessExclusiveLock, allowSystemTableMods, BTEqualStrategyNumber, CacheInvalidateRelcache(), elog, ereport, errcode(), errmsg(), ERROR, GETSTRUCT, heap_close, heap_open(), HeapTupleIsValid, IsSystemRelation(), NoLock, ObjectIdAttributeNumber, ObjectIdGetDatum, RelationData::rd_rel, RelationGetRelationName, RELKIND_RELATION, RELKIND_VIEW, RowExclusiveLock, ScanKeyInit(), simple_heap_delete(), SnapshotNow, systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, TriggerOidIndexId, and TriggerRelationId.

Referenced by doDeletion().

{
    Relation    tgrel;
    SysScanDesc tgscan;
    ScanKeyData skey[1];
    HeapTuple   tup;
    Oid         relid;
    Relation    rel;

    tgrel = heap_open(TriggerRelationId, RowExclusiveLock);

    /*
     * Find the trigger to delete.
     */
    ScanKeyInit(&skey[0],
                ObjectIdAttributeNumber,
                BTEqualStrategyNumber, F_OIDEQ,
                ObjectIdGetDatum(trigOid));

    tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true,
                                SnapshotNow, 1, skey);

    tup = systable_getnext(tgscan);
    if (!HeapTupleIsValid(tup))
        elog(ERROR, "could not find tuple for trigger %u", trigOid);

    /*
     * Open and exclusive-lock the relation the trigger belongs to.
     */
    relid = ((Form_pg_trigger) GETSTRUCT(tup))->tgrelid;

    rel = heap_open(relid, AccessExclusiveLock);

    if (rel->rd_rel->relkind != RELKIND_RELATION &&
        rel->rd_rel->relkind != RELKIND_VIEW)
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                 errmsg("\"%s\" is not a table or view",
                        RelationGetRelationName(rel))));

    if (!allowSystemTableMods && IsSystemRelation(rel))
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                 errmsg("permission denied: \"%s\" is a system catalog",
                        RelationGetRelationName(rel))));

    /*
     * Delete the pg_trigger tuple.
     */
    simple_heap_delete(tgrel, &tup->t_self);

    systable_endscan(tgscan);
    heap_close(tgrel, RowExclusiveLock);

    /*
     * We do not bother to try to determine whether any other triggers remain,
     * which would be needed in order to decide whether it's safe to clear the
     * relation's relhastriggers.  (In any case, there might be a concurrent
     * process adding new triggers.)  Instead, just force a relcache inval to
     * make other backends (and this one too!) rebuild their relcache entries.
     * There's no great harm in leaving relhastriggers true even if there are
     * no triggers left.
     */
    CacheInvalidateRelcache(rel);

    /* Keep lock on trigger's rel until end of xact */
    heap_close(rel, NoLock);
}

Oid renametrig ( RenameStmt stmt  ) 

Definition at line 1200 of file trigger.c.

References AccessExclusiveLock, Anum_pg_trigger_tgname, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, CacheInvalidateRelcache(), CatalogUpdateIndexes(), ereport, errcode(), errmsg(), ERROR, GETSTRUCT, heap_close, heap_copytuple(), heap_open(), HeapTupleGetOid, HeapTupleIsValid, InvokeObjectPostAlterHook, namestrcpy(), RenameStmt::newname, NoLock, NULL, ObjectIdGetDatum, PointerGetDatum, RangeVarCallbackForRenameTrigger(), RangeVarGetRelidExtended(), RenameStmt::relation, relation_close(), relation_open(), RelationGetRelationName, RowExclusiveLock, ScanKeyInit(), simple_heap_update(), SnapshotNow, RenameStmt::subname, systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, TriggerRelationId, and TriggerRelidNameIndexId.

Referenced by ExecRenameStmt().

{
    Oid         tgoid;
    Relation    targetrel;
    Relation    tgrel;
    HeapTuple   tuple;
    SysScanDesc tgscan;
    ScanKeyData key[2];
    Oid         relid;

    /*
     * Look up name, check permissions, and acquire lock (which we will NOT
     * release until end of transaction).
     */
    relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
                                     false, false,
                                     RangeVarCallbackForRenameTrigger,
                                     NULL);

    /* Have lock already, so just need to build relcache entry. */
    targetrel = relation_open(relid, NoLock);

    /*
     * Scan pg_trigger twice for existing triggers on relation.  We do this in
     * order to ensure a trigger does not exist with newname (The unique index
     * on tgrelid/tgname would complain anyway) and to ensure a trigger does
     * exist with oldname.
     *
     * NOTE that this is cool only because we have AccessExclusiveLock on the
     * relation, so the trigger set won't be changing underneath us.
     */
    tgrel = heap_open(TriggerRelationId, RowExclusiveLock);

    /*
     * First pass -- look for name conflict
     */
    ScanKeyInit(&key[0],
                Anum_pg_trigger_tgrelid,
                BTEqualStrategyNumber, F_OIDEQ,
                ObjectIdGetDatum(relid));
    ScanKeyInit(&key[1],
                Anum_pg_trigger_tgname,
                BTEqualStrategyNumber, F_NAMEEQ,
                PointerGetDatum(stmt->newname));
    tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
                                SnapshotNow, 2, key);
    if (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
        ereport(ERROR,
                (errcode(ERRCODE_DUPLICATE_OBJECT),
                 errmsg("trigger \"%s\" for relation \"%s\" already exists",
                        stmt->newname, RelationGetRelationName(targetrel))));
    systable_endscan(tgscan);

    /*
     * Second pass -- look for trigger existing with oldname and update
     */
    ScanKeyInit(&key[0],
                Anum_pg_trigger_tgrelid,
                BTEqualStrategyNumber, F_OIDEQ,
                ObjectIdGetDatum(relid));
    ScanKeyInit(&key[1],
                Anum_pg_trigger_tgname,
                BTEqualStrategyNumber, F_NAMEEQ,
                PointerGetDatum(stmt->subname));
    tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
                                SnapshotNow, 2, key);
    if (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
    {
        tgoid = HeapTupleGetOid(tuple);
        /*
         * Update pg_trigger tuple with new tgname.
         */
        tuple = heap_copytuple(tuple);  /* need a modifiable copy */

        namestrcpy(&((Form_pg_trigger) GETSTRUCT(tuple))->tgname,
                   stmt->newname);

        simple_heap_update(tgrel, &tuple->t_self, tuple);

        /* keep system catalog indexes current */
        CatalogUpdateIndexes(tgrel, tuple);

        InvokeObjectPostAlterHook(TriggerRelationId,
                                  HeapTupleGetOid(tuple), 0);

        /*
         * Invalidate relation's relcache entry so that other backends (and
         * this one too!) are sent SI message to make them rebuild relcache
         * entries.  (Ideally this should happen automatically...)
         */
        CacheInvalidateRelcache(targetrel);
    }
    else
    {
        ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_OBJECT),
                 errmsg("trigger \"%s\" for table \"%s\" does not exist",
                        stmt->subname, RelationGetRelationName(targetrel))));
    }

    systable_endscan(tgscan);

    heap_close(tgrel, RowExclusiveLock);

    /*
     * Close rel, but keep exclusive lock!
     */
    relation_close(targetrel, NoLock);

    return tgoid;
}

bool RI_FKey_fk_upd_check_required ( Trigger trigger,
Relation  fk_rel,
HeapTuple  old_row,
HeapTuple  new_row 
)

Definition at line 2140 of file ri_triggers.c.

References RI_ConstraintInfo::confmatchtype, elog, ereport, errcode(), errmsg(), ERROR, FKCONSTR_MATCH_FULL, FKCONSTR_MATCH_PARTIAL, FKCONSTR_MATCH_SIMPLE, HeapTupleHeaderGetXmin, ri_FetchConstraintInfo(), RI_KEYS_ALL_NULL, RI_KEYS_NONE_NULL, RI_KEYS_SOME_NULL, ri_KeysEqual(), ri_NullCheck(), HeapTupleData::t_data, and TransactionIdIsCurrentTransactionId().

Referenced by AfterTriggerSaveEvent().

{
    const RI_ConstraintInfo *riinfo;

    /*
     * Get arguments.
     */
    riinfo = ri_FetchConstraintInfo(trigger, fk_rel, false);

    switch (riinfo->confmatchtype)
    {
        case FKCONSTR_MATCH_SIMPLE:
            /*
             * If any new key value is NULL, the row must satisfy the
             * constraint, so no check is needed.
             */
            if (ri_NullCheck(new_row, riinfo, false) != RI_KEYS_NONE_NULL)
                return false;

            /*
             * If the original row was inserted by our own transaction, we
             * must fire the trigger whether or not the keys are equal.  This
             * is because our UPDATE will invalidate the INSERT so that the
             * INSERT RI trigger will not do anything; so we had better do the
             * UPDATE check.  (We could skip this if we knew the INSERT
             * trigger already fired, but there is no easy way to know that.)
             */
            if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
                return true;

            /* If all old and new key values are equal, no check is needed */
            if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false))
                return false;

            /* Else we need to fire the trigger. */
            return true;

        case FKCONSTR_MATCH_FULL:
            /*
             * If all new key values are NULL, the row must satisfy the
             * constraint, so no check is needed.  On the other hand, if only
             * some of them are NULL, the row must fail the constraint.  We
             * must not throw error here, because the row might get
             * invalidated before the constraint is to be checked, but we
             * should queue the event to apply the check later.
             */
            switch (ri_NullCheck(new_row, riinfo, false))
            {
                case RI_KEYS_ALL_NULL:
                    return false;
                case RI_KEYS_SOME_NULL:
                    return true;
                case RI_KEYS_NONE_NULL:
                    break;      /* continue with the check */
            }

            /*
             * If the original row was inserted by our own transaction, we
             * must fire the trigger whether or not the keys are equal.  This
             * is because our UPDATE will invalidate the INSERT so that the
             * INSERT RI trigger will not do anything; so we had better do the
             * UPDATE check.  (We could skip this if we knew the INSERT
             * trigger already fired, but there is no easy way to know that.)
             */
            if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
                return true;

            /* If all old and new key values are equal, no check is needed */
            if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false))
                return false;

            /* Else we need to fire the trigger. */
            return true;

            /* Handle MATCH PARTIAL check. */
        case FKCONSTR_MATCH_PARTIAL:
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                     errmsg("MATCH PARTIAL not yet implemented")));
            break;

        default:
            elog(ERROR, "unrecognized confmatchtype: %d",
                 riinfo->confmatchtype);
            break;
    }

    /* Never reached */
    return false;
}

bool RI_FKey_pk_upd_check_required ( Trigger trigger,
Relation  pk_rel,
HeapTuple  old_row,
HeapTuple  new_row 
)

Definition at line 2083 of file ri_triggers.c.

References RI_ConstraintInfo::confmatchtype, elog, ereport, errcode(), errmsg(), ERROR, FKCONSTR_MATCH_FULL, FKCONSTR_MATCH_PARTIAL, FKCONSTR_MATCH_SIMPLE, ri_FetchConstraintInfo(), RI_KEYS_NONE_NULL, ri_KeysEqual(), and ri_NullCheck().

Referenced by AfterTriggerSaveEvent().

{
    const RI_ConstraintInfo *riinfo;

    /*
     * Get arguments.
     */
    riinfo = ri_FetchConstraintInfo(trigger, pk_rel, true);

    switch (riinfo->confmatchtype)
    {
        case FKCONSTR_MATCH_SIMPLE:
        case FKCONSTR_MATCH_FULL:

            /*
             * If any old key value is NULL, the row could not have been
             * referenced by an FK row, so no check is needed.
             */
            if (ri_NullCheck(old_row, riinfo, true) != RI_KEYS_NONE_NULL)
                return false;

            /* If all old and new key values are equal, no check is needed */
            if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
                return false;

            /* Else we need to fire the trigger. */
            return true;

            /* Handle MATCH PARTIAL check. */
        case FKCONSTR_MATCH_PARTIAL:
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                     errmsg("MATCH PARTIAL not yet implemented")));
            break;

        default:
            elog(ERROR, "unrecognized confmatchtype: %d",
                 riinfo->confmatchtype);
            break;
    }

    /* Never reached */
    return false;
}

int RI_FKey_trigger_type ( Oid  tgfoid  ) 

Definition at line 3611 of file ri_triggers.c.

Referenced by AfterTriggerSaveEvent(), and CreateTrigger().

{
    switch (tgfoid)
    {
        case F_RI_FKEY_CASCADE_DEL:
        case F_RI_FKEY_CASCADE_UPD:
        case F_RI_FKEY_RESTRICT_DEL:
        case F_RI_FKEY_RESTRICT_UPD:
        case F_RI_FKEY_SETNULL_DEL:
        case F_RI_FKEY_SETNULL_UPD:
        case F_RI_FKEY_SETDEFAULT_DEL:
        case F_RI_FKEY_SETDEFAULT_UPD:
        case F_RI_FKEY_NOACTION_DEL:
        case F_RI_FKEY_NOACTION_UPD:
            return RI_TRIGGER_PK;

        case F_RI_FKEY_CHECK_INS:
        case F_RI_FKEY_CHECK_UPD:
            return RI_TRIGGER_FK;
    }

    return RI_TRIGGER_NONE;
}

bool RI_Initial_Check ( Trigger trigger,
Relation  fk_rel,
Relation  pk_rel 
)

Definition at line 2251 of file ri_triggers.c.

References appendStringInfo(), AtEOXact_GUC(), bms_add_member(), RI_ConstraintInfo::confmatchtype, RI_ConstraintInfo::conname, StringInfoData::data, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, errtableconstraint(), ExecCheckRTPerms(), RI_ConstraintInfo::fk_attnums, FKCONSTR_MATCH_FULL, FKCONSTR_MATCH_PARTIAL, FKCONSTR_MATCH_SIMPLE, GetLatestSnapshot(), GUC_ACTION_SAVE, i, initStringInfo(), InvalidSnapshot, list_make2, maintenance_work_mem, makeNode, MAX_QUOTED_NAME_LEN, NameStr, NewGUCNestLevel(), RI_ConstraintInfo::nkeys, NULL, RI_ConstraintInfo::pf_eq_oprs, PGC_S_SESSION, PGC_USERSET, RI_ConstraintInfo::pk_attnums, quoteOneName(), quoteRelationName(), RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, RangeTblEntry::relid, RangeTblEntry::relkind, RangeTblEntry::requiredPerms, ri_FetchConstraintInfo(), ri_GenerateQual(), ri_GenerateQualCollation(), RI_KEYS_NONE_NULL, ri_NullCheck(), RI_PLAN_CHECK_LOOKUPPK, ri_ReportViolation(), RIAttCollation, RIAttName, RIAttType, RangeTblEntry::rtekind, RangeTblEntry::selectedCols, set_config_option(), snprintf(), SPI_connect(), SPI_execute_snapshot(), SPI_finish(), SPI_OK_FINISH, SPI_OK_SELECT, SPI_prepare(), SPI_processed, SPI_result, SPI_tuptable, SPITupleTable::tupdesc, and SPITupleTable::vals.

Referenced by validateForeignKeyConstraint().

{
    const RI_ConstraintInfo *riinfo;
    StringInfoData querybuf;
    char        pkrelname[MAX_QUOTED_REL_NAME_LEN];
    char        fkrelname[MAX_QUOTED_REL_NAME_LEN];
    char        pkattname[MAX_QUOTED_NAME_LEN + 3];
    char        fkattname[MAX_QUOTED_NAME_LEN + 3];
    RangeTblEntry *pkrte;
    RangeTblEntry *fkrte;
    const char *sep;
    int         i;
    int         save_nestlevel;
    char        workmembuf[32];
    int         spi_result;
    SPIPlanPtr  qplan;

    /* Fetch constraint info. */
    riinfo = ri_FetchConstraintInfo(trigger, fk_rel, false);

    /*
     * Check to make sure current user has enough permissions to do the test
     * query.  (If not, caller can fall back to the trigger method, which
     * works because it changes user IDs on the fly.)
     *
     * XXX are there any other show-stopper conditions to check?
     */
    pkrte = makeNode(RangeTblEntry);
    pkrte->rtekind = RTE_RELATION;
    pkrte->relid = RelationGetRelid(pk_rel);
    pkrte->relkind = pk_rel->rd_rel->relkind;
    pkrte->requiredPerms = ACL_SELECT;

    fkrte = makeNode(RangeTblEntry);
    fkrte->rtekind = RTE_RELATION;
    fkrte->relid = RelationGetRelid(fk_rel);
    fkrte->relkind = fk_rel->rd_rel->relkind;
    fkrte->requiredPerms = ACL_SELECT;

    for (i = 0; i < riinfo->nkeys; i++)
    {
        int         attno;

        attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber;
        pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno);

        attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber;
        fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno);
    }

    if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false))
        return false;

    /*----------
     * The query string built is:
     *  SELECT fk.keycols FROM ONLY relname fk
     *   LEFT OUTER JOIN ONLY pkrelname pk
     *   ON (pk.pkkeycol1=fk.keycol1 [AND ...])
     *   WHERE pk.pkkeycol1 IS NULL AND
     * For MATCH SIMPLE:
     *   (fk.keycol1 IS NOT NULL [AND ...])
     * For MATCH FULL:
     *   (fk.keycol1 IS NOT NULL [OR ...])
     *
     * We attach COLLATE clauses to the operators when comparing columns
     * that have different collations.
     *----------
     */
    initStringInfo(&querybuf);
    appendStringInfo(&querybuf, "SELECT ");
    sep = "";
    for (i = 0; i < riinfo->nkeys; i++)
    {
        quoteOneName(fkattname,
                     RIAttName(fk_rel, riinfo->fk_attnums[i]));
        appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
        sep = ", ";
    }

    quoteRelationName(pkrelname, pk_rel);
    quoteRelationName(fkrelname, fk_rel);
    appendStringInfo(&querybuf,
                     " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
                     fkrelname, pkrelname);

    strcpy(pkattname, "pk.");
    strcpy(fkattname, "fk.");
    sep = "(";
    for (i = 0; i < riinfo->nkeys; i++)
    {
        Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
        Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
        Oid         pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
        Oid         fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);

        quoteOneName(pkattname + 3,
                     RIAttName(pk_rel, riinfo->pk_attnums[i]));
        quoteOneName(fkattname + 3,
                     RIAttName(fk_rel, riinfo->fk_attnums[i]));
        ri_GenerateQual(&querybuf, sep,
                        pkattname, pk_type,
                        riinfo->pf_eq_oprs[i],
                        fkattname, fk_type);
        if (pk_coll != fk_coll)
            ri_GenerateQualCollation(&querybuf, pk_coll);
        sep = "AND";
    }

    /*
     * It's sufficient to test any one pk attribute for null to detect a join
     * failure.
     */
    quoteOneName(pkattname, RIAttName(pk_rel, riinfo->pk_attnums[0]));
    appendStringInfo(&querybuf, ") WHERE pk.%s IS NULL AND (", pkattname);

    sep = "";
    for (i = 0; i < riinfo->nkeys; i++)
    {
        quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
        appendStringInfo(&querybuf,
                         "%sfk.%s IS NOT NULL",
                         sep, fkattname);
        switch (riinfo->confmatchtype)
        {
            case FKCONSTR_MATCH_SIMPLE:
                sep = " AND ";
                break;
            case FKCONSTR_MATCH_FULL:
                sep = " OR ";
                break;
            case FKCONSTR_MATCH_PARTIAL:
                ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                         errmsg("MATCH PARTIAL not yet implemented")));
                break;
            default:
                elog(ERROR, "unrecognized confmatchtype: %d",
                     riinfo->confmatchtype);
                break;
        }
    }
    appendStringInfo(&querybuf, ")");

    /*
     * Temporarily increase work_mem so that the check query can be executed
     * more efficiently.  It seems okay to do this because the query is simple
     * enough to not use a multiple of work_mem, and one typically would not
     * have many large foreign-key validations happening concurrently.  So
     * this seems to meet the criteria for being considered a "maintenance"
     * operation, and accordingly we use maintenance_work_mem.
     *
     * We use the equivalent of a function SET option to allow the setting to
     * persist for exactly the duration of the check query.  guc.c also takes
     * care of undoing the setting on error.
     */
    save_nestlevel = NewGUCNestLevel();

    snprintf(workmembuf, sizeof(workmembuf), "%d", maintenance_work_mem);
    (void) set_config_option("work_mem", workmembuf,
                             PGC_USERSET, PGC_S_SESSION,
                             GUC_ACTION_SAVE, true, 0);

    if (SPI_connect() != SPI_OK_CONNECT)
        elog(ERROR, "SPI_connect failed");

    /*
     * Generate the plan.  We don't need to cache it, and there are no
     * arguments to the plan.
     */
    qplan = SPI_prepare(querybuf.data, 0, NULL);

    if (qplan == NULL)
        elog(ERROR, "SPI_prepare returned %d for %s",
             SPI_result, querybuf.data);

    /*
     * Run the plan.  For safety we force a current snapshot to be used. (In
     * transaction-snapshot mode, this arguably violates transaction isolation
     * rules, but we really haven't got much choice.) We don't need to
     * register the snapshot, because SPI_execute_snapshot will see to it. We
     * need at most one tuple returned, so pass limit = 1.
     */
    spi_result = SPI_execute_snapshot(qplan,
                                      NULL, NULL,
                                      GetLatestSnapshot(),
                                      InvalidSnapshot,
                                      true, false, 1);

    /* Check result */
    if (spi_result != SPI_OK_SELECT)
        elog(ERROR, "SPI_execute_snapshot returned %d", spi_result);

    /* Did we find a tuple violating the constraint? */
    if (SPI_processed > 0)
    {
        HeapTuple   tuple = SPI_tuptable->vals[0];
        TupleDesc   tupdesc = SPI_tuptable->tupdesc;
        RI_ConstraintInfo fake_riinfo;

        /*
         * The columns to look at in the result tuple are 1..N, not whatever
         * they are in the fk_rel.  Hack up riinfo so that the subroutines
         * called here will behave properly.
         *
         * In addition to this, we have to pass the correct tupdesc to
         * ri_ReportViolation, overriding its normal habit of using the pk_rel
         * or fk_rel's tupdesc.
         */
        memcpy(&fake_riinfo, riinfo, sizeof(RI_ConstraintInfo));
        for (i = 0; i < fake_riinfo.nkeys; i++)
            fake_riinfo.fk_attnums[i] = i + 1;

        /*
         * If it's MATCH FULL, and there are any nulls in the FK keys,
         * complain about that rather than the lack of a match.  MATCH FULL
         * disallows partially-null FK rows.
         */
        if (fake_riinfo.confmatchtype == FKCONSTR_MATCH_FULL &&
            ri_NullCheck(tuple, &fake_riinfo, false) != RI_KEYS_NONE_NULL)
            ereport(ERROR,
                    (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
                     errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
                            RelationGetRelationName(fk_rel),
                            NameStr(fake_riinfo.conname)),
                     errdetail("MATCH FULL does not allow mixing of null and nonnull key values."),
                     errtableconstraint(fk_rel,
                                        NameStr(fake_riinfo.conname))));

        /*
         * We tell ri_ReportViolation we were doing the RI_PLAN_CHECK_LOOKUPPK
         * query, which isn't true, but will cause it to use
         * fake_riinfo.fk_attnums as we need.
         */
        ri_ReportViolation(&fake_riinfo,
                           pk_rel, fk_rel,
                           tuple, tupdesc,
                           RI_PLAN_CHECK_LOOKUPPK, false);
    }

    if (SPI_finish() != SPI_OK_FINISH)
        elog(ERROR, "SPI_finish failed");

    /*
     * Restore work_mem.
     */
    AtEOXact_GUC(true, save_nestlevel);

    return true;
}


Variable Documentation

PGDLLIMPORT int SessionReplicationRole