aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorRobert Haas <rhaas@postgresql.org>2012-07-18 10:16:16 -0400
committerRobert Haas <rhaas@postgresql.org>2012-07-18 10:16:16 -0400
commit3855968f328918b6cd1401dd11d109d471a54d40 (patch)
tree37cf34023851c0c3d14f30b4833937cfaeb2fa34 /src/backend
parentfaf26bf1175530cc97ce3e804ff10dc2be7026d3 (diff)
downloadpostgresql-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/Makefile2
-rw-r--r--src/backend/catalog/aclchk.c40
-rw-r--r--src/backend/catalog/dependency.c27
-rw-r--r--src/backend/catalog/objectaddress.c22
-rw-r--r--src/backend/catalog/pg_shdepend.c6
-rw-r--r--src/backend/catalog/system_views.sql13
-rw-r--r--src/backend/commands/Makefile4
-rw-r--r--src/backend/commands/alter.c9
-rw-r--r--src/backend/commands/dropcmds.c4
-rw-r--r--src/backend/commands/event_trigger.c539
-rw-r--r--src/backend/nodes/copyfuncs.c30
-rw-r--r--src/backend/nodes/equalfuncs.c26
-rw-r--r--src/backend/parser/gram.y113
-rw-r--r--src/backend/tcop/utility.c33
-rw-r--r--src/backend/utils/adt/pseudotypes.c27
-rw-r--r--src/backend/utils/cache/syscache.c23
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,