#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"
Go to the source code of this file.
typedef struct EventTriggerQueryState EventTriggerQueryState |
typedef struct SQLDropObject SQLDropObject |
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;
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; }
Definition at line 459 of file event_trigger.c.
References AlterEventTriggerOwner_internal(), CStringGetDatum, ereport, errcode(), errmsg(), ERROR, EVENTTRIGGERNAME, EventTriggerRelationId, heap_close, heap_freetuple(), heap_open(), HeapTupleGetOid, HeapTupleIsValid, RowExclusiveLock, and SearchSysCacheCopy1.
Referenced by ExecAlterOwnerStmt().
{ Oid evtOid; HeapTuple tup; Relation rel; rel = heap_open(EventTriggerRelationId, RowExclusiveLock); tup = SearchSysCacheCopy1(EVENTTRIGGERNAME, CStringGetDatum(name)); if (!HeapTupleIsValid(tup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("event trigger \"%s\" does not exist", name))); evtOid = HeapTupleGetOid(tup); AlterEventTriggerOwner_internal(rel, tup, newOwnerId); heap_freetuple(tup); heap_close(rel, RowExclusiveLock); return evtOid; }
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); }
Definition at line 489 of file event_trigger.c.
References AlterEventTriggerOwner_internal(), ereport, errcode(), errmsg(), ERROR, EVENTTRIGGEROID, EventTriggerRelationId, heap_close, heap_freetuple(), heap_open(), HeapTupleIsValid, ObjectIdGetDatum, RowExclusiveLock, and SearchSysCacheCopy1.
Referenced by shdepReassignOwned().
{ HeapTuple tup; Relation rel; rel = heap_open(EventTriggerRelationId, RowExclusiveLock); tup = SearchSysCacheCopy1(EVENTTRIGGEROID, ObjectIdGetDatum(trigOid)); if (!HeapTupleIsValid(tup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("event trigger with OID %u does not exist", trigOid))); AlterEventTriggerOwner_internal(rel, tup, newOwnerId); heap_freetuple(tup); heap_close(rel, RowExclusiveLock); }
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] |
bool EventTriggerBeginCompleteQuery | ( | void | ) |
Definition at line 1008 of file event_trigger.c.
References ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), EventTriggerQueryState::cxt, EventTriggerQueryState::in_sql_drop, MemoryContextAlloc(), EventTriggerQueryState::previous, slist_init(), EventTriggerQueryState::SQLDropList, TopMemoryContext, and trackDroppedObjectsNeeded().
Referenced by ProcessUtilitySlow().
{ EventTriggerQueryState *state; MemoryContext cxt; /* * Currently, sql_drop events are the only reason to have event trigger * state at all; so if there are none, don't install one. */ if (!trackDroppedObjectsNeeded()) return false; cxt = AllocSetContextCreate(TopMemoryContext, "event trigger state", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); state = MemoryContextAlloc(cxt, sizeof(EventTriggerQueryState)); state->cxt = cxt; slist_init(&(state->SQLDropList)); state->in_sql_drop = false; state->previous = currentEventTriggerState; currentEventTriggerState = state; return true; }
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(¤tEventTriggerState->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; }
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')); }
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 | ) |
Definition at line 392 of file event_trigger.c.
References elog, ERROR, EVENTTRIGGEROID, EventTriggerRelationId, heap_close, heap_open(), HeapTupleIsValid, ObjectIdGetDatum, ReleaseSysCache(), RowExclusiveLock, SearchSysCache1, simple_heap_delete(), and HeapTupleData::t_self.
Referenced by doDeletion().
{ Relation tgrel; HeapTuple tup; tgrel = heap_open(EventTriggerRelationId, RowExclusiveLock); tup = SearchSysCache1(EVENTTRIGGEROID, ObjectIdGetDatum(trigOid)); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for event trigger %u", trigOid); simple_heap_delete(tgrel, &tup->t_self); ReleaseSysCache(tup); heap_close(tgrel, RowExclusiveLock); }
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))); } }
Definition at line 54 of file event_trigger.c.
event_trigger_support_data event_trigger_support[] [static] |
Definition at line 69 of file event_trigger.c.