Header And Logo

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

Data Structures | Defines | Typedefs | Functions

event_trigger.h File Reference

#include "catalog/dependency.h"
#include "catalog/objectaddress.h"
#include "catalog/pg_event_trigger.h"
#include "nodes/parsenodes.h"
Include dependency graph for event_trigger.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  EventTriggerData

Defines

#define CALLED_AS_EVENT_TRIGGER(fcinfo)   ((fcinfo)->context != NULL && IsA((fcinfo)->context, EventTriggerData))

Typedefs

typedef struct EventTriggerData EventTriggerData

Functions

Oid CreateEventTrigger (CreateEventTrigStmt *stmt)
void RemoveEventTriggerById (Oid ctrigOid)
Oid get_event_trigger_oid (const char *trigname, bool missing_ok)
Oid AlterEventTrigger (AlterEventTrigStmt *stmt)
Oid AlterEventTriggerOwner (const char *name, Oid newOwnerId)
void AlterEventTriggerOwner_oid (Oid, Oid newOwnerId)
bool EventTriggerSupportsObjectType (ObjectType obtype)
bool EventTriggerSupportsObjectClass (ObjectClass objclass)
void EventTriggerDDLCommandStart (Node *parsetree)
void EventTriggerDDLCommandEnd (Node *parsetree)
void EventTriggerSQLDrop (Node *parsetree)
bool EventTriggerBeginCompleteQuery (void)
void EventTriggerEndCompleteQuery (void)
bool trackDroppedObjectsNeeded (void)
void EventTriggerSQLDropAddObject (ObjectAddress *object)

Define Documentation

#define CALLED_AS_EVENT_TRIGGER (   fcinfo  )     ((fcinfo)->context != NULL && IsA((fcinfo)->context, EventTriggerData))

Definition at line 33 of file event_trigger.h.

Referenced by do_compile(), and plpgsql_call_handler().


Typedef Documentation


Function Documentation

Oid AlterEventTrigger ( AlterEventTrigStmt stmt  ) 

Definition at line 414 of file event_trigger.c.

References ACL_KIND_EVENT_TRIGGER, aclcheck_error(), ACLCHECK_NOT_OWNER, CatalogUpdateIndexes(), CStringGetDatum, ereport, errcode(), errmsg(), ERROR, EVENTTRIGGERNAME, EventTriggerRelationId, GETSTRUCT, GetUserId(), heap_close, heap_freetuple(), heap_open(), HeapTupleGetOid, HeapTupleIsValid, InvokeObjectPostAlterHook, pg_event_trigger_ownercheck(), RowExclusiveLock, SearchSysCacheCopy1, simple_heap_update(), HeapTupleData::t_self, AlterEventTrigStmt::tgenabled, and AlterEventTrigStmt::trigname.

Referenced by standard_ProcessUtility().

{
    Relation    tgrel;
    HeapTuple   tup;
    Oid         trigoid;
    Form_pg_event_trigger evtForm;
    char        tgenabled = stmt->tgenabled;

    tgrel = heap_open(EventTriggerRelationId, RowExclusiveLock);

    tup = SearchSysCacheCopy1(EVENTTRIGGERNAME,
                              CStringGetDatum(stmt->trigname));
    if (!HeapTupleIsValid(tup))
        ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_OBJECT),
                 errmsg("event trigger \"%s\" does not exist",
                    stmt->trigname)));

    trigoid = HeapTupleGetOid(tup);

    if (!pg_event_trigger_ownercheck(trigoid, GetUserId()))
        aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER,
                       stmt->trigname);

    /* tuple is a copy, so we can modify it below */
    evtForm = (Form_pg_event_trigger) GETSTRUCT(tup);
    evtForm->evtenabled = tgenabled;

    simple_heap_update(tgrel, &tup->t_self, tup);
    CatalogUpdateIndexes(tgrel, tup);

    InvokeObjectPostAlterHook(EventTriggerRelationId,
                              trigoid, 0);

    /* clean up */
    heap_freetuple(tup);
    heap_close(tgrel, RowExclusiveLock);

    return trigoid;
}

Oid AlterEventTriggerOwner ( const char *  name,
Oid  newOwnerId 
)
void AlterEventTriggerOwner_oid ( Oid  ,
Oid  newOwnerId 
)
Oid CreateEventTrigger ( CreateEventTrigStmt stmt  ) 

Definition at line 132 of file event_trigger.c.

References DefElem::arg, CStringGetDatum, DefElem::defname, ereport, errcode(), errhint(), errmsg(), ERROR, error_duplicate_filter_variable(), CreateEventTrigStmt::eventname, EVENTTRIGGERNAME, EVTTRIGGEROID, CreateEventTrigStmt::funcname, get_func_rettype(), GetUserId(), HeapTupleIsValid, insert_event_trigger_tuple(), lfirst, LookupFuncName(), NameListToString(), NULL, SearchSysCache1, superuser(), CreateEventTrigStmt::trigname, validate_ddl_tags(), and CreateEventTrigStmt::whenclause.

Referenced by standard_ProcessUtility().

{
    HeapTuple   tuple;
    Oid         funcoid;
    Oid         funcrettype;
    Oid         evtowner = GetUserId();
    ListCell   *lc;
    List       *tags = NULL;

    /*
     * It would be nice to allow database owners or even regular users to do
     * this, but there are obvious privilege escalation risks which would have
     * to somehow be plugged first.
     */
    if (!superuser())
        ereport(ERROR,
            (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
             errmsg("permission denied to create event trigger \"%s\"",
                    stmt->trigname),
             errhint("Must be superuser to create an event trigger.")));

    /* Validate event name. */
    if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&
        strcmp(stmt->eventname, "ddl_command_end") != 0 &&
        strcmp(stmt->eventname, "sql_drop") != 0)
        ereport(ERROR,
            (errcode(ERRCODE_SYNTAX_ERROR),
             errmsg("unrecognized event name \"%s\"",
                    stmt->eventname)));

    /* Validate filter conditions. */
    foreach (lc, stmt->whenclause)
    {
        DefElem    *def = (DefElem *) lfirst(lc);

        if (strcmp(def->defname, "tag") == 0)
        {
            if (tags != NULL)
                error_duplicate_filter_variable(def->defname);
            tags = (List *) def->arg;
        }
        else
            ereport(ERROR,
                (errcode(ERRCODE_SYNTAX_ERROR),
                 errmsg("unrecognized filter variable \"%s\"", def->defname)));
    }

    /* Validate tag list, if any. */
    if ((strcmp(stmt->eventname, "ddl_command_start") == 0 ||
         strcmp(stmt->eventname, "ddl_command_end") == 0 ||
         strcmp(stmt->eventname, "sql_drop") == 0)
        && tags != NULL)
        validate_ddl_tags("tag", tags);

    /*
     * Give user a nice error message if an event trigger of the same name
     * already exists.
     */
    tuple = SearchSysCache1(EVENTTRIGGERNAME, CStringGetDatum(stmt->trigname));
    if (HeapTupleIsValid(tuple))
        ereport(ERROR,
                (errcode(ERRCODE_DUPLICATE_OBJECT),
                 errmsg("event trigger \"%s\" already exists",
                    stmt->trigname)));

    /* Find and validate the trigger function. */
    funcoid = LookupFuncName(stmt->funcname, 0, NULL, false);
    funcrettype = get_func_rettype(funcoid);
    if (funcrettype != EVTTRIGGEROID)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                 errmsg("function \"%s\" must return type \"event_trigger\"",
                        NameListToString(stmt->funcname))));

    /* Insert catalog entries. */
    return insert_event_trigger_tuple(stmt->trigname, stmt->eventname,
                                      evtowner, funcoid, tags);
}

bool EventTriggerBeginCompleteQuery ( void   ) 
void EventTriggerDDLCommandEnd ( Node parsetree  ) 

Definition at line 729 of file event_trigger.c.

References CommandCounterIncrement(), EventTriggerCommonSetup(), EventTriggerInvoke(), EVT_DDLCommandEnd, IsUnderPostmaster, list_free(), and NIL.

Referenced by ProcessUtilitySlow().

{
    List       *runlist;
    EventTriggerData    trigdata;

    /*
     * See EventTriggerDDLCommandStart for a discussion about why event
     * triggers are disabled in single user mode.
     */
    if (!IsUnderPostmaster)
        return;

    runlist = EventTriggerCommonSetup(parsetree,
                                      EVT_DDLCommandEnd, "ddl_command_end",
                                      &trigdata);
    if (runlist == NIL)
        return;

    /*
     * Make sure anything the main command did will be visible to the
     * event triggers.
     */
    CommandCounterIncrement();

    /* Run the triggers. */
    EventTriggerInvoke(runlist, &trigdata);

    /* Cleanup. */
    list_free(runlist);
}

void EventTriggerDDLCommandStart ( Node parsetree  ) 

Definition at line 682 of file event_trigger.c.

References CommandCounterIncrement(), EventTriggerCommonSetup(), EventTriggerInvoke(), EVT_DDLCommandStart, IsUnderPostmaster, list_free(), and NIL.

Referenced by ProcessUtilitySlow().

{
    List       *runlist;
    EventTriggerData    trigdata;

    /*
     * Event Triggers are completely disabled in standalone mode.  There are
     * (at least) two reasons for this:
     *
     * 1. A sufficiently broken event trigger might not only render the
     * database unusable, but prevent disabling itself to fix the situation.
     * In this scenario, restarting in standalone mode provides an escape
     * hatch.
     *
     * 2. BuildEventTriggerCache relies on systable_beginscan_ordered, and
     * therefore will malfunction if pg_event_trigger's indexes are damaged.
     * To allow recovery from a damaged index, we need some operating mode
     * wherein event triggers are disabled.  (Or we could implement
     * heapscan-and-sort logic for that case, but having disaster recovery
     * scenarios depend on code that's otherwise untested isn't appetizing.)
     */
    if (!IsUnderPostmaster)
        return;

    runlist = EventTriggerCommonSetup(parsetree,
                                      EVT_DDLCommandStart, "ddl_command_start",
                                      &trigdata);
    if (runlist == NIL)
        return;

    /* Run the triggers. */
    EventTriggerInvoke(runlist, &trigdata);

    /* Cleanup. */
    list_free(runlist);

    /*
     * Make sure anything the event triggers did will be visible to
     * the main command.
     */
    CommandCounterIncrement();
}

void EventTriggerEndCompleteQuery ( void   ) 

Definition at line 1048 of file event_trigger.c.

References EventTriggerQueryState::cxt, MemoryContextDelete(), and EventTriggerQueryState::previous.

Referenced by ProcessUtilitySlow().

{
    EventTriggerQueryState *prevstate;

    prevstate = currentEventTriggerState->previous;

    /* this avoids the need for retail pfree of SQLDropList items: */
    MemoryContextDelete(currentEventTriggerState->cxt);

    currentEventTriggerState = prevstate;
}

void EventTriggerSQLDrop ( Node parsetree  ) 

Definition at line 764 of file event_trigger.c.

References CommandCounterIncrement(), EventTriggerCommonSetup(), EventTriggerInvoke(), EVT_SQLDrop, EventTriggerQueryState::in_sql_drop, IsUnderPostmaster, list_free(), NIL, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, slist_is_empty(), and EventTriggerQueryState::SQLDropList.

Referenced by ProcessUtilitySlow().

{
    List       *runlist;
    EventTriggerData    trigdata;

    /*
     * See EventTriggerDDLCommandStart for a discussion about why event
     * triggers are disabled in single user mode.
     */
    if (!IsUnderPostmaster)
        return;

    /*
     * Use current state to determine whether this event fires at all.  If there
     * are no triggers for the sql_drop event, then we don't have anything to do
     * here.  Note that dropped object collection is disabled if this is the case,
     * so even if we were to try to run, the list would be empty.
     */
    if (!currentEventTriggerState ||
        slist_is_empty(&currentEventTriggerState->SQLDropList))
        return;

    runlist = EventTriggerCommonSetup(parsetree,
                                      EVT_SQLDrop, "sql_drop",
                                      &trigdata);
    /*
     * Nothing to do if run list is empty.  Note this shouldn't happen, because
     * if there are no sql_drop events, then objects-to-drop wouldn't have been
     * collected in the first place and we would have quitted above.
     */
    if (runlist == NIL)
        return;

    /*
     * Make sure anything the main command did will be visible to the
     * event triggers.
     */
    CommandCounterIncrement();

    /*
     * Make sure pg_event_trigger_dropped_objects only works when running these
     * triggers.  Use PG_TRY to ensure in_sql_drop is reset even when one
     * trigger fails.  (This is perhaps not necessary, as the currentState
     * variable will be removed shortly by our caller, but it seems better to
     * play safe.)
     */
    currentEventTriggerState->in_sql_drop = true;

    /* Run the triggers. */
    PG_TRY();
    {
        EventTriggerInvoke(runlist, &trigdata);
    }
    PG_CATCH();
    {
        currentEventTriggerState->in_sql_drop = false;
        PG_RE_THROW();
    }
    PG_END_TRY();
    currentEventTriggerState->in_sql_drop = false;

    /* Cleanup. */
    list_free(runlist);
}

void EventTriggerSQLDropAddObject ( ObjectAddress object  ) 

Definition at line 1093 of file event_trigger.c.

References AccessShareLock, SQLDropObject::address, Assert, ObjectAddress::classId, EventTriggerQueryState::cxt, DatumGetName, DatumGetObjectId, EventTriggerSupportsObjectClass(), get_catalog_object_by_oid(), get_namespace_name(), get_object_attnum_name(), get_object_attnum_namespace(), get_object_namensp_unique(), getObjectClass(), getObjectIdentity(), getObjectTypeDescription(), heap_close, heap_getattr, heap_open(), InvalidAttrNumber, is_objectclass_supported(), isAnyTempNamespace(), MemoryContextSwitchTo(), NamespaceRelationId, NameStr, SQLDropObject::next, ObjectAddress::objectId, ObjectAddress::objectSubId, SQLDropObject::objecttype, SQLDropObject::objidentity, SQLDropObject::objname, palloc0(), pfree(), pstrdup(), RelationGetDescr, SQLDropObject::schemaname, slist_push_head(), and EventTriggerQueryState::SQLDropList.

Referenced by deleteObjectsInList().

{
    SQLDropObject  *obj;
    MemoryContext   oldcxt;

    if (!currentEventTriggerState)
        return;

    Assert(EventTriggerSupportsObjectClass(getObjectClass(object)));

    /* don't report temp schemas */
    if (object->classId == NamespaceRelationId &&
        isAnyTempNamespace(object->objectId))
        return;

    oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);

    obj = palloc0(sizeof(SQLDropObject));
    obj->address = *object;

    /*
     * Obtain schema names from the object's catalog tuple, if one exists;
     * this lets us skip objects in temp schemas.  We trust that ObjectProperty
     * contains all object classes that can be schema-qualified.
     */
    if (is_objectclass_supported(object->classId))
    {
        Relation    catalog;
        HeapTuple   tuple;

        catalog = heap_open(obj->address.classId, AccessShareLock);
        tuple = get_catalog_object_by_oid(catalog, obj->address.objectId);

        if (tuple)
        {
            AttrNumber  attnum;
            Datum       datum;
            bool        isnull;

            attnum = get_object_attnum_namespace(obj->address.classId);
            if (attnum != InvalidAttrNumber)
            {
                datum = heap_getattr(tuple, attnum,
                                     RelationGetDescr(catalog), &isnull);
                if (!isnull)
                {
                    Oid     namespaceId;

                    namespaceId = DatumGetObjectId(datum);
                    /* Don't report objects in temp namespaces */
                    if (isAnyTempNamespace(namespaceId))
                    {
                        pfree(obj);
                        heap_close(catalog, AccessShareLock);
                        MemoryContextSwitchTo(oldcxt);
                        return;
                    }

                    obj->schemaname = get_namespace_name(namespaceId);
                }
            }

            if (get_object_namensp_unique(obj->address.classId) &&
                obj->address.objectSubId == 0)
            {
                attnum = get_object_attnum_name(obj->address.classId);
                if (attnum != InvalidAttrNumber)
                {
                    datum = heap_getattr(tuple, attnum,
                                         RelationGetDescr(catalog), &isnull);
                    if (!isnull)
                        obj->objname = pstrdup(NameStr(*DatumGetName(datum)));
                }
            }
        }

        heap_close(catalog, AccessShareLock);
    }

    /* object identity */
    obj->objidentity = getObjectIdentity(&obj->address);

    /* and object type, too */
    obj->objecttype = getObjectTypeDescription(&obj->address);

    slist_push_head(&(currentEventTriggerState->SQLDropList), &obj->next);

    MemoryContextSwitchTo(oldcxt);
}

bool EventTriggerSupportsObjectClass ( ObjectClass  objclass  ) 

Definition at line 948 of file event_trigger.c.

References Assert, MAX_OCLASS, OCLASS_AMOP, OCLASS_AMPROC, OCLASS_CAST, OCLASS_CLASS, OCLASS_COLLATION, OCLASS_CONSTRAINT, OCLASS_CONVERSION, OCLASS_DATABASE, OCLASS_DEFACL, OCLASS_DEFAULT, OCLASS_EVENT_TRIGGER, OCLASS_EXTENSION, OCLASS_FDW, OCLASS_FOREIGN_SERVER, OCLASS_LANGUAGE, OCLASS_LARGEOBJECT, OCLASS_OPCLASS, OCLASS_OPERATOR, OCLASS_OPFAMILY, OCLASS_PROC, OCLASS_REWRITE, OCLASS_ROLE, OCLASS_SCHEMA, OCLASS_TBLSPACE, OCLASS_TRIGGER, OCLASS_TSCONFIG, OCLASS_TSDICT, OCLASS_TSPARSER, OCLASS_TSTEMPLATE, OCLASS_TYPE, and OCLASS_USER_MAPPING.

Referenced by deleteObjectsInList(), and EventTriggerSQLDropAddObject().

{
    switch (objclass)
    {
        case OCLASS_DATABASE:
        case OCLASS_TBLSPACE:
        case OCLASS_ROLE:
            /* no support for global objects */
            return false;
        case OCLASS_EVENT_TRIGGER:
            /* no support for event triggers on event triggers */
            return false;
        case OCLASS_CLASS:
        case OCLASS_PROC:
        case OCLASS_TYPE:
        case OCLASS_CAST:
        case OCLASS_COLLATION:
        case OCLASS_CONSTRAINT:
        case OCLASS_CONVERSION:
        case OCLASS_DEFAULT:
        case OCLASS_LANGUAGE:
        case OCLASS_LARGEOBJECT:
        case OCLASS_OPERATOR:
        case OCLASS_OPCLASS:
        case OCLASS_OPFAMILY:
        case OCLASS_AMOP:
        case OCLASS_AMPROC:
        case OCLASS_REWRITE:
        case OCLASS_TRIGGER:
        case OCLASS_SCHEMA:
        case OCLASS_TSPARSER:
        case OCLASS_TSDICT:
        case OCLASS_TSTEMPLATE:
        case OCLASS_TSCONFIG:
        case OCLASS_FDW:
        case OCLASS_FOREIGN_SERVER:
        case OCLASS_USER_MAPPING:
        case OCLASS_DEFACL:
        case OCLASS_EXTENSION:
            return true;

        case MAX_OCLASS:
            /*
             * This shouldn't ever happen, but we keep the case to avoid a
             * compiler warning without a "default" clause in the switch.
             */
            Assert(false);
            break;
    }

    return true;
}

bool EventTriggerSupportsObjectType ( ObjectType  obtype  ) 

Definition at line 896 of file event_trigger.c.

References OBJECT_AGGREGATE, OBJECT_ATTRIBUTE, OBJECT_CAST, OBJECT_COLLATION, OBJECT_COLUMN, OBJECT_CONSTRAINT, OBJECT_CONVERSION, OBJECT_DATABASE, OBJECT_DOMAIN, OBJECT_EVENT_TRIGGER, OBJECT_EXTENSION, OBJECT_FDW, OBJECT_FOREIGN_SERVER, OBJECT_FOREIGN_TABLE, OBJECT_FUNCTION, OBJECT_INDEX, OBJECT_LANGUAGE, OBJECT_LARGEOBJECT, OBJECT_MATVIEW, OBJECT_OPCLASS, OBJECT_OPERATOR, OBJECT_OPFAMILY, OBJECT_ROLE, OBJECT_RULE, OBJECT_SCHEMA, OBJECT_SEQUENCE, OBJECT_TABLE, OBJECT_TABLESPACE, OBJECT_TRIGGER, OBJECT_TSCONFIGURATION, OBJECT_TSDICTIONARY, OBJECT_TSPARSER, OBJECT_TSTEMPLATE, OBJECT_TYPE, and OBJECT_VIEW.

Referenced by standard_ProcessUtility().

{
    switch (obtype)
    {
        case OBJECT_DATABASE:
        case OBJECT_TABLESPACE:
        case OBJECT_ROLE:
            /* no support for global objects */
            return false;
        case OBJECT_EVENT_TRIGGER:
            /* no support for event triggers on event triggers */
            return false;
        case OBJECT_AGGREGATE:
        case OBJECT_ATTRIBUTE:
        case OBJECT_CAST:
        case OBJECT_COLUMN:
        case OBJECT_CONSTRAINT:
        case OBJECT_COLLATION:
        case OBJECT_CONVERSION:
        case OBJECT_DOMAIN:
        case OBJECT_EXTENSION:
        case OBJECT_FDW:
        case OBJECT_FOREIGN_SERVER:
        case OBJECT_FOREIGN_TABLE:
        case OBJECT_FUNCTION:
        case OBJECT_INDEX:
        case OBJECT_LANGUAGE:
        case OBJECT_LARGEOBJECT:
        case OBJECT_MATVIEW:
        case OBJECT_OPCLASS:
        case OBJECT_OPERATOR:
        case OBJECT_OPFAMILY:
        case OBJECT_RULE:
        case OBJECT_SCHEMA:
        case OBJECT_SEQUENCE:
        case OBJECT_TABLE:
        case OBJECT_TRIGGER:
        case OBJECT_TSCONFIGURATION:
        case OBJECT_TSDICTIONARY:
        case OBJECT_TSPARSER:
        case OBJECT_TSTEMPLATE:
        case OBJECT_TYPE:
        case OBJECT_VIEW:
            return true;
    }
    return true;
}

Oid get_event_trigger_oid ( const char *  trigname,
bool  missing_ok 
)

Definition at line 555 of file event_trigger.c.

References CStringGetDatum, ereport, errcode(), errmsg(), ERROR, EVENTTRIGGERNAME, GetSysCacheOid1, and OidIsValid.

Referenced by get_object_address_unqualified().

{
    Oid         oid;

    oid = GetSysCacheOid1(EVENTTRIGGERNAME, CStringGetDatum(trigname));
    if (!OidIsValid(oid) && !missing_ok)
        ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_OBJECT),
                 errmsg("event trigger \"%s\" does not exist", trigname)));
    return oid;
}

void RemoveEventTriggerById ( Oid  ctrigOid  ) 
bool trackDroppedObjectsNeeded ( void   ) 

Definition at line 1066 of file event_trigger.c.

References EventCacheLookup(), EVT_SQLDrop, and list_length().

Referenced by deleteObjectsInList(), and EventTriggerBeginCompleteQuery().

{
    /* true if any sql_drop event trigger exists */
    return list_length(EventCacheLookup(EVT_SQLDrop)) > 0;
}