diff options
author | Robert Haas <rhaas@postgresql.org> | 2012-07-18 10:16:16 -0400 |
---|---|---|
committer | Robert Haas <rhaas@postgresql.org> | 2012-07-18 10:16:16 -0400 |
commit | 3855968f328918b6cd1401dd11d109d471a54d40 (patch) | |
tree | 37cf34023851c0c3d14f30b4833937cfaeb2fa34 /src/backend | |
parent | faf26bf1175530cc97ce3e804ff10dc2be7026d3 (diff) | |
download | postgresql-3855968f328918b6cd1401dd11d109d471a54d40.tar.gz postgresql-3855968f328918b6cd1401dd11d109d471a54d40.zip |
Syntax support and documentation for event triggers.
They don't actually do anything yet; that will get fixed in a
follow-on commit. But this gets the basic infrastructure in place,
including CREATE/ALTER/DROP EVENT TRIGGER; support for COMMENT,
SECURITY LABEL, and ALTER EXTENSION .. ADD/DROP EVENT TRIGGER;
pg_dump and psql support; and documentation for the anticipated
initial feature set.
Dimitri Fontaine, with review and a bunch of additional hacking by me.
Thom Brown extensively reviewed earlier versions of this patch set,
but there's not a whole lot of that code left in this commit, as it
turns out.
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/catalog/Makefile | 2 | ||||
-rw-r--r-- | src/backend/catalog/aclchk.c | 40 | ||||
-rw-r--r-- | src/backend/catalog/dependency.c | 27 | ||||
-rw-r--r-- | src/backend/catalog/objectaddress.c | 22 | ||||
-rw-r--r-- | src/backend/catalog/pg_shdepend.c | 6 | ||||
-rw-r--r-- | src/backend/catalog/system_views.sql | 13 | ||||
-rw-r--r-- | src/backend/commands/Makefile | 4 | ||||
-rw-r--r-- | src/backend/commands/alter.c | 9 | ||||
-rw-r--r-- | src/backend/commands/dropcmds.c | 4 | ||||
-rw-r--r-- | src/backend/commands/event_trigger.c | 539 | ||||
-rw-r--r-- | src/backend/nodes/copyfuncs.c | 30 | ||||
-rw-r--r-- | src/backend/nodes/equalfuncs.c | 26 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 113 | ||||
-rw-r--r-- | src/backend/tcop/utility.c | 33 | ||||
-rw-r--r-- | src/backend/utils/adt/pseudotypes.c | 27 | ||||
-rw-r--r-- | src/backend/utils/cache/syscache.c | 23 |
16 files changed, 910 insertions, 8 deletions
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index 62fc9b04663..df6da1f0d33 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -31,7 +31,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \ pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \ pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \ - pg_statistic.h pg_rewrite.h pg_trigger.h pg_description.h \ + pg_statistic.h pg_rewrite.h pg_trigger.h pg_event_trigger.h pg_description.h \ pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \ pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index 56c40b14e00..b097813a063 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -29,6 +29,7 @@ #include "catalog/pg_conversion.h" #include "catalog/pg_database.h" #include "catalog/pg_default_acl.h" +#include "catalog/pg_event_trigger.h" #include "catalog/pg_extension.h" #include "catalog/pg_foreign_data_wrapper.h" #include "catalog/pg_foreign_server.h" @@ -277,6 +278,10 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs, case ACL_KIND_FOREIGN_SERVER: whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER; break; + case ACL_KIND_EVENT_TRIGGER: + elog(ERROR, "grantable rights not supported for event triggers"); + /* not reached, but keep compiler quiet */ + return ACL_NO_RIGHTS; case ACL_KIND_TYPE: whole_mask = ACL_ALL_RIGHTS_TYPE; break; @@ -3286,6 +3291,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] = gettext_noop("permission denied for foreign-data wrapper %s"), /* ACL_KIND_FOREIGN_SERVER */ gettext_noop("permission denied for foreign server %s"), + /* ACL_KIND_EVENT_TRIGGER */ + gettext_noop("permission denied for event trigger %s"), /* ACL_KIND_EXTENSION */ gettext_noop("permission denied for extension %s"), }; @@ -3330,6 +3337,8 @@ static const char *const not_owner_msg[MAX_ACL_KIND] = gettext_noop("must be owner of foreign-data wrapper %s"), /* ACL_KIND_FOREIGN_SERVER */ gettext_noop("must be owner of foreign server %s"), + /* ACL_KIND_EVENT_TRIGGER */ + gettext_noop("must be owner of event trigger %s"), /* ACL_KIND_EXTENSION */ gettext_noop("must be owner of extension %s"), }; @@ -3455,6 +3464,10 @@ pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum, Oid roleid, return pg_foreign_data_wrapper_aclmask(table_oid, roleid, mask, how); case ACL_KIND_FOREIGN_SERVER: return pg_foreign_server_aclmask(table_oid, roleid, mask, how); + case ACL_KIND_EVENT_TRIGGER: + elog(ERROR, "grantable rights not supported for event triggers"); + /* not reached, but keep compiler quiet */ + return ACL_NO_RIGHTS; case ACL_KIND_TYPE: return pg_type_aclmask(table_oid, roleid, mask, how); default: @@ -4876,6 +4889,33 @@ pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid) } /* + * Ownership check for an event trigger (specified by OID). + */ +bool +pg_event_trigger_ownercheck(Oid et_oid, Oid roleid) +{ + HeapTuple tuple; + Oid ownerId; + + /* Superusers bypass all permission checking. */ + if (superuser_arg(roleid)) + return true; + + tuple = SearchSysCache1(EVENTTRIGGEROID, ObjectIdGetDatum(et_oid)); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("event trigger with OID %u does not exist", + et_oid))); + + ownerId = ((Form_pg_event_trigger) GETSTRUCT(tuple))->evtowner; + + ReleaseSysCache(tuple); + + return has_privs_of_role(roleid, ownerId); +} + +/* * Ownership check for a database (specified by OID). */ bool diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 98ce5981c11..7d07c56cd0b 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -34,6 +34,7 @@ #include "catalog/pg_database.h" #include "catalog/pg_default_acl.h" #include "catalog/pg_depend.h" +#include "catalog/pg_event_trigger.h" #include "catalog/pg_extension.h" #include "catalog/pg_foreign_data_wrapper.h" #include "catalog/pg_foreign_server.h" @@ -56,6 +57,7 @@ #include "commands/comment.h" #include "commands/dbcommands.h" #include "commands/defrem.h" +#include "commands/event_trigger.h" #include "commands/extension.h" #include "commands/proclang.h" #include "commands/schemacmds.h" @@ -158,7 +160,8 @@ static const Oid object_classes[MAX_OCLASS] = { ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */ UserMappingRelationId, /* OCLASS_USER_MAPPING */ DefaultAclRelationId, /* OCLASS_DEFACL */ - ExtensionRelationId /* OCLASS_EXTENSION */ + ExtensionRelationId, /* OCLASS_EXTENSION */ + EventTriggerRelationId /* OCLASS_EVENT_TRIGGER */ }; @@ -1112,6 +1115,10 @@ doDeletion(const ObjectAddress *object, int flags) break; } + case OCLASS_EVENT_TRIGGER: + RemoveEventTriggerById(object->objectId); + break; + case OCLASS_PROC: RemoveFunctionById(object->objectId); break; @@ -2269,6 +2276,9 @@ getObjectClass(const ObjectAddress *object) case ExtensionRelationId: return OCLASS_EXTENSION; + + case EventTriggerRelationId: + return OCLASS_EVENT_TRIGGER; } /* shouldn't get here */ @@ -2903,6 +2913,21 @@ getObjectDescription(const ObjectAddress *object) break; } + case OCLASS_EVENT_TRIGGER: + { + HeapTuple tup; + + tup = SearchSysCache1(EVENTTRIGGEROID, + ObjectIdGetDatum(object->objectId)); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for event trigger %u", + object->objectId); + appendStringInfo(&buffer, _("event trigger %s"), + NameStr(((Form_pg_event_trigger) GETSTRUCT(tup))->evtname)); + ReleaseSysCache(tup); + break; + } + default: appendStringInfo(&buffer, "unrecognized object %u %u %d", object->classId, diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index 19bde9f4761..5b8140b0ae7 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -21,6 +21,7 @@ #include "catalog/objectaddress.h" #include "catalog/pg_authid.h" #include "catalog/pg_cast.h" +#include "catalog/pg_event_trigger.h" #include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_conversion.h" @@ -46,6 +47,7 @@ #include "catalog/pg_type.h" #include "commands/dbcommands.h" #include "commands/defrem.h" +#include "commands/event_trigger.h" #include "commands/extension.h" #include "commands/proclang.h" #include "commands/tablespace.h" @@ -204,6 +206,12 @@ static ObjectPropertyType ObjectProperty[] = InvalidAttrNumber }, { + EventTriggerRelationId, + EventTriggerOidIndexId, + -1, + InvalidAttrNumber + }, + { TSConfigRelationId, TSConfigOidIndexId, TSCONFIGOID, @@ -325,6 +333,7 @@ get_object_address(ObjectType objtype, List *objname, List *objargs, case OBJECT_LANGUAGE: case OBJECT_FDW: case OBJECT_FOREIGN_SERVER: + case OBJECT_EVENT_TRIGGER: address = get_object_address_unqualified(objtype, objname, missing_ok); break; @@ -546,6 +555,9 @@ get_object_address_unqualified(ObjectType objtype, case OBJECT_FOREIGN_SERVER: msg = gettext_noop("server name cannot be qualified"); break; + case OBJECT_EVENT_TRIGGER: + msg = gettext_noop("event trigger name cannot be qualified"); + break; default: elog(ERROR, "unrecognized objtype: %d", (int) objtype); msg = NULL; /* placate compiler */ @@ -601,6 +613,11 @@ get_object_address_unqualified(ObjectType objtype, address.objectId = get_foreign_server_oid(name, missing_ok); address.objectSubId = 0; break; + case OBJECT_EVENT_TRIGGER: + address.classId = EventTriggerRelationId; + address.objectId = get_event_trigger_oid(name, missing_ok); + address.objectSubId = 0; + break; default: elog(ERROR, "unrecognized objtype: %d", (int) objtype); /* placate compiler, which doesn't know elog won't return */ @@ -980,6 +997,11 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, NameListToString(objname)); break; + case OBJECT_EVENT_TRIGGER: + if (!pg_event_trigger_ownercheck(address.objectId, roleid)) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER, + NameListToString(objname)); + break; case OBJECT_LANGUAGE: if (!pg_language_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c index a2bb9f58a2d..23111eccc5d 100644 --- a/src/backend/catalog/pg_shdepend.c +++ b/src/backend/catalog/pg_shdepend.c @@ -25,6 +25,7 @@ #include "catalog/pg_conversion.h" #include "catalog/pg_database.h" #include "catalog/pg_default_acl.h" +#include "catalog/pg_event_trigger.h" #include "catalog/pg_extension.h" #include "catalog/pg_foreign_data_wrapper.h" #include "catalog/pg_foreign_server.h" @@ -42,6 +43,7 @@ #include "commands/collationcmds.h" #include "commands/conversioncmds.h" #include "commands/defrem.h" +#include "commands/event_trigger.h" #include "commands/extension.h" #include "commands/proclang.h" #include "commands/schemacmds.h" @@ -1398,6 +1400,10 @@ shdepReassignOwned(List *roleids, Oid newrole) AlterExtensionOwner_oid(sdepForm->objid, newrole); break; + case EventTriggerRelationId: + AlterEventTriggerOwner_oid(sdepForm->objid, newrole); + break; + default: elog(ERROR, "unexpected classid %u", sdepForm->classid); break; diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 7cc1d41a7da..607a72f6d76 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -311,6 +311,19 @@ WHERE l.objsubid = 0 UNION ALL SELECT + l.objoid, l.classoid, l.objsubid, + 'event trigger'::text AS objtype, + NULL::oid AS objnamespace, + quote_ident(evt.evtname) AS objname, + l.provider, l.label +FROM + pg_seclabel l + JOIN pg_event_trigger evt ON l.classoid = evt.tableoid + AND l.objoid = evt.oid +WHERE + l.objsubid = 0 +UNION ALL +SELECT l.objoid, l.classoid, 0::int4 AS objsubid, 'database'::text AS objtype, NULL::oid AS objnamespace, diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile index 9573a0db45b..3c322a34413 100644 --- a/src/backend/commands/Makefile +++ b/src/backend/commands/Makefile @@ -14,8 +14,8 @@ include $(top_builddir)/src/Makefile.global OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \ collationcmds.o constraint.o conversioncmds.o copy.o createas.o \ - dbcommands.o define.o discard.o dropcmds.o explain.o extension.o \ - foreigncmds.o functioncmds.o \ + dbcommands.o define.o discard.o dropcmds.o \ + event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \ indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \ portalcmds.o prepare.o proclang.o \ schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \ diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index 4dd9927afba..19f989549c7 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -24,6 +24,7 @@ #include "commands/conversioncmds.h" #include "commands/dbcommands.h" #include "commands/defrem.h" +#include "commands/event_trigger.h" #include "commands/extension.h" #include "commands/proclang.h" #include "commands/schemacmds.h" @@ -77,6 +78,10 @@ ExecRenameStmt(RenameStmt *stmt) RenameForeignServer(stmt->subname, stmt->newname); break; + case OBJECT_EVENT_TRIGGER: + RenameEventTrigger(stmt->subname, stmt->newname); + break; + case OBJECT_FUNCTION: RenameFunction(stmt->object, stmt->objarg, stmt->newname); break; @@ -534,6 +539,10 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt) AlterForeignServerOwner(strVal(linitial(stmt->object)), newowner); break; + case OBJECT_EVENT_TRIGGER: + AlterEventTriggerOwner(strVal(linitial(stmt->object)), newowner); + break; + default: elog(ERROR, "unrecognized AlterOwnerStmt type: %d", (int) stmt->objectType); diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c index 1b8529ed843..8f5d7e0ed2b 100644 --- a/src/backend/commands/dropcmds.c +++ b/src/backend/commands/dropcmds.c @@ -206,6 +206,10 @@ does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs) args = NameListToString(list_truncate(objname, list_length(objname) - 1)); break; + case OBJECT_EVENT_TRIGGER: + msg = gettext_noop("event trigger \"%s\" does not exist, skipping"); + name = NameListToString(objname); + break; case OBJECT_RULE: msg = gettext_noop("rule \"%s\" for relation \"%s\" does not exist, skipping"); name = strVal(llast(objname)); diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c new file mode 100644 index 00000000000..9d36b0c9316 --- /dev/null +++ b/src/backend/commands/event_trigger.c @@ -0,0 +1,539 @@ +/*------------------------------------------------------------------------- + * + * event_trigger.c + * PostgreSQL EVENT TRIGGER support code. + * + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/commands/event_trigger.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "catalog/dependency.h" +#include "catalog/indexing.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_event_trigger.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 "parser/parse_func.h" +#include "pgstat.h" +#include "miscadmin.h" +#include "utils/acl.h" +#include "utils/builtins.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" + +typedef struct +{ + const char *obtypename; + ObjectType obtype; + bool supported; +} event_trigger_support_data; + +static event_trigger_support_data event_trigger_support[] = { + { "AGGREGATE", OBJECT_AGGREGATE, true }, + { "CAST", OBJECT_CAST, true }, + { "CONSTRAINT", OBJECT_CONSTRAINT, true }, + { "COLLATION", OBJECT_COLLATION, true }, + { "CONVERSION", OBJECT_CONVERSION, true }, + { "DATABASE", OBJECT_DATABASE, false }, + { "DOMAIN", OBJECT_DOMAIN, true }, + { "EXTENSION", OBJECT_EXTENSION, true }, + { "EVENT TRIGGER", OBJECT_EVENT_TRIGGER, false }, + { "FOREIGN DATA WRAPPER", OBJECT_FDW, true }, + { "FOREIGN SERVER", OBJECT_FOREIGN_SERVER, true }, + { "FOREIGN TABLE", OBJECT_FOREIGN_TABLE, true }, + { "FUNCTION", OBJECT_FUNCTION, true }, + { "INDEX", OBJECT_INDEX, true }, + { "LANGUAGE", OBJECT_LANGUAGE, true }, + { "OPERATOR", OBJECT_OPERATOR, true }, + { "OPERATOR CLASS", OBJECT_OPCLASS, true }, + { "OPERATOR FAMILY", OBJECT_OPFAMILY, true }, + { "ROLE", OBJECT_ROLE, false }, + { "RULE", OBJECT_RULE, true }, + { "SCHEMA", OBJECT_SCHEMA, true }, + { "SEQUENCE", OBJECT_SEQUENCE, true }, + { "TABLE", OBJECT_TABLE, true }, + { "TABLESPACE", OBJECT_TABLESPACE, false}, + { "TRIGGER", OBJECT_TRIGGER, true }, + { "TEXT SEARCH CONFIGURATION", OBJECT_TSCONFIGURATION, true }, + { "TEXT SEARCH DICTIONARY", OBJECT_TSDICTIONARY, true }, + { "TEXT SEARCH PARSER", OBJECT_TSPARSER, true }, + { "TEXT SEARCH TEMPLATE", OBJECT_TSTEMPLATE, true }, + { "TYPE", OBJECT_TYPE, true }, + { "VIEW", OBJECT_VIEW, true }, + { NULL, (ObjectType) 0, false } +}; + +static void AlterEventTriggerOwner_internal(Relation rel, + HeapTuple tup, + Oid newOwnerId); +static void error_duplicate_filter_variable(const char *defname); +static void error_unrecognized_filter_value(const char *var, const char *val); +static Datum filter_list_to_array(List *filterlist); +static void insert_event_trigger_tuple(char *trigname, char *eventname, + Oid evtOwner, Oid funcoid, List *tags); +static void validate_ddl_tags(const char *filtervar, List *taglist); + +/* + * Create an event trigger. + */ +void +CreateEventTrigger(CreateEventTrigStmt *stmt) +{ + 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) + 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 && 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. */ + insert_event_trigger_tuple(stmt->trigname, stmt->eventname, + evtowner, funcoid, tags); +} + +/* + * Validate DDL command tags. + */ +static void +validate_ddl_tags(const char *filtervar, List *taglist) +{ + ListCell *lc; + + foreach (lc, taglist) + { + const char *tag = strVal(lfirst(lc)); + const char *obtypename = NULL; + event_trigger_support_data *etsd; + + /* + * As a special case, SELECT INTO is considered DDL, since it creates + * a table. + */ + if (strcmp(tag, "SELECT INTO") == 0) + continue; + + + /* + * Otherwise, it 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; + if (obtypename == NULL) + error_unrecognized_filter_value(filtervar, tag); + + /* + * ...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) + error_unrecognized_filter_value(filtervar, tag); + if (!etsd->supported) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + /* translator: %s represents an SQL statement name */ + errmsg("event triggers are not supported for \"%s\"", + tag))); + } +} + +/* + * Complain about a duplicate filter variable. + */ +static void +error_duplicate_filter_variable(const char *defname) +{ + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("filter variable \"%s\" specified more than once", + defname))); +} + +/* + * Complain about an invalid filter value. + */ +static void +error_unrecognized_filter_value(const char *var, const char *val) +{ + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("filter value \"%s\" not recognized for filter variable \"%s\"", + val, var))); +} + +/* + * Insert the new pg_event_trigger row and record dependencies. + */ +static void +insert_event_trigger_tuple(char *trigname, char *eventname, Oid evtOwner, + Oid funcoid, List *taglist) +{ + 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 */ + InvokeObjectAccessHook(OAT_POST_CREATE, + EventTriggerRelationId, trigoid, 0, NULL); + + /* Close pg_event_trigger. */ + heap_close(tgrel, RowExclusiveLock); +} + +/* + * In the parser, a clause like WHEN tag IN ('cmd1', 'cmd2') is represented + * by a DefElem whose value is a List of String nodes; in the catalog, we + * store the list of strings as a text array. This function transforms the + * former representation into the latter one. + * + * For cleanliness, we store command tags in the catalog as text. It's + * possible (although not currently anticipated) that we might have + * a case-sensitive filter variable in the future, in which case this would + * need some further adjustment. + */ +static Datum +filter_list_to_array(List *filterlist) +{ + 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')); +} + +/* + * Guts of event trigger deletion. + */ +void +RemoveEventTriggerById(Oid trigOid) +{ + 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); +} + +/* + * ALTER EVENT TRIGGER foo ENABLE|DISABLE|ENABLE ALWAYS|REPLICA + */ +void +AlterEventTrigger(AlterEventTrigStmt *stmt) +{ + Relation tgrel; + HeapTuple tup; + 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))); + if (!pg_event_trigger_ownercheck(HeapTupleGetOid(tup), 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); + + /* clean up */ + heap_freetuple(tup); + heap_close(tgrel, RowExclusiveLock); +} + + +/* + * Rename event trigger + */ +void +RenameEventTrigger(const char *trigname, const char *newname) +{ + HeapTuple tup; + Relation rel; + Form_pg_event_trigger evtForm; + + rel = heap_open(EventTriggerRelationId, RowExclusiveLock); + + /* newname must be available */ + if (SearchSysCacheExists1(EVENTTRIGGERNAME, CStringGetDatum(newname))) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("event trigger \"%s\" already exists", newname))); + + /* trigname must exists */ + tup = SearchSysCacheCopy1(EVENTTRIGGERNAME, CStringGetDatum(trigname)); + if (!HeapTupleIsValid(tup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("event trigger \"%s\" does not exist", trigname))); + if (!pg_event_trigger_ownercheck(HeapTupleGetOid(tup), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER, + trigname); + + evtForm = (Form_pg_event_trigger) GETSTRUCT(tup); + + /* tuple is a copy, so we can rename it now */ + namestrcpy(&(evtForm->evtname), newname); + simple_heap_update(rel, &tup->t_self, tup); + CatalogUpdateIndexes(rel, tup); + + heap_freetuple(tup); + heap_close(rel, RowExclusiveLock); +} + + +/* + * Change event trigger's owner -- by name + */ +void +AlterEventTriggerOwner(const char *name, Oid newOwnerId) +{ + 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))); + + AlterEventTriggerOwner_internal(rel, tup, newOwnerId); + + heap_freetuple(tup); + + heap_close(rel, RowExclusiveLock); +} + +/* + * Change extension owner, by OID + */ +void +AlterEventTriggerOwner_oid(Oid trigOid, Oid newOwnerId) +{ + 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); +} + +/* + * Internal workhorse for changing an event trigger's owner + */ +static void +AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) +{ + 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); +} + +/* + * get_event_trigger_oid - Look up an event trigger by name to find its OID. + * + * If missing_ok is false, throw an error if trigger not found. If + * true, just return InvalidOid. + */ +Oid +get_event_trigger_oid(const char *trigname, bool missing_ok) +{ + 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; +} diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 9bac99452d3..9d9de7c4251 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -3466,6 +3466,30 @@ _copyCreateTrigStmt(const CreateTrigStmt *from) return newnode; } +static CreateEventTrigStmt * +_copyCreateEventTrigStmt(const CreateEventTrigStmt *from) +{ + CreateEventTrigStmt *newnode = makeNode(CreateEventTrigStmt); + + COPY_STRING_FIELD(trigname); + COPY_SCALAR_FIELD(eventname); + COPY_NODE_FIELD(whenclause); + COPY_NODE_FIELD(funcname); + + return newnode; +} + +static AlterEventTrigStmt * +_copyAlterEventTrigStmt(const AlterEventTrigStmt *from) +{ + AlterEventTrigStmt *newnode = makeNode(AlterEventTrigStmt); + + COPY_STRING_FIELD(trigname); + COPY_SCALAR_FIELD(tgenabled); + + return newnode; +} + static CreatePLangStmt * _copyCreatePLangStmt(const CreatePLangStmt *from) { @@ -4317,6 +4341,12 @@ copyObject(const void *from) case T_CreateTrigStmt: retval = _copyCreateTrigStmt(from); break; + case T_CreateEventTrigStmt: + retval = _copyCreateEventTrigStmt(from); + break; + case T_AlterEventTrigStmt: + retval = _copyAlterEventTrigStmt(from); + break; case T_CreatePLangStmt: retval = _copyCreatePLangStmt(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 2171d8d018b..6d4030a3224 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -1793,6 +1793,26 @@ _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b) } static bool +_equalCreateEventTrigStmt(const CreateEventTrigStmt *a, const CreateEventTrigStmt *b) +{ + COMPARE_STRING_FIELD(trigname); + COMPARE_SCALAR_FIELD(eventname); + COMPARE_NODE_FIELD(funcname); + COMPARE_NODE_FIELD(whenclause); + + return true; +} + +static bool +_equalAlterEventTrigStmt(const AlterEventTrigStmt *a, const AlterEventTrigStmt *b) +{ + COMPARE_STRING_FIELD(trigname); + COMPARE_SCALAR_FIELD(tgenabled); + + return true; +} + +static bool _equalCreatePLangStmt(const CreatePLangStmt *a, const CreatePLangStmt *b) { COMPARE_SCALAR_FIELD(replace); @@ -2872,6 +2892,12 @@ equal(const void *a, const void *b) case T_CreateTrigStmt: retval = _equalCreateTrigStmt(a, b); break; + case T_CreateEventTrigStmt: + retval = _equalCreateEventTrigStmt(a, b); + break; + case T_AlterEventTrigStmt: + retval = _equalAlterEventTrigStmt(a, b); + break; case T_CreatePLangStmt: retval = _equalCreatePLangStmt(a, b); break; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1a17337a7ec..777da1139cd 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -55,6 +55,7 @@ #include "catalog/namespace.h" #include "catalog/pg_trigger.h" #include "commands/defrem.h" +#include "commands/trigger.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "parser/gramparse.h" @@ -194,6 +195,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType, } %type <node> stmt schema_stmt + AlterEventTrigStmt AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt AlterFdwStmt AlterForeignServerStmt AlterGroupStmt AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt @@ -207,7 +209,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType, CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt - CreateAssertStmt CreateTrigStmt + CreateAssertStmt CreateTrigStmt CreateEventTrigStmt CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt @@ -268,6 +270,10 @@ static void processCASbits(int cas_bits, int location, const char *constrType, %type <value> TriggerFuncArg %type <node> TriggerWhen +%type <list> event_trigger_when_list event_trigger_value_list +%type <defelt> event_trigger_when_item +%type <chr> enable_trigger + %type <str> copy_file_name database_name access_method_clause access_method attr_name name cursor_name file_name @@ -505,7 +511,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType, DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DESC DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP - EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EXCEPT + EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTENSION EXTERNAL EXTRACT @@ -674,7 +680,8 @@ stmtmulti: stmtmulti ';' stmt ; stmt : - AlterDatabaseStmt + AlterEventTrigStmt + | AlterDatabaseStmt | AlterDatabaseSetStmt | AlterDefaultPrivilegesStmt | AlterDomainStmt @@ -725,6 +732,7 @@ stmt : | CreateStmt | CreateTableSpaceStmt | CreateTrigStmt + | CreateEventTrigStmt | CreateRoleStmt | CreateUserStmt | CreateUserMappingStmt @@ -3554,6 +3562,15 @@ AlterExtensionContentsStmt: n->objname = list_make1(makeString($6)); $$ = (Node *)n; } + | ALTER EXTENSION name add_drop EVENT TRIGGER name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_EVENT_TRIGGER; + n->objname = list_make1(makeString($7)); + $$ = (Node *)n; + } | ALTER EXTENSION name add_drop TABLE any_name { AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); @@ -4285,6 +4302,75 @@ DropTrigStmt: /***************************************************************************** * * QUERIES : + * CREATE EVENT TRIGGER ... + * DROP EVENT TRIGGER ... + * ALTER EVENT TRIGGER ... + * + *****************************************************************************/ + +CreateEventTrigStmt: + CREATE EVENT TRIGGER name ON ColLabel + EXECUTE PROCEDURE func_name '(' ')' + { + CreateEventTrigStmt *n = makeNode(CreateEventTrigStmt); + n->trigname = $4; + n->eventname = $6; + n->whenclause = NULL; + n->funcname = $9; + $$ = (Node *)n; + } + | CREATE EVENT TRIGGER name ON ColLabel + WHEN event_trigger_when_list + EXECUTE PROCEDURE func_name '(' ')' + { + CreateEventTrigStmt *n = makeNode(CreateEventTrigStmt); + n->trigname = $4; + n->eventname = $6; + n->whenclause = $8; + n->funcname = $11; + $$ = (Node *)n; + } + ; + +event_trigger_when_list: + event_trigger_when_item + { $$ = list_make1($1); } + | event_trigger_when_list AND event_trigger_when_item + { $$ = lappend($1, $3); } + ; + +event_trigger_when_item: + ColId IN_P '(' event_trigger_value_list ')' + { $$ = makeDefElem($1, (Node *) $4); } + ; + +event_trigger_value_list: + SCONST + { $$ = list_make1(makeString($1)); } + | event_trigger_value_list ',' SCONST + { $$ = lappend($1, makeString($3)); } + ; + +AlterEventTrigStmt: + ALTER EVENT TRIGGER name enable_trigger + { + AlterEventTrigStmt *n = makeNode(AlterEventTrigStmt); + n->trigname = $4; + n->tgenabled = $5; + $$ = (Node *) n; + } + ; + +enable_trigger: + ENABLE_P { $$ = TRIGGER_FIRES_ON_ORIGIN; } + | ENABLE_P REPLICA { $$ = TRIGGER_FIRES_ON_REPLICA; } + | ENABLE_P ALWAYS { $$ = TRIGGER_FIRES_ALWAYS; } + | DISABLE_P { $$ = TRIGGER_DISABLED; } + ; + +/***************************************************************************** + * + * QUERIES : * CREATE ASSERTION ... * DROP ASSERTION ... * @@ -4868,6 +4954,7 @@ drop_type: TABLE { $$ = OBJECT_TABLE; } | VIEW { $$ = OBJECT_VIEW; } | INDEX { $$ = OBJECT_INDEX; } | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } + | EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; } | TYPE_P { $$ = OBJECT_TYPE; } | DOMAIN_P { $$ = OBJECT_DOMAIN; } | COLLATION { $$ = OBJECT_COLLATION; } @@ -4931,7 +5018,7 @@ opt_restart_seqs: * EXTENSION | ROLE | TEXT SEARCH PARSER | * TEXT SEARCH DICTIONARY | TEXT SEARCH TEMPLATE | * TEXT SEARCH CONFIGURATION | FOREIGN TABLE | - * FOREIGN DATA WRAPPER | SERVER ] <objname> | + * FOREIGN DATA WRAPPER | SERVER | EVENT TRIGGER ] <objname> | * AGGREGATE <aggname> (arg1, ...) | * FUNCTION <funcname> (arg1, arg2, ...) | * OPERATOR <op> (leftoperand_typ, rightoperand_typ) | @@ -5113,6 +5200,7 @@ comment_type: | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } | SERVER { $$ = OBJECT_FOREIGN_SERVER; } | FOREIGN DATA_P WRAPPER { $$ = OBJECT_FDW; } + | EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; } ; comment_text: @@ -5195,6 +5283,7 @@ opt_provider: FOR ColId_or_Sconst { $$ = $2; } security_label_type: COLUMN { $$ = OBJECT_COLUMN; } | DATABASE { $$ = OBJECT_DATABASE; } + | EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; } | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } | SCHEMA { $$ = OBJECT_SCHEMA; } | SEQUENCE { $$ = OBJECT_SEQUENCE; } @@ -6850,6 +6939,14 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name n->missing_ok = false; $$ = (Node *)n; } + | ALTER EVENT TRIGGER name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_EVENT_TRIGGER; + n->subname = $4; + n->newname = $7; + $$ = (Node *)n; + } | ALTER ROLE RoleId RENAME TO RoleId { RenameStmt *n = makeNode(RenameStmt); @@ -7329,6 +7426,14 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId n->newowner = $6; $$ = (Node *)n; } + | ALTER EVENT TRIGGER name OWNER TO RoleId + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_EVENT_TRIGGER; + n->object = list_make1(makeString($4)); + n->newowner = $7; + $$ = (Node *)n; + } ; diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 7f36a09f46d..4438a3daf8a 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -33,6 +33,7 @@ #include "commands/dbcommands.h" #include "commands/defrem.h" #include "commands/discard.h" +#include "commands/event_trigger.h" #include "commands/explain.h" #include "commands/extension.h" #include "commands/lockcmds.h" @@ -183,6 +184,8 @@ check_xact_readonly(Node *parsetree) case T_CommentStmt: case T_DefineStmt: case T_CreateCastStmt: + case T_CreateEventTrigStmt: + case T_AlterEventTrigStmt: case T_CreateConversionStmt: case T_CreatedbStmt: case T_CreateDomainStmt: @@ -1056,6 +1059,14 @@ standard_ProcessUtility(Node *parsetree, InvalidOid, InvalidOid, false); break; + case T_CreateEventTrigStmt: + CreateEventTrigger((CreateEventTrigStmt *) parsetree); + break; + + case T_AlterEventTrigStmt: + AlterEventTrigger((AlterEventTrigStmt *) parsetree); + break; + case T_CreatePLangStmt: CreateProceduralLanguage((CreatePLangStmt *) parsetree); break; @@ -1472,6 +1483,9 @@ AlterObjectTypeCommandTag(ObjectType objtype) case OBJECT_TRIGGER: tag = "ALTER TRIGGER"; break; + case OBJECT_EVENT_TRIGGER: + tag = "ALTER EVENT TRIGGER"; + break; case OBJECT_TSCONFIGURATION: tag = "ALTER TEXT SEARCH CONFIGURATION"; break; @@ -1741,6 +1755,9 @@ CreateCommandTag(Node *parsetree) case OBJECT_TRIGGER: tag = "DROP TRIGGER"; break; + case OBJECT_EVENT_TRIGGER: + tag = "DROP EVENT TRIGGER"; + break; case OBJECT_RULE: tag = "DROP RULE"; break; @@ -1994,6 +2011,14 @@ CreateCommandTag(Node *parsetree) tag = "CREATE TRIGGER"; break; + case T_CreateEventTrigStmt: + tag = "CREATE EVENT TRIGGER"; + break; + + case T_AlterEventTrigStmt: + tag = "ALTER EVENT TRIGGER"; + break; + case T_CreatePLangStmt: tag = "CREATE LANGUAGE"; break; @@ -2489,6 +2514,14 @@ GetCommandLogLevel(Node *parsetree) lev = LOGSTMT_DDL; break; + case T_CreateEventTrigStmt: + lev = LOGSTMT_DDL; + break; + + case T_AlterEventTrigStmt: + lev = LOGSTMT_DDL; + break; + case T_CreatePLangStmt: lev = LOGSTMT_DDL; break; diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c index d7770b829aa..8590f3caa57 100644 --- a/src/backend/utils/adt/pseudotypes.c +++ b/src/backend/utils/adt/pseudotypes.c @@ -293,6 +293,33 @@ trigger_out(PG_FUNCTION_ARGS) /* + * event_trigger_in - input routine for pseudo-type event_trigger. + */ +Datum +event_trigger_in(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type event_trigger"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +/* + * event_trigger_out - output routine for pseudo-type event_trigger. + */ +Datum +event_trigger_out(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot display a value of type event_trigger"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + + +/* * language_handler_in - input routine for pseudo-type LANGUAGE_HANDLER. */ Datum diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c index c365ec7597a..bb754e3d03b 100644 --- a/src/backend/utils/cache/syscache.c +++ b/src/backend/utils/cache/syscache.c @@ -34,6 +34,7 @@ #include "catalog/pg_database.h" #include "catalog/pg_default_acl.h" #include "catalog/pg_enum.h" +#include "catalog/pg_event_trigger.h" #include "catalog/pg_foreign_data_wrapper.h" #include "catalog/pg_foreign_server.h" #include "catalog/pg_foreign_table.h" @@ -379,6 +380,28 @@ static const struct cachedesc cacheinfo[] = { }, 256 }, + {EventTriggerRelationId, /* EVENTTRIGGERNAME */ + EventTriggerNameIndexId, + 1, + { + Anum_pg_event_trigger_evtname, + 0, + 0, + 0 + }, + 8 + }, + {EventTriggerRelationId, /* EVENTTRIGGEROID */ + EventTriggerOidIndexId, + 1, + { + ObjectIdAttributeNumber, + 0, + 0, + 0 + }, + 8 + }, {ForeignDataWrapperRelationId, /* FOREIGNDATAWRAPPERNAME */ ForeignDataWrapperNameIndexId, 1, |