diff options
Diffstat (limited to 'src/backend/commands/event_trigger.c')
-rw-r--r-- | src/backend/commands/event_trigger.c | 192 |
1 files changed, 184 insertions, 8 deletions
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index 6a3002f5268..6fb4817845c 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -42,11 +42,16 @@ #include "utils/syscache.h" #include "tcop/utility.h" - typedef struct EventTriggerQueryState { + /* sql_drop */ slist_head SQLDropList; bool in_sql_drop; + + /* table_rewrite */ + Oid table_rewrite_oid; /* InvalidOid, or set for table_rewrite event */ + int table_rewrite_reason; /* AT_REWRITE reason */ + MemoryContext cxt; struct EventTriggerQueryState *previous; } EventTriggerQueryState; @@ -119,11 +124,14 @@ static void AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId); static event_trigger_command_tag_check_result check_ddl_tag(const char *tag); +static event_trigger_command_tag_check_result check_table_rewrite_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 validate_table_rewrite_tags(const char *filtervar, List *taglist); static void EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata); /* @@ -154,7 +162,8 @@ CreateEventTrigger(CreateEventTrigStmt *stmt) /* Validate event name. */ if (strcmp(stmt->eventname, "ddl_command_start") != 0 && strcmp(stmt->eventname, "ddl_command_end") != 0 && - strcmp(stmt->eventname, "sql_drop") != 0) + strcmp(stmt->eventname, "sql_drop") != 0 && + strcmp(stmt->eventname, "table_rewrite") != 0) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("unrecognized event name \"%s\"", @@ -183,6 +192,9 @@ CreateEventTrigger(CreateEventTrigStmt *stmt) strcmp(stmt->eventname, "sql_drop") == 0) && tags != NULL) validate_ddl_tags("tag", tags); + else if (strcmp(stmt->eventname, "table_rewrite") == 0 + && tags != NULL) + validate_table_rewrite_tags("tag", tags); /* * Give user a nice error message if an event trigger of the same name @@ -281,6 +293,38 @@ check_ddl_tag(const char *tag) } /* + * Validate DDL command tags for event table_rewrite. + */ +static void +validate_table_rewrite_tags(const char *filtervar, List *taglist) +{ + ListCell *lc; + + foreach(lc, taglist) + { + const char *tag = strVal(lfirst(lc)); + event_trigger_command_tag_check_result result; + + result = check_table_rewrite_ddl_tag(tag); + 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))); + } +} + +static event_trigger_command_tag_check_result +check_table_rewrite_ddl_tag(const char *tag) +{ + if (pg_strcasecmp(tag, "ALTER TABLE") == 0) + return EVENT_TRIGGER_COMMAND_TAG_OK; + + return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED; +} + +/* * Complain about a duplicate filter variable. */ static void @@ -641,8 +685,18 @@ EventTriggerCommonSetup(Node *parsetree, const char *dbgtag; dbgtag = CreateCommandTag(parsetree); - if (check_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK) - elog(ERROR, "unexpected command tag \"%s\"", dbgtag); + if (event == EVT_DDLCommandStart || + event == EVT_DDLCommandEnd || + event == EVT_SQLDrop) + { + if (check_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK) + elog(ERROR, "unexpected command tag \"%s\"", dbgtag); + } + else if (event == EVT_TableRewrite) + { + if (check_table_rewrite_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK) + elog(ERROR, "unexpected command tag \"%s\"", dbgtag); + } } #endif @@ -838,6 +892,80 @@ EventTriggerSQLDrop(Node *parsetree) list_free(runlist); } + +/* + * Fire table_rewrite triggers. + */ +void +EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason) +{ + List *runlist; + EventTriggerData trigdata; + + elog(DEBUG1, "EventTriggerTableRewrite(%u)", tableOid); + + /* + * 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_TableRewrite, + "table_rewrite", + &trigdata); + if (runlist == NIL) + return; + + /* + * Make sure pg_event_trigger_table_rewrite_oid only works when running + * these triggers. Use PG_TRY to ensure table_rewrite_oid 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->table_rewrite_oid = tableOid; + currentEventTriggerState->table_rewrite_reason = reason; + + /* Run the triggers. */ + PG_TRY(); + { + EventTriggerInvoke(runlist, &trigdata); + } + PG_CATCH(); + { + currentEventTriggerState->table_rewrite_oid = InvalidOid; + currentEventTriggerState->table_rewrite_reason = 0; + PG_RE_THROW(); + } + PG_END_TRY(); + + currentEventTriggerState->table_rewrite_oid = InvalidOid; + currentEventTriggerState->table_rewrite_reason = 0; + + /* Cleanup. */ + list_free(runlist); + + /* + * Make sure anything the event triggers did will be visible to the main + * command. + */ + CommandCounterIncrement(); +} + /* * Invoke each event trigger in a list of event triggers. */ @@ -871,6 +999,8 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata) FunctionCallInfoData fcinfo; PgStat_FunctionCallUsage fcusage; + elog(DEBUG1, "EventTriggerInvoke %u", fnoid); + /* * We want each event trigger to be able to see the results of the * previous event trigger's action. Caller is responsible for any @@ -1026,8 +1156,9 @@ EventTriggerBeginCompleteQuery(void) 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. + * Currently, sql_drop and table_rewrite 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; @@ -1041,6 +1172,7 @@ EventTriggerBeginCompleteQuery(void) state->cxt = cxt; slist_init(&(state->SQLDropList)); state->in_sql_drop = false; + state->table_rewrite_oid = InvalidOid; state->previous = currentEventTriggerState; currentEventTriggerState = state; @@ -1080,8 +1212,9 @@ EventTriggerEndCompleteQuery(void) bool trackDroppedObjectsNeeded(void) { - /* true if any sql_drop event trigger exists */ - return list_length(EventCacheLookup(EVT_SQLDrop)) > 0; + /* true if any sql_drop or table_rewrite event trigger exists */ + return list_length(EventCacheLookup(EVT_SQLDrop)) > 0 || + list_length(EventCacheLookup(EVT_TableRewrite)) > 0; } /* @@ -1297,3 +1430,46 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS) return (Datum) 0; } + +/* + * pg_event_trigger_table_rewrite_oid + * + * Make the Oid of the table going to be rewritten available to the user + * function run by the Event Trigger. + */ +Datum +pg_event_trigger_table_rewrite_oid(PG_FUNCTION_ARGS) +{ + /* + * Protect this function from being called out of context + */ + if (!currentEventTriggerState || + currentEventTriggerState->table_rewrite_oid == InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("%s can only be called in a table_rewrite event trigger function", + "pg_event_trigger_table_rewrite_oid()"))); + + PG_RETURN_OID(currentEventTriggerState->table_rewrite_oid); +} + +/* + * pg_event_trigger_table_rewrite_reason + * + * Make the rewrite reason available to the user. + */ +Datum +pg_event_trigger_table_rewrite_reason(PG_FUNCTION_ARGS) +{ + /* + * Protect this function from being called out of context + */ + if (!currentEventTriggerState || + currentEventTriggerState->table_rewrite_reason == 0) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("%s can only be called in a table_rewrite event trigger function", + "pg_event_trigger_table_rewrite_reason()"))); + + PG_RETURN_INT32(currentEventTriggerState->table_rewrite_reason); +} |