#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.
1.7.1