Header And Logo

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

Data Structures | Typedefs | Enumerations | Functions | Variables

event_trigger.c File Reference

#include "postgres.h"
#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "commands/event_trigger.h"
#include "commands/trigger.h"
#include "funcapi.h"
#include "parser/parse_func.h"
#include "pgstat.h"
#include "lib/ilist.h"
#include "miscadmin.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/evtcache.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/tqual.h"
#include "utils/syscache.h"
#include "tcop/utility.h"
Include dependency graph for event_trigger.c:

Go to the source code of this file.

Data Structures

struct  EventTriggerQueryState
struct  event_trigger_support_data
struct  SQLDropObject

Typedefs

typedef struct
EventTriggerQueryState 
EventTriggerQueryState
typedef struct SQLDropObject SQLDropObject

Enumerations

enum  event_trigger_command_tag_check_result { EVENT_TRIGGER_COMMAND_TAG_OK, EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED, EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED }

Functions

static void AlterEventTriggerOwner_internal (Relation rel, HeapTuple tup, Oid newOwnerId)
static
event_trigger_command_tag_check_result 
check_ddl_tag (const char *tag)
static void error_duplicate_filter_variable (const char *defname)
static Datum filter_list_to_array (List *filterlist)
static Oid insert_event_trigger_tuple (char *trigname, char *eventname, Oid evtOwner, Oid funcoid, List *tags)
static void validate_ddl_tags (const char *filtervar, List *taglist)
static void EventTriggerInvoke (List *fn_oid_list, EventTriggerData *trigdata)
Oid CreateEventTrigger (CreateEventTrigStmt *stmt)
void RemoveEventTriggerById (Oid trigOid)
Oid AlterEventTrigger (AlterEventTrigStmt *stmt)
Oid AlterEventTriggerOwner (const char *name, Oid newOwnerId)
void AlterEventTriggerOwner_oid (Oid trigOid, Oid newOwnerId)
Oid get_event_trigger_oid (const char *trigname, bool missing_ok)
static bool filter_event_trigger (const char **tag, EventTriggerCacheItem *item)
static ListEventTriggerCommonSetup (Node *parsetree, EventTriggerEvent event, const char *eventstr, EventTriggerData *trigdata)
void EventTriggerDDLCommandStart (Node *parsetree)
void EventTriggerDDLCommandEnd (Node *parsetree)
void EventTriggerSQLDrop (Node *parsetree)
bool EventTriggerSupportsObjectType (ObjectType obtype)
bool EventTriggerSupportsObjectClass (ObjectClass objclass)
bool EventTriggerBeginCompleteQuery (void)
void EventTriggerEndCompleteQuery (void)
bool trackDroppedObjectsNeeded (void)
void EventTriggerSQLDropAddObject (ObjectAddress *object)
Datum pg_event_trigger_dropped_objects (PG_FUNCTION_ARGS)

Variables

EventTriggerQueryStatecurrentEventTriggerState = NULL
static event_trigger_support_data event_trigger_support []

Typedef Documentation

typedef struct SQLDropObject SQLDropObject

Enumeration Type Documentation

Enumerator:
EVENT_TRIGGER_COMMAND_TAG_OK 
EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED 
EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED 

Definition at line 62 of file event_trigger.c.

{
    EVENT_TRIGGER_COMMAND_TAG_OK,
    EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED,
    EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED
} event_trigger_command_tag_check_result;


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 
)
static void AlterEventTriggerOwner_internal ( Relation  rel,
HeapTuple  tup,
Oid  newOwnerId 
) [static]

Definition at line 514 of file event_trigger.c.

References ACL_KIND_EVENT_TRIGGER, aclcheck_error(), ACLCHECK_NOT_OWNER, CatalogUpdateIndexes(), changeDependencyOnOwner(), ereport, errcode(), errhint(), errmsg(), ERROR, EventTriggerRelationId, GETSTRUCT, GetUserId(), HeapTupleGetOid, InvokeObjectPostAlterHook, NameStr, pg_event_trigger_ownercheck(), simple_heap_update(), superuser_arg(), and HeapTupleData::t_self.

Referenced by AlterEventTriggerOwner(), and AlterEventTriggerOwner_oid().

{
    Form_pg_event_trigger form;

    form = (Form_pg_event_trigger) GETSTRUCT(tup);

    if (form->evtowner == newOwnerId)
        return;

    if (!pg_event_trigger_ownercheck(HeapTupleGetOid(tup), GetUserId()))
        aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER,
                       NameStr(form->evtname));

    /* New owner must be a superuser */
    if (!superuser_arg(newOwnerId))
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                 errmsg("permission denied to change owner of event trigger \"%s\"",
                    NameStr(form->evtname)),
                 errhint("The owner of an event trigger must be a superuser.")));

    form->evtowner = newOwnerId;
    simple_heap_update(rel, &tup->t_self, tup);
    CatalogUpdateIndexes(rel, tup);

    /* Update owner dependency reference */
    changeDependencyOnOwner(EventTriggerRelationId,
                            HeapTupleGetOid(tup),
                            newOwnerId);

    InvokeObjectPostAlterHook(EventTriggerRelationId,
                              HeapTupleGetOid(tup), 0);
}

void AlterEventTriggerOwner_oid ( Oid  trigOid,
Oid  newOwnerId 
)
static event_trigger_command_tag_check_result check_ddl_tag ( const char *  tag  )  [static]

Definition at line 240 of file event_trigger.c.

References NULL, event_trigger_support_data::obtypename, pg_strcasecmp(), pg_strncasecmp(), and event_trigger_support_data::supported.

Referenced by EventTriggerCommonSetup(), and validate_ddl_tags().

{
    const char *obtypename;
    event_trigger_support_data     *etsd;

    /*
     * Handle some idiosyncratic special cases.
     */
    if (pg_strcasecmp(tag, "CREATE TABLE AS") == 0 ||
        pg_strcasecmp(tag, "SELECT INTO") == 0 ||
        pg_strcasecmp(tag, "REFRESH MATERIALIZED VIEW") == 0 ||
        pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 ||
        pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 ||
        pg_strcasecmp(tag, "DROP OWNED") == 0)
        return EVENT_TRIGGER_COMMAND_TAG_OK;

    /*
     * Otherwise, command should be CREATE, ALTER, or DROP.
     */
    if (pg_strncasecmp(tag, "CREATE ", 7) == 0)
        obtypename = tag + 7;
    else if (pg_strncasecmp(tag, "ALTER ", 6) == 0)
        obtypename = tag + 6;
    else if (pg_strncasecmp(tag, "DROP ", 5) == 0)
        obtypename = tag + 5;
    else
        return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED;

    /*
     * ...and the object type should be something recognizable.
     */
    for (etsd = event_trigger_support; etsd->obtypename != NULL; etsd++)
        if (pg_strcasecmp(etsd->obtypename, obtypename) == 0)
            break;
    if (etsd->obtypename == NULL)
        return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED;
    if (!etsd->supported)
        return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED;
    return EVENT_TRIGGER_COMMAND_TAG_OK;
}

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);
}

static void error_duplicate_filter_variable ( const char *  defname  )  [static]

Definition at line 285 of file event_trigger.c.

References ereport, errcode(), errmsg(), and ERROR.

Referenced by CreateEventTrigger().

{
    ereport(ERROR,
            (errcode(ERRCODE_SYNTAX_ERROR),
             errmsg("filter variable \"%s\" specified more than once",
                defname)));
}

bool EventTriggerBeginCompleteQuery ( void   ) 
static List* EventTriggerCommonSetup ( Node parsetree,
EventTriggerEvent  event,
const char *  eventstr,
EventTriggerData trigdata 
) [static]

Definition at line 606 of file event_trigger.c.

References assert_enabled, check_ddl_tag(), CreateCommandTag(), elog, ERROR, EventTriggerData::event, EVENT_TRIGGER_COMMAND_TAG_OK, EventCacheLookup(), filter_event_trigger(), EventTriggerCacheItem::fnoid, lappend_oid(), lfirst, NIL, EventTriggerData::parsetree, EventTriggerData::tag, and EventTriggerData::type.

Referenced by EventTriggerDDLCommandEnd(), EventTriggerDDLCommandStart(), and EventTriggerSQLDrop().

{
    const char *tag;
    List       *cachelist;
    ListCell   *lc;
    List       *runlist = NIL;

    /*
     * We want the list of command tags for which this procedure is actually
     * invoked to match up exactly with the list that CREATE EVENT TRIGGER
     * accepts.  This debugging cross-check will throw an error if this
     * function is invoked for a command tag that CREATE EVENT TRIGGER won't
     * accept.  (Unfortunately, there doesn't seem to be any simple, automated
     * way to verify that CREATE EVENT TRIGGER doesn't accept extra stuff that
     * never reaches this control point.)
     *
     * If this cross-check fails for you, you probably need to either adjust
     * standard_ProcessUtility() not to invoke event triggers for the command
     * type in question, or you need to adjust check_ddl_tag to accept the
     * relevant command tag.
     */
#ifdef USE_ASSERT_CHECKING
    if (assert_enabled)
    {
        const char *dbgtag;

        dbgtag = CreateCommandTag(parsetree);
        if (check_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK)
            elog(ERROR, "unexpected command tag \"%s\"", dbgtag);
    }
#endif

    /* Use cache to find triggers for this event; fast exit if none. */
    cachelist = EventCacheLookup(event);
    if (cachelist == NIL)
        return NIL;

    /* Get the command tag. */
    tag = CreateCommandTag(parsetree);

    /*
     * Filter list of event triggers by command tag, and copy them into
     * our memory context.  Once we start running the command trigers, or
     * indeed once we do anything at all that touches the catalogs, an
     * invalidation might leave cachelist pointing at garbage, so we must
     * do this before we can do much else.
     */
    foreach (lc, cachelist)
    {
        EventTriggerCacheItem  *item = lfirst(lc);

        if (filter_event_trigger(&tag, item))
        {
            /* We must plan to fire this trigger. */
            runlist = lappend_oid(runlist, item->fnoid);
        }
    }

    /* don't spend any more time on this if no functions to run */
    if (runlist == NIL)
        return NIL;

    trigdata->type = T_EventTriggerData;
    trigdata->event = eventstr;
    trigdata->parsetree = parsetree;
    trigdata->tag = tag;

    return runlist;
}

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;
}

static void EventTriggerInvoke ( List fn_oid_list,
EventTriggerData trigdata 
) [static]

Definition at line 833 of file event_trigger.c.

References ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), check_stack_depth(), CommandCounterIncrement(), CurrentMemoryContext, fmgr_info(), FunctionCallInvoke, InitFunctionCallInfoData, InvalidOid, lfirst_oid, MemoryContextDelete(), MemoryContextReset(), MemoryContextSwitchTo(), NULL, pgstat_end_function_usage(), and pgstat_init_function_usage().

Referenced by EventTriggerDDLCommandEnd(), EventTriggerDDLCommandStart(), and EventTriggerSQLDrop().

{
    MemoryContext   context;
    MemoryContext   oldcontext;
    ListCell       *lc;
    bool            first = true;

    /* Guard against stack overflow due to recursive event trigger */
    check_stack_depth();

    /*
     * Let's evaluate event triggers in their own memory context, so
     * that any leaks get cleaned up promptly.
     */
    context = AllocSetContextCreate(CurrentMemoryContext,
                                    "event trigger context",
                                    ALLOCSET_DEFAULT_MINSIZE,
                                    ALLOCSET_DEFAULT_INITSIZE,
                                    ALLOCSET_DEFAULT_MAXSIZE);
    oldcontext = MemoryContextSwitchTo(context);

    /* Call each event trigger. */
    foreach (lc, fn_oid_list)
    {
        Oid     fnoid = lfirst_oid(lc);
        FmgrInfo        flinfo;
        FunctionCallInfoData fcinfo;
        PgStat_FunctionCallUsage fcusage;

        /*
         * We want each event trigger to be able to see the results of
         * the previous event trigger's action.  Caller is responsible
         * for any command-counter increment that is needed between the
         * event trigger and anything else in the transaction.
         */
        if (first)
            first = false;
        else
            CommandCounterIncrement();

        /* Look up the function */
        fmgr_info(fnoid, &flinfo);

        /* Call the function, passing no arguments but setting a context. */
        InitFunctionCallInfoData(fcinfo, &flinfo, 0,
                                 InvalidOid, (Node *) trigdata, NULL);
        pgstat_init_function_usage(&fcinfo, &fcusage);
        FunctionCallInvoke(&fcinfo);
        pgstat_end_function_usage(&fcusage, true);

        /* Reclaim memory. */
        MemoryContextReset(context);
    }

    /* Restore old memory context and delete the temporary one. */
    MemoryContextSwitchTo(oldcontext);
    MemoryContextDelete(context);
}

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;
}

static bool filter_event_trigger ( const char **  tag,
EventTriggerCacheItem item 
) [static]

Definition at line 573 of file event_trigger.c.

References EventTriggerCacheItem::enabled, EventTriggerCacheItem::ntags, NULL, pg_qsort_strcmp(), SESSION_REPLICATION_ROLE_REPLICA, SessionReplicationRole, EventTriggerCacheItem::tag, TRIGGER_FIRES_ON_ORIGIN, and TRIGGER_FIRES_ON_REPLICA.

Referenced by EventTriggerCommonSetup().

{
    /*
     * Filter by session replication role, knowing that we never see disabled
     * items down here.
     */
    if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
    {
        if (item->enabled == TRIGGER_FIRES_ON_ORIGIN)
            return false;
    }
    else
    {
        if (item->enabled == TRIGGER_FIRES_ON_REPLICA)
            return false;
    }

    /* Filter by tags, if any were specified. */
    if (item->ntags != 0 && bsearch(tag, item->tag,
                                    item->ntags, sizeof(char *),
                                    pg_qsort_strcmp) == NULL)
        return false;

    /* if we reach that point, we're not filtering out this item */
    return true;
}

static Datum filter_list_to_array ( List filterlist  )  [static]

Definition at line 363 of file event_trigger.c.

References construct_array(), cstring_to_text(), i, lfirst, list_length(), palloc(), pfree(), pg_ascii_toupper(), PointerGetDatum, pstrdup(), strVal, TEXTOID, and value.

Referenced by insert_event_trigger_tuple().

{
    ListCell   *lc;
    Datum      *data;
    int         i = 0,
                l = list_length(filterlist);

    data = (Datum *) palloc(l * sizeof(Datum));

    foreach(lc, filterlist)
    {
        const char *value = strVal(lfirst(lc));
        char       *result,
                   *p;

        result = pstrdup(value);
        for (p = result; *p; p++)
            *p = pg_ascii_toupper((unsigned char) *p);
        data[i++] = PointerGetDatum(cstring_to_text(result));
        pfree(result);
    }

    return PointerGetDatum(construct_array(data, l, TEXTOID, -1, false, 'i'));
}

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;
}

static Oid insert_event_trigger_tuple ( char *  trigname,
char *  eventname,
Oid  evtOwner,
Oid  funcoid,
List tags 
) [static]

Definition at line 297 of file event_trigger.c.

References Anum_pg_event_trigger_evtenabled, Anum_pg_event_trigger_evtevent, Anum_pg_event_trigger_evtfoid, Anum_pg_event_trigger_evtname, Anum_pg_event_trigger_evtowner, Anum_pg_event_trigger_evttags, CatalogUpdateIndexes(), CharGetDatum, ObjectAddress::classId, DEPENDENCY_NORMAL, EventTriggerRelationId, filter_list_to_array(), heap_close, heap_form_tuple(), heap_freetuple(), heap_open(), InvokeObjectPostCreateHook, NameGetDatum, NIL, ObjectAddress::objectId, ObjectIdGetDatum, ObjectAddress::objectSubId, RelationData::rd_att, recordDependencyOn(), recordDependencyOnOwner(), RowExclusiveLock, simple_heap_insert(), TRIGGER_FIRES_ON_ORIGIN, and values.

Referenced by CreateEventTrigger().

{
    Relation tgrel;
    Oid         trigoid;
    HeapTuple   tuple;
    Datum       values[Natts_pg_trigger];
    bool        nulls[Natts_pg_trigger];
    ObjectAddress myself, referenced;

    /* Open pg_event_trigger. */
    tgrel = heap_open(EventTriggerRelationId, RowExclusiveLock);

    /* Build the new pg_trigger tuple. */
    memset(nulls, false, sizeof(nulls));
    values[Anum_pg_event_trigger_evtname - 1] = NameGetDatum(trigname);
    values[Anum_pg_event_trigger_evtevent - 1] = NameGetDatum(eventname);
    values[Anum_pg_event_trigger_evtowner - 1] = ObjectIdGetDatum(evtOwner);
    values[Anum_pg_event_trigger_evtfoid - 1] = ObjectIdGetDatum(funcoid);
    values[Anum_pg_event_trigger_evtenabled - 1] =
        CharGetDatum(TRIGGER_FIRES_ON_ORIGIN);
    if (taglist == NIL)
        nulls[Anum_pg_event_trigger_evttags - 1] = true;
    else
        values[Anum_pg_event_trigger_evttags - 1] =
            filter_list_to_array(taglist);

    /* Insert heap tuple. */
    tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
    trigoid = simple_heap_insert(tgrel, tuple);
    CatalogUpdateIndexes(tgrel, tuple);
    heap_freetuple(tuple);

    /* Depend on owner. */
    recordDependencyOnOwner(EventTriggerRelationId, trigoid, evtOwner);

    /* Depend on event trigger function. */
    myself.classId = EventTriggerRelationId;
    myself.objectId = trigoid;
    myself.objectSubId = 0;
    referenced.classId = ProcedureRelationId;
    referenced.objectId = funcoid;
    referenced.objectSubId = 0;
    recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);

    /* Post creation hook for new operator family */
    InvokeObjectPostCreateHook(EventTriggerRelationId, trigoid, 0);

    /* Close pg_event_trigger. */
    heap_close(tgrel, RowExclusiveLock);

    return trigoid;
}

Datum pg_event_trigger_dropped_objects ( PG_FUNCTION_ARGS   ) 

Definition at line 1190 of file event_trigger.c.

References SQLDropObject::address, ReturnSetInfo::allowedModes, ObjectAddress::classId, CStringGetTextDatum, ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, elog, ereport, errcode(), errmsg(), ERROR, get_call_result_type(), i, EventTriggerQueryState::in_sql_drop, Int32GetDatum, IsA, MemoryContextSwitchTo(), MemSet, next(), NULL, ObjectAddress::objectId, ObjectIdGetDatum, ObjectAddress::objectSubId, SQLDropObject::objecttype, SQLDropObject::objidentity, SQLDropObject::objname, ReturnSetInfo::returnMode, SQLDropObject::schemaname, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, slist_container, slist_foreach, EventTriggerQueryState::SQLDropList, tuplestore_begin_heap(), tuplestore_donestoring, tuplestore_putvalues(), TYPEFUNC_COMPOSITE, values, and work_mem.

{
    ReturnSetInfo      *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
    TupleDesc           tupdesc;
    Tuplestorestate    *tupstore;
    MemoryContext       per_query_ctx;
    MemoryContext       oldcontext;
    slist_iter          iter;

    /*
     * Protect this function from being called out of context
     */
    if (!currentEventTriggerState ||
        !currentEventTriggerState->in_sql_drop)
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("%s can only be called in a sql_drop event trigger function",
                        "pg_event_trigger_dropped_objects()")));

    /* check to see if caller supports us returning a tuplestore */
    if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("set-valued function called in context that cannot accept a set")));
    if (!(rsinfo->allowedModes & SFRM_Materialize))
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("materialize mode required, but it is not allowed in this context")));

    /* Build a tuple descriptor for our result type */
    if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
        elog(ERROR, "return type must be a row type");

    /* Build tuplestore to hold the result rows */
    per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
    oldcontext = MemoryContextSwitchTo(per_query_ctx);

    tupstore = tuplestore_begin_heap(true, false, work_mem);
    rsinfo->returnMode = SFRM_Materialize;
    rsinfo->setResult = tupstore;
    rsinfo->setDesc = tupdesc;

    MemoryContextSwitchTo(oldcontext);

    slist_foreach(iter, &(currentEventTriggerState->SQLDropList))
    {
        SQLDropObject *obj;
        int         i = 0;
        Datum       values[7];
        bool        nulls[7];

        obj = slist_container(SQLDropObject, next, iter.cur);

        MemSet(values, 0, sizeof(values));
        MemSet(nulls, 0, sizeof(nulls));

        /* classid */
        values[i++] = ObjectIdGetDatum(obj->address.classId);

        /* objid */
        values[i++] = ObjectIdGetDatum(obj->address.objectId);

        /* objsubid */
        values[i++] = Int32GetDatum(obj->address.objectSubId);

        /* object_type */
        values[i++] = CStringGetTextDatum(obj->objecttype);

        /* schema_name */
        if (obj->schemaname)
            values[i++] = CStringGetTextDatum(obj->schemaname);
        else
            nulls[i++] = true;

        /* object_name */
        if (obj->objname)
            values[i++] = CStringGetTextDatum(obj->objname);
        else
            nulls[i++] = true;

        /* object_identity */
        if (obj->objidentity)
            values[i++] = CStringGetTextDatum(obj->objidentity);
        else
            nulls[i++] = true;

        tuplestore_putvalues(tupstore, tupdesc, values, nulls);
    }

    /* clean up and return the tuplestore */
    tuplestore_donestoring(tupstore);

    return (Datum) 0;
}

void RemoveEventTriggerById ( Oid  trigOid  ) 
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;
}

static void validate_ddl_tags ( const char *  filtervar,
List taglist 
) [static]

Definition at line 215 of file event_trigger.c.

References check_ddl_tag(), ereport, errcode(), errmsg(), ERROR, EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED, EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED, lfirst, and strVal.

Referenced by CreateEventTrigger().

{
    ListCell   *lc;

    foreach (lc, taglist)
    {
        const char *tag = strVal(lfirst(lc));
        event_trigger_command_tag_check_result result;

        result = check_ddl_tag(tag);
        if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED)
            ereport(ERROR,
                    (errcode(ERRCODE_SYNTAX_ERROR),
                     errmsg("filter value \"%s\" not recognized for filter variable \"%s\"",
                        tag, filtervar)));
        if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED)
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 /* translator: %s represents an SQL statement name */
                 errmsg("event triggers are not supported for %s",
                    tag)));
    }
}


Variable Documentation

Definition at line 54 of file event_trigger.c.

Definition at line 69 of file event_trigger.c.