mirror of
https://github.com/postgres/postgres.git
synced 2025-07-05 07:21:24 +03:00
Add ddl_command_end support for event triggers.
Dimitri Fontaine, with slight changes by me
This commit is contained in:
@ -125,7 +125,8 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
|
||||
errhint("Must be superuser to create an event trigger.")));
|
||||
|
||||
/* Validate event name. */
|
||||
if (strcmp(stmt->eventname, "ddl_command_start") != 0)
|
||||
if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&
|
||||
strcmp(stmt->eventname, "ddl_command_end") != 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("unrecognized event name \"%s\"",
|
||||
@ -526,6 +527,39 @@ get_event_trigger_oid(const char *trigname, bool missing_ok)
|
||||
return oid;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true when we want to fire given Event Trigger and false otherwise,
|
||||
* filtering on the session replication role and the event trigger registered
|
||||
* tags matching.
|
||||
*/
|
||||
static bool
|
||||
filter_event_trigger(const char **tag, EventTriggerCacheItem *item)
|
||||
{
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fire ddl_command_start triggers.
|
||||
*/
|
||||
@ -601,26 +635,11 @@ EventTriggerDDLCommandStart(Node *parsetree)
|
||||
{
|
||||
EventTriggerCacheItem *item = lfirst(lc);
|
||||
|
||||
/* Filter by session replication role. */
|
||||
if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
|
||||
if (filter_event_trigger(&tag, item))
|
||||
{
|
||||
if (item->enabled == TRIGGER_FIRES_ON_ORIGIN)
|
||||
continue;
|
||||
/* We must plan to fire this trigger. */
|
||||
runlist = lappend_oid(runlist, item->fnoid);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (item->enabled == TRIGGER_FIRES_ON_REPLICA)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Filter by tags, if any were specified. */
|
||||
if (item->ntags != 0 && bsearch(&tag, item->tag,
|
||||
item->ntags, sizeof(char *),
|
||||
pg_qsort_strcmp) == NULL)
|
||||
continue;
|
||||
|
||||
/* We must plan to fire this trigger. */
|
||||
runlist = lappend_oid(runlist, item->fnoid);
|
||||
}
|
||||
|
||||
/* Construct event trigger data. */
|
||||
@ -634,6 +653,92 @@ EventTriggerDDLCommandStart(Node *parsetree)
|
||||
|
||||
/* Cleanup. */
|
||||
list_free(runlist);
|
||||
|
||||
/*
|
||||
* Make sure anything the event triggers did will be visible to
|
||||
* the main command.
|
||||
*/
|
||||
CommandCounterIncrement();
|
||||
}
|
||||
|
||||
/*
|
||||
* Fire ddl_command_end triggers.
|
||||
*/
|
||||
void
|
||||
EventTriggerDDLCommandEnd(Node *parsetree)
|
||||
{
|
||||
List *cachelist;
|
||||
List *runlist = NIL;
|
||||
ListCell *lc;
|
||||
const char *tag;
|
||||
EventTriggerData trigdata;
|
||||
|
||||
/*
|
||||
* See EventTriggerDDLCommandStart for a discussion about why event
|
||||
* triggers are disabled in single user mode.
|
||||
*/
|
||||
if (!IsUnderPostmaster)
|
||||
return;
|
||||
|
||||
/*
|
||||
* See EventTriggerDDLCommandStart for a discussion about why this check is
|
||||
* important.
|
||||
*
|
||||
*/
|
||||
#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(EVT_DDLCommandEnd);
|
||||
if (cachelist == NULL)
|
||||
return;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/* Construct event trigger data. */
|
||||
trigdata.type = T_EventTriggerData;
|
||||
trigdata.event = "ddl_command_end";
|
||||
trigdata.parsetree = parsetree;
|
||||
trigdata.tag = tag;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -645,6 +750,7 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
|
||||
MemoryContext context;
|
||||
MemoryContext oldcontext;
|
||||
ListCell *lc;
|
||||
bool first = true;
|
||||
|
||||
/*
|
||||
* Let's evaluate event triggers in their own memory context, so
|
||||
@ -665,6 +771,17 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
|
||||
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);
|
||||
|
||||
@ -677,13 +794,6 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
|
||||
|
||||
/* Reclaim memory. */
|
||||
MemoryContextReset(context);
|
||||
|
||||
/*
|
||||
* We want each event trigger to be able to see the results of
|
||||
* the previous event trigger's action, and we want the main
|
||||
* command to be able to see the results of all event triggers.
|
||||
*/
|
||||
CommandCounterIncrement();
|
||||
}
|
||||
|
||||
/* Restore old memory context and delete the temporary one. */
|
||||
|
Reference in New Issue
Block a user