@@ -1891,23 +1892,28 @@
evtnamename
- Trigger name (unique)
+ Trigger name (must be unique)evteventname
- The event this trigger fires for.
+ Identifies the event for which this trigger fires
+
+
+
+ evtowner
+ oid
+ pg_authid.oid
+ Owner of the event triggerevtfoidoidpg_proc.oid
-
- The OID of the function called by this event trigger.
-
+ The function to be called
@@ -1928,7 +1934,10 @@
evttagstext[]
- Command tags of the commands this trigger is restricted to.
+
+ Command tags for which this trigger will fire. If NULL, the firing
+ of this trigger is not restricted on the basis of the command tag.
+
diff --git a/doc/src/sgml/event-trigger.sgml b/doc/src/sgml/event-trigger.sgml
new file mode 100644
index 0000000..52e9764
--- /dev/null
+++ b/doc/src/sgml/event-trigger.sgml
@@ -0,0 +1,466 @@
+
+
+
+ Event Triggers
+
+
+ event trigger
+
+
+
+ To supplement the trigger mechanism discussed in ,
+ PostgreSQL> also provides event triggers. Unlike regular
+ triggers, which are attached to a single table and capture only DML events,
+ event triggers are global to a particular database and are capable of
+ capturing DDL events.
+
+
+
+ Like regular triggers, event triggers can be written in any procedural
+ language that includes event trigger support, or in C, but not in plain
+ SQL.
+
+
+
+ Overview of Event Trigger Behavior
+
+
+ An event trigger fires whenever the event with which it is
+ associated occurs in the database in which it is defined. Currently,
+ the only supported event is command_start>. Support for
+ additional events may be added in future releases.
+
+
+
+ The command_start> event occurs just before the execution of
+ many SQL commands, most of which can be broadly described as
+ DDL. There are a number of commands that,
+ for various reasons, are specifically excluded from the event trigger
+ system:
+
+
+
+
+ The command_start> event does not occur for commands that
+ access or modify data within a particular table, such
+ , ,
+ , , and
+ . This also includes commands related
+ to prepared plans, such as ,
+ , ,
+ and . Ordinary triggers or rules should
+ be used in these cases.
+
+
+
+
+ The command_start> event does not occur for commands that
+ create, alter, or drop global objects. Unlike the event trigger, these
+ objects are not part of the current database; they are accessible from
+ all databases in the cluster, and are therefore unaffected by event
+ triggers. Such objects include databases, tablespaces, and roles.
+
+
+
+
+ The command_start> event does not occur for commands where
+ firing a trigger might result either result in system instability or
+ interfere with the administrator's ability to regain control of the
+ database. These include transaction control commands, such as
+ or ; configuration
+ commands, such as or ;
+ and commands related to the event trigger mechanism itself, such as
+ ,
+ , and
+ .
+
+
+
+
+
+
+ For a complete list of commands supported by the event trigger mechanism,
+ see .
+
+
+
+ In order to create an event trigger, you must first create a function with
+ the special return type event_trigger. This function
+ need not (and may not) return a value; the return type serves merely as
+ a signal that the function is to be invoked as an event trigger.
+
+
+
+ If more than one event trigger is defined for a particular event, they will
+ fire in alphabetical order by trigger name.
+
+
+
+ A trigger definition can also specify a WHEN
+ condition so that, for example, a command_start
+ trigger can be fired only for particular commands which the user wishes
+ to intercept. A common use of such triggers is to restrict the range of
+ DDL operations which users may perform.
+
+
+
+
+ Event Trigger Firing Matrix
+
+
+ lists all commands
+ for which event triggers are supported.
+
+
+
+ Event Trigger Support by Command Tag
+
+
+
+ command tag
+ command_start
+
+
+
+
+ ALTER AGGREGATE
+ X
+
+
+ ALTER COLLATION
+ X
+
+
+ ALTER CONVERSION
+ X
+
+
+ ALTER DOMAIN
+ X
+
+
+ ALTER EXTENSION
+ X
+
+
+ ALTER FOREIGN DATA WRAPPER
+ X
+
+
+ ALTER FOREIGN TABLE
+ X
+
+
+ ALTER FUNCTION
+ X
+
+
+ ALTER LANGUAGE
+ X
+
+
+ ALTER OPERATOR
+ X
+
+
+ ALTER OPERATOR CLASS
+ X
+
+
+ ALTER OPERATOR FAMILY
+ X
+
+
+ ALTER SCHEMA
+ X
+
+
+ ALTER SEQUENCE
+ X
+
+
+ ALTER SERVER
+ X
+
+
+ ALTER TABLE
+ X
+
+
+ ALTER TEXT SEARCH CONFIGURATION
+ X
+
+
+ ALTER TEXT SEARCH DICTIONARY
+ X
+
+
+ ALTER TEXT SEARCH PARSER
+ X
+
+
+ ALTER TEXT SEARCH TEMPLATE
+ X
+
+
+ ALTER TRIGGER
+ X
+
+
+ ALTER TYPE
+ X
+
+
+ ALTER USER MAPPING
+ X
+
+
+ ALTER VIEW
+ X
+
+
+ CLUSTER
+ X
+
+
+ CREATE AGGREGATE
+ X
+
+
+ CREATE CAST
+ X
+
+
+ CREATE COLLATION
+ X
+
+
+ CREATE CONVERSION
+ X
+
+
+ CREATE DOMAIN
+ X
+
+
+ CREATE EXTENSION
+ X
+
+
+ CREATE FOREIGN DATA WRAPPER
+ X
+
+
+ CREATE FOREIGN TABLE
+ X
+
+
+ CREATE FUNCTION
+ X
+
+
+ CREATE INDEX
+ X
+
+
+ CREATE LANGUAGE
+ X
+
+
+ CREATE OPERATOR
+ X
+
+
+ CREATE OPERATOR CLASS
+ X
+
+
+ CREATE OPERATOR FAMILY
+ X
+
+
+ CREATE RULE
+ X
+
+
+ CREATE SCHEMA
+ X
+
+
+ CREATE SEQUENCE
+ X
+
+
+ CREATE SERVER
+ X
+
+
+ CREATE TABLE
+ X
+
+
+ CREATE TABLE AS
+ X
+
+
+ CREATE TEXT SEARCH CONFIGURATION
+ X
+
+
+ CREATE TEXT SEARCH DICTIONARY
+ X
+
+
+ CREATE TEXT SEARCH PARSER
+ X
+
+
+ CREATE TEXT SEARCH TEMPLATE
+ X
+
+
+ CREATE TRIGGER
+ X
+
+
+ CREATE TYPE
+ X
+
+
+ CREATE USER MAPPING
+ X
+
+
+ CREATE VIEW
+ X
+
+
+ DROP AGGREGATE
+ X
+
+
+ DROP CAST
+ X
+
+
+ DROP COLLATION
+ X
+
+
+ DROP CONVERSION
+ X
+
+
+ DROP DOMAIN
+ X
+
+
+ DROP EXTENSION
+ X
+
+
+ DROP FOREIGN DATA WRAPPER
+ X
+
+
+ DROP FOREIGN TABLE
+ X
+
+
+ DROP FUNCTION
+ X
+
+
+ DROP INDEX
+ X
+
+
+ DROP LANGUAGE
+ X
+
+
+ DROP OPERATOR
+ X
+
+
+ DROP OPERATOR CLASS
+ X
+
+
+ DROP OPERATOR FAMILY
+ X
+
+
+ DROP RULE
+ X
+
+
+ DROP SCHEMA
+ X
+
+
+ DROP SEQUENCE
+ X
+
+
+ DROP SERVER
+ X
+
+
+ DROP TABLE
+ X
+
+
+ DROP TEXT SEARCH CONFIGURATION
+ X
+
+
+ DROP TEXT SEARCH DICTIONARY
+ X
+
+
+ DROP TEXT SEARCH PARSER
+ X
+
+
+ DROP TEXT SEARCH TEMPLATE
+ X
+
+
+ DROP TRIGGER
+ X
+
+
+ DROP TYPE
+ X
+
+
+ DROP USER MAPPING
+ X
+
+
+ DROP VIEW
+ X
+
+
+ LOAD
+ X
+
+
+ REINDEX
+ X
+
+
+ SELECT INTO
+ X
+
+
+ VACUUM
+ X
+
+
+
+
+
+
+
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 82b9e39..db4cc3a 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -61,6 +61,7 @@
+
diff --git a/doc/src/sgml/plperl.sgml b/doc/src/sgml/plperl.sgml
index b783e86..e490086 100644
--- a/doc/src/sgml/plperl.sgml
+++ b/doc/src/sgml/plperl.sgml
@@ -634,7 +634,7 @@ SELECT init_hosts_query();
SELECT query_hosts('192.168.1.0/30');
SELECT release_hosts_query();
- query_hosts
+ query_hosts
-----------------
(1,192.168.1.1)
(2,192.168.1.2)
@@ -1220,7 +1220,7 @@ CREATE TRIGGER test_valid_id_trig
- Trigger Procedures on Events in PL/Perl
+ Trigger Procedures on Events in PL/Perlevent trigger
@@ -1229,9 +1229,9 @@ CREATE TRIGGER test_valid_id_trig
Event trigger procedures can be written in PL/Perl.
- PostgreSQL requires that a procedure that is to be called
- as a trigger must be declared as a function with no arguments
- and a return type of event_trigger>.
+ PostgreSQL requires that a procedure that
+ is to be called as an event trigger must be declared as a function with
+ no arguments and a return type of event_trigger>.
@@ -1272,7 +1272,7 @@ CREATE TRIGGER test_valid_id_trig
$_TD->{objectname}
- The name of the objectthat caused the trigger procedure
+ The name of the object that caused the trigger procedure
to be invoked.
@@ -1289,7 +1289,7 @@ CREATE TRIGGER test_valid_id_trig
-
+
diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
index fadd5d7..a21aaf2 100644
--- a/doc/src/sgml/plpgsql.sgml
+++ b/doc/src/sgml/plpgsql.sgml
@@ -3378,7 +3378,7 @@ RAISE unique_violation USING MESSAGE = 'Duplicate user ID: ' || user_id;
- Triggers on data change
+ Triggers on data changesPL/pgSQL can be used to define trigger
@@ -3930,19 +3930,19 @@ SELECT * FROM sales_summary_bytime;
- Triggers on event
+ Triggers on events
- PL/pgSQL can be used to define command
- trigger procedures. An event trigger procedure is created with the
- CREATE FUNCTION> command, declaring it as a function with
- no arguments and a return type of event trigger.
+ PL/pgSQL can be used to define event
+ triggers. PostgreSQL> requires that a procedure that
+ is to be called as an event trigger must be declared as a function with
+ no arguments and a return type of event_trigger>.
-
- When a PL/pgSQL function is called as a
- event trigger, several special variables are created automatically
- in the top-level block. They are:
+
+ When a PL/pgSQL function is called as a
+ event trigger, several special variables are created automatically
+ in the top-level block. They are:
@@ -3999,10 +3999,6 @@ SELECT * FROM sales_summary_bytime;
-
- The event trigger function's return value is not used.
-
-
shows an example of a
event trigger procedure in PL/pgSQL.
diff --git a/doc/src/sgml/plpython.sgml b/doc/src/sgml/plpython.sgml
index 07f017a..99ce8f9 100644
--- a/doc/src/sgml/plpython.sgml
+++ b/doc/src/sgml/plpython.sgml
@@ -441,7 +441,7 @@ return (1, 2, 3, 4, 5)
$$ LANGUAGE plpythonu;
SELECT return_arr();
- return_arr
+ return_arr
-------------
{1,2,3,4,5}
(1 row)
@@ -872,7 +872,7 @@ $$ LANGUAGE plpythonu;
- Trigger Procedures on Events in PL/Python
+ Trigger Procedures on Events in PL/Pythonevent trigger
@@ -881,9 +881,9 @@ $$ LANGUAGE plpythonu;
Event trigger procedures can be written in PL/Python.
- PostgreSQL requires that a procedure that is to be called
- as a trigger must be declared as a function with no arguments
- and a return type of event_trigger>.
+ PostgreSQL requires that a procedure that
+ is to be called as an event trigger must be declared as a function with
+ no arguments and a return type of event_trigger>.
@@ -924,7 +924,7 @@ $$ LANGUAGE plpythonu;
TD["objectname"]
- The name of the objectthat caused the trigger procedure
+ The name of the object that caused the trigger procedure
to be invoked.
diff --git a/doc/src/sgml/pltcl.sgml b/doc/src/sgml/pltcl.sgml
index c9a4e4c..c932ddb 100644
--- a/doc/src/sgml/pltcl.sgml
+++ b/doc/src/sgml/pltcl.sgml
@@ -725,9 +725,9 @@ CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab
Event trigger procedures can be written in PL/Tcl.
- PostgreSQL requires that a procedure that is to be called
- as a trigger must be declared as a function with no arguments
- and a return type of event_trigger>.
+ PostgreSQL requires that a procedure that
+ is to be called as an event trigger must be declared as a function with
+ no arguments and a return type of event_trigger>.
@@ -768,7 +768,7 @@ CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab
$TG_objectname
- The name of the objectthat caused the trigger procedure
+ The name of the object that caused the trigger procedure
to be invoked.
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 7e80265..4ef1fc1 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -208,6 +208,7 @@
&extend;
&trigger;
+ &event-trigger;
&rules;
&xplang;
diff --git a/doc/src/sgml/ref/alter_event_trigger.sgml b/doc/src/sgml/ref/alter_event_trigger.sgml
index e236921..db4727b 100644
--- a/doc/src/sgml/ref/alter_event_trigger.sgml
+++ b/doc/src/sgml/ref/alter_event_trigger.sgml
@@ -12,7 +12,7 @@ PostgreSQL documentation
ALTER EVENT TRIGGER
- change the definition of a trigger
+ change the definition of an event trigger
@@ -46,7 +46,7 @@ ALTER EVENT TRIGGER name RENAME TO
- You must be superuser to alter a event trigger.
+ You must be superuser to alter an event trigger.
@@ -93,12 +93,12 @@ ALTER EVENT TRIGGER name RENAME TO
-
+ Compatibility
- ALTER EVENT TRIGGER is a PostgreSQL>
- extension of the SQL standard.
+ There is no ALTER EVENT TRIGGER statement in the
+ SQL standard.
diff --git a/doc/src/sgml/ref/create_event_trigger.sgml b/doc/src/sgml/ref/create_event_trigger.sgml
index af06c88..3b3d474 100644
--- a/doc/src/sgml/ref/create_event_trigger.sgml
+++ b/doc/src/sgml/ref/create_event_trigger.sgml
@@ -12,7 +12,7 @@ PostgreSQL documentation
CREATE EVENT TRIGGER
- define a new trigger
+ define a new event trigger
@@ -23,100 +23,8 @@ PostgreSQL documentation
CREATE EVENT TRIGGER name
ON event
- [ WHEN variable IN (tag [, tag [, ...] ] ) ]
- EXECUTE PROCEDURE function_name ()
-
-where event can be one of:
-
- command_start
-
-where tag can be one of:
-
- ALTER AGGREGATE
- ALTER COLLATION
- ALTER CONVERSION
- ALTER DOMAIN
- ALTER EXTENSION
- ALTER FOREIGN DATA WRAPPER
- ALTER FOREIGN TABLE
- ALTER FUNCTION
- ALTER LANGUAGE
- ALTER OPERATOR
- ALTER OPERATOR CLASS
- ALTER OPERATOR FAMILY
- ALTER SCHEMA
- ALTER SEQUENCE
- ALTER SERVER
- ALTER TABLE
- ALTER TEXT SEARCH CONFIGURATION
- ALTER TEXT SEARCH DICTIONARY
- ALTER TEXT SEARCH PARSER
- ALTER TEXT SEARCH TEMPLATE
- ALTER TRIGGER
- ALTER TYPE
- ALTER USER MAPPING
- ALTER VIEW
- CLUSTER
- CREATE AGGREGATE
- CREATE CAST
- CREATE COLLATION
- CREATE CONVERSION
- CREATE DOMAIN
- CREATE EXTENSION
- CREATE FOREIGN DATA WRAPPER
- CREATE FOREIGN TABLE
- CREATE FUNCTION
- CREATE INDEX
- CREATE LANGUAGE
- CREATE OPERATOR
- CREATE OPERATOR CLASS
- CREATE OPERATOR FAMILY
- CREATE RULE
- CREATE SCHEMA
- CREATE SEQUENCE
- CREATE SERVER
- CREATE TABLE
- CREATE TABLE AS
- CREATE TEXT SEARCH CONFIGURATION
- CREATE TEXT SEARCH DICTIONARY
- CREATE TEXT SEARCH PARSER
- CREATE TEXT SEARCH TEMPLATE
- CREATE TRIGGER
- CREATE TYPE
- CREATE USER MAPPING
- CREATE VIEW
- DROP AGGREGATE
- DROP CAST
- DROP COLLATION
- DROP CONVERSION
- DROP DOMAIN
- DROP EXTENSION
- DROP FOREIGN DATA WRAPPER
- DROP FOREIGN TABLE
- DROP FUNCTION
- DROP INDEX
- DROP LANGUAGE
- DROP OPERATOR
- DROP OPERATOR CLASS
- DROP OPERATOR FAMILY
- DROP RULE
- DROP SCHEMA
- DROP SEQUENCE
- DROP SERVER
- DROP TABLE
- DROP TEXT SEARCH CONFIGURATION
- DROP TEXT SEARCH DICTIONARY
- DROP TEXT SEARCH PARSER
- DROP TEXT SEARCH TEMPLATE
- DROP TRIGGER
- DROP TYPE
- DROP USER MAPPING
- DROP VIEW
- LOAD
- REINDEX
- SELECT INTO
- VACUUM
-
+ [ WHEN filter_variable IN (filter_value [ , ... ] ) ]
+ EXECUTE PROCEDURE function_name()
@@ -125,35 +33,11 @@ CREATE EVENT TRIGGER nameCREATE EVENT TRIGGER creates a new event trigger.
- The trigger will be associated with the specified event and will
- execute the specified
- function function_name when
- that event is run.
-
-
-
- The command trigger gives a procedure to fire before the event is
- executed. In some cases the procedure can be fired instead of the event
- code PostgreSQL would run itself. A command trigger's function must
- return event_trigger data type. It can then only
- abort the execution of the command by raising an exception.
-
-
-
- If multiple triggers of the same kind are defined for the same event,
- they will be fired in alphabetical order by name.
-
-
-
- Note that objects dropped by the effect of DROP
- CASCADE will not result in a event trigger firing, only the
- top-level event for the main object will fire a event trigger. That
- also applies to other dependencies following, as in DROP OWNED
- BY.
-
-
-
- Refer to for more information about triggers.
+ Whenever the designated event occurs and the WHEN> condition
+ associated with the trigger, if any, is satisfied, the trigger function
+ will be executed. For a general introduction to event triggers, see
+ . The user who creates an event trigger
+ becomes its owner.
@@ -165,52 +49,44 @@ CREATE EVENT TRIGGER namename
- The name to give the new trigger. This must be distinct from the name
- of any other trigger for the same table. The name cannot be
- schema-qualified.
+ The name to give the new trigger. This name must be unique within
+ the database.
- tag
+ event
- The tag of the command the trigger is for. Supported commands are
- mainly those acting on database objects, plus some more facilities.
- That leaves out the following list of non supported commands.
-
-
- Commands that refer to global objects, such as databases, tablespaces
- and roles, are not supported.
-
-
- The command ALTER TYPE ... ADD VALUE ... prevents
- transaction control entirely, thus no command trigger will get fired
- when it's used.
-
-
- Commands that are related to transaction control (such
- as BEGIN or COMMIT), related to
- prepared plans
- (e.g. PREPARE, DEALLOCATE),
- cursors management
- (e.g. DECLARE, FETCH), setting
- variables (SET), the LISTEN
- feature, and security are not supported either.
+ The name of the event that triggers a call to the given function.
+ See for more information
+ on event names.
+
+
+
+
+ filter_variable
+
- Event triggers on CREATE EVENT
- TRIGGER, ALTER EVENT TRIGGER
- and DROP EVENT TRIGGER are not supported so as
- not to be able to take over control from a superuser.
+ The name of a variable used to filter events. This makes it possible
+ to restrict the firing of the trigger to a subset of the cases in which
+ it is supported. Currently the only suppoted
+ filter_variable
+ is TAG.
+
+
+
+
+ filter_value
+
- Triggers on ANY command support more commands than
- just this list, and will only provide the command
- tag argument as NOT NULL. Supporting more
- commands is made so that you can actually block
- commands in one go.
+ A list of values for the
+ associated filter_variable
+ for which the trigger should fire. For TAG>, this means a
+ list of command tags (e.g. 'DROP FUNCTION'>).
@@ -220,7 +96,7 @@ CREATE EVENT TRIGGER name
A user-supplied function that is declared as taking no argument and
- returning type event trigger.
+ returning type event_trigger.
If your event trigger is implemented in C then it
@@ -234,24 +110,20 @@ CREATE EVENT TRIGGER name
-
+ Notes
To create a trigger on a event, the user must be superuser.
-
-
- Use to remove a event trigger.
-
-
+ Examples
- Forbids the execution of any command supported by the event trigger
- mechanism, which includes all commands listed above:
+ Forbid the execution of any command supported by the event trigger
+ mechanism:
CREATE OR REPLACE FUNCTION abort_any_command()
@@ -291,13 +163,12 @@ $$;
-
+ Compatibility
- CREATE EVENT TRIGGER is a
- PostgreSQL extension of the SQL>
- standard.
+ There is no CREATE EVENT TRIGGER statement in the
+ SQL standard.
diff --git a/doc/src/sgml/ref/drop_event_trigger.sgml b/doc/src/sgml/ref/drop_event_trigger.sgml
index fc45dff..86f9628 100644
--- a/doc/src/sgml/ref/drop_event_trigger.sgml
+++ b/doc/src/sgml/ref/drop_event_trigger.sgml
@@ -12,7 +12,7 @@ PostgreSQL documentation
DROP EVENT TRIGGER
- remove a event trigger
+ remove an event trigger
@@ -29,8 +29,9 @@ DROP EVENT TRIGGER [ IF EXISTS ] nameDescription
- DROP EVENT TRIGGER removes an existing trigger definition.
- To execute this command, the current user must be superuser.
+ DROP EVENT TRIGGER removes an existing event trigger.
+ To execute this command, the current user must be the owner of the event
+ trigger.
@@ -79,7 +80,7 @@ DROP EVENT TRIGGER [ IF EXISTS ] name
-
+ Examples
@@ -90,13 +91,14 @@ DROP EVENT TRIGGER snitch;
-
+ Compatibility
- The DROP EVENT TRIGGER statement is a
- PostgreSQL extension.
+ There is no DROP EVENT TRIGGER statement in the
+ SQL standard.
+
diff --git a/doc/src/sgml/trigger.sgml b/doc/src/sgml/trigger.sgml
index 32994b9..f579340 100644
--- a/doc/src/sgml/trigger.sgml
+++ b/doc/src/sgml/trigger.sgml
@@ -27,24 +27,6 @@
plain SQL function language.
-
- PostgreSQL offers both triggers on commands
- (see ) and triggers on data manipulation
- (see ).
-
-
-
- Overview of Event Trigger Behavior
-
-
- A trigger is a specification that the database should automatically
- execute a particular function whenever a certain command is performed.
- The whole set of PostgreSQL commands is not
- supported for triggers, see
- for details.
-
-
-
Overview of Trigger Behavior
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 6e29758..df6da1f 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -12,8 +12,7 @@ include $(top_builddir)/src/Makefile.global
OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
objectaddress.o pg_aggregate.o pg_collation.o pg_constraint.o pg_conversion.o \
- pg_depend.o pg_enum.o pg_event_trigger.o pg_inherits.o pg_largeobject.o \
- pg_namespace.o \
+ pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \
pg_operator.o pg_proc.o pg_range.o pg_db_role_setting.o pg_shdepend.o \
pg_type.o storage.o toasting.o
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 56c40b1..b097813 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/objectaddress.c b/src/backend/catalog/objectaddress.c
index 40ae60d..5b8140b 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -997,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,
@@ -1075,7 +1080,6 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
break;
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
- case OBJECT_EVENT_TRIGGER:
/* We treat these object types as being owned by superusers */
if (!superuser_arg(roleid))
ereport(ERROR,
diff --git a/src/backend/catalog/pg_event_trigger.c b/src/backend/catalog/pg_event_trigger.c
deleted file mode 100644
index 4995d2b..0000000
--- a/src/backend/catalog/pg_event_trigger.c
+++ /dev/null
@@ -1,409 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * pg_event_trigger.c
- * routines to support manipulation of the pg_event_trigger relation
- *
- * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * src/backend/catalog/pg_event_trigger.c
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "catalog/pg_event_trigger.h"
-#include "utils/builtins.h"
-
-char *
-event_to_string(TrigEvent event)
-{
- switch (event)
- {
- case E_CommandStart:
- return "command_start";
- }
- return NULL;
-}
-
-TrigEvent
-parse_event_name(char *event)
-{
- if (pg_strcasecmp(event, "command_start") == 0)
- return E_CommandStart;
- else
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("unrecognized event \"%s\"", event)));
-
- /* make compiler happy */
- return -1;
-}
-
-TrigEventCommand
-parse_event_tag(char *command, bool noerror)
-{
- if (pg_strcasecmp(command, "ALTER AGGREGATE") == 0)
- return E_AlterAggregate;
- else if (pg_strcasecmp(command, "ALTER COLLATION") == 0)
- return E_AlterCollation;
- else if (pg_strcasecmp(command, "ALTER CONVERSION") == 0)
- return E_AlterConversion;
- else if (pg_strcasecmp(command, "ALTER DOMAIN") == 0)
- return E_AlterDomain;
- else if (pg_strcasecmp(command, "ALTER EXTENSION") == 0)
- return E_AlterExtension;
- else if (pg_strcasecmp(command, "ALTER FOREIGN DATA WRAPPER") == 0)
- return E_AlterForeignDataWrapper;
- else if (pg_strcasecmp(command, "ALTER FOREIGN TABLE") == 0)
- return E_AlterForeignTable;
- else if (pg_strcasecmp(command, "ALTER FUNCTION") == 0)
- return E_AlterFunction;
- else if (pg_strcasecmp(command, "ALTER LANGUAGE") == 0)
- return E_AlterLanguage;
- else if (pg_strcasecmp(command, "ALTER OPERATOR") == 0)
- return E_AlterOperator;
- else if (pg_strcasecmp(command, "ALTER OPERATOR CLASS") == 0)
- return E_AlterOperatorClass;
- else if (pg_strcasecmp(command, "ALTER OPERATOR FAMILY") == 0)
- return E_AlterOperatorFamily;
- else if (pg_strcasecmp(command, "ALTER SEQUENCE") == 0)
- return E_AlterSequence;
- else if (pg_strcasecmp(command, "ALTER SERVER") == 0)
- return E_AlterServer;
- else if (pg_strcasecmp(command, "ALTER SCHEMA") == 0)
- return E_AlterSchema;
- else if (pg_strcasecmp(command, "ALTER TABLE") == 0)
- return E_AlterTable;
- else if (pg_strcasecmp(command, "ALTER TEXT SEARCH CONFIGURATION") == 0)
- return E_AlterTextSearchConfiguration;
- else if (pg_strcasecmp(command, "ALTER TEXT SEARCH DICTIONARY") == 0)
- return E_AlterTextSearchDictionary;
- else if (pg_strcasecmp(command, "ALTER TEXT SEARCH PARSER") == 0)
- return E_AlterTextSearchParser;
- else if (pg_strcasecmp(command, "ALTER TEXT SEARCH TEMPLATE") == 0)
- return E_AlterTextSearchTemplate;
- else if (pg_strcasecmp(command, "ALTER TRIGGER") == 0)
- return E_AlterTrigger;
- else if (pg_strcasecmp(command, "ALTER TYPE") == 0)
- return E_AlterType;
- else if (pg_strcasecmp(command, "ALTER USER MAPPING") == 0)
- return E_AlterUserMapping;
- else if (pg_strcasecmp(command, "ALTER VIEW") == 0)
- return E_AlterView;
- else if (pg_strcasecmp(command, "CLUSTER") == 0)
- return E_Cluster;
- else if (pg_strcasecmp(command, "CREATE AGGREGATE") == 0)
- return E_CreateAggregate;
- else if (pg_strcasecmp(command, "CREATE CAST") == 0)
- return E_CreateCast;
- else if (pg_strcasecmp(command, "CREATE COLLATION") == 0)
- return E_CreateCollation;
- else if (pg_strcasecmp(command, "CREATE CONVERSION") == 0)
- return E_CreateConversion;
- else if (pg_strcasecmp(command, "CREATE DOMAIN") == 0)
- return E_CreateDomain;
- else if (pg_strcasecmp(command, "CREATE EXTENSION") == 0)
- return E_CreateExtension;
- else if (pg_strcasecmp(command, "CREATE FOREIGN DATA WRAPPER") == 0)
- return E_CreateForeignDataWrapper;
- else if (pg_strcasecmp(command, "CREATE FOREIGN TABLE") == 0)
- return E_CreateForeignTable;
- else if (pg_strcasecmp(command, "CREATE FUNCTION") == 0)
- return E_CreateFunction;
- else if (pg_strcasecmp(command, "CREATE INDEX") == 0)
- return E_CreateIndex;
- else if (pg_strcasecmp(command, "CREATE LANGUAGE") == 0)
- return E_CreateLanguage;
- else if (pg_strcasecmp(command, "CREATE OPERATOR") == 0)
- return E_CreateOperator;
- else if (pg_strcasecmp(command, "CREATE OPERATOR CLASS") == 0)
- return E_CreateOperatorClass;
- else if (pg_strcasecmp(command, "CREATE OPERATOR FAMILY") == 0)
- return E_CreateOperatorFamily;
- else if (pg_strcasecmp(command, "CREATE RULE") == 0)
- return E_CreateRule;
- else if (pg_strcasecmp(command, "CREATE SEQUENCE") == 0)
- return E_CreateSequence;
- else if (pg_strcasecmp(command, "CREATE SERVER") == 0)
- return E_CreateServer;
- else if (pg_strcasecmp(command, "CREATE SCHEMA") == 0)
- return E_CreateSchema;
- else if (pg_strcasecmp(command, "CREATE TABLE") == 0)
- return E_CreateTable;
- else if (pg_strcasecmp(command, "CREATE TABLE AS") == 0)
- return E_CreateTableAs;
- else if (pg_strcasecmp(command, "CREATE TEXT SEARCH CONFIGURATION") == 0)
- return E_CreateTextSearchConfiguration;
- else if (pg_strcasecmp(command, "CREATE TEXT SEARCH DICTIONARY") == 0)
- return E_CreateTextSearchDictionary;
- else if (pg_strcasecmp(command, "CREATE TEXT SEARCH PARSER") == 0)
- return E_CreateTextSearchParser;
- else if (pg_strcasecmp(command, "CREATE TEXT SEARCH TEMPLATE") == 0)
- return E_CreateTextSearchTemplate;
- else if (pg_strcasecmp(command, "CREATE TRIGGER") == 0)
- return E_CreateTrigger;
- else if (pg_strcasecmp(command, "CREATE TYPE") == 0)
- return E_CreateType;
- else if (pg_strcasecmp(command, "CREATE USER MAPPING") == 0)
- return E_CreateUserMapping;
- else if (pg_strcasecmp(command, "CREATE VIEW") == 0)
- return E_CreateView;
- else if (pg_strcasecmp(command, "DROP AGGREGATE") == 0)
- return E_DropAggregate;
- else if (pg_strcasecmp(command, "DROP CAST") == 0)
- return E_DropCast;
- else if (pg_strcasecmp(command, "DROP COLLATION") == 0)
- return E_DropCollation;
- else if (pg_strcasecmp(command, "DROP CONVERSION") == 0)
- return E_DropConversion;
- else if (pg_strcasecmp(command, "DROP DOMAIN") == 0)
- return E_DropDomain;
- else if (pg_strcasecmp(command, "DROP EXTENSION") == 0)
- return E_DropExtension;
- else if (pg_strcasecmp(command, "DROP FOREIGN DATA WRAPPER") == 0)
- return E_DropForeignDataWrapper;
- else if (pg_strcasecmp(command, "DROP FOREIGN TABLE") == 0)
- return E_DropForeignTable;
- else if (pg_strcasecmp(command, "DROP FUNCTION") == 0)
- return E_DropFunction;
- else if (pg_strcasecmp(command, "DROP INDEX") == 0)
- return E_DropIndex;
- else if (pg_strcasecmp(command, "DROP LANGUAGE") == 0)
- return E_DropLanguage;
- else if (pg_strcasecmp(command, "DROP OPERATOR") == 0)
- return E_DropOperator;
- else if (pg_strcasecmp(command, "DROP OPERATOR CLASS") == 0)
- return E_DropOperatorClass;
- else if (pg_strcasecmp(command, "DROP OPERATOR FAMILY") == 0)
- return E_DropOperatorFamily;
- else if (pg_strcasecmp(command, "DROP RULE") == 0)
- return E_DropRule;
- else if (pg_strcasecmp(command, "DROP SCHEMA") == 0)
- return E_DropSchema;
- else if (pg_strcasecmp(command, "DROP SEQUENCE") == 0)
- return E_DropSequence;
- else if (pg_strcasecmp(command, "DROP SERVER") == 0)
- return E_DropServer;
- else if (pg_strcasecmp(command, "DROP TABLE") == 0)
- return E_DropTable;
- else if (pg_strcasecmp(command, "DROP TEXT SEARCH CONFIGURATION") == 0)
- return E_DropTextSearchConfiguration;
- else if (pg_strcasecmp(command, "DROP TEXT SEARCH DICTIONARY") == 0)
- return E_DropTextSearchDictionary;
- else if (pg_strcasecmp(command, "DROP TEXT SEARCH PARSER") == 0)
- return E_DropTextSearchParser;
- else if (pg_strcasecmp(command, "DROP TEXT SEARCH TEMPLATE") == 0)
- return E_DropTextSearchTemplate;
- else if (pg_strcasecmp(command, "DROP TRIGGER") == 0)
- return E_DropTrigger;
- else if (pg_strcasecmp(command, "DROP TYPE") == 0)
- return E_DropType;
- else if (pg_strcasecmp(command, "DROP USER MAPPING") == 0)
- return E_DropUserMapping;
- else if (pg_strcasecmp(command, "DROP VIEW") == 0)
- return E_DropView;
- else if (pg_strcasecmp(command, "LOAD") == 0)
- return E_Load;
- else if (pg_strcasecmp(command, "REINDEX") == 0)
- return E_Reindex;
- else if (pg_strcasecmp(command, "SELECT INTO") == 0)
- return E_SelectInto;
- else if (pg_strcasecmp(command, "VACUUM") == 0)
- return E_Vacuum;
- else
- {
- if (!noerror)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("unrecognized command \"%s\"", command)));
- }
- return E_UNKNOWN;
-}
-
-char *
-command_to_string(TrigEventCommand command)
-{
- switch (command)
- {
- case E_UNKNOWN:
- return "UNKNOWN";
- case E_ANY:
- return "ANY";
- case E_AlterCast:
- return "ALTER CAST";
- case E_AlterIndex:
- return "ALTER INDEX";
- case E_AlterAggregate:
- return "ALTER AGGREGATE";
- case E_AlterCollation:
- return "ALTER COLLATION";
- case E_AlterConversion:
- return "ALTER CONVERSION";
- case E_AlterDomain:
- return "ALTER DOMAIN";
- case E_AlterExtension:
- return "ALTER EXTENSION";
- case E_AlterForeignDataWrapper:
- return "ALTER FOREIGN DATA WRAPPER";
- case E_AlterForeignTable:
- return "ALTER FOREIGN TABLE";
- case E_AlterFunction:
- return "ALTER FUNCTION";
- case E_AlterLanguage:
- return "ALTER LANGUAGE";
- case E_AlterOperator:
- return "ALTER OPERATOR";
- case E_AlterOperatorClass:
- return "ALTER OPERATOR CLASS";
- case E_AlterOperatorFamily:
- return "ALTER OPERATOR FAMILY";
- case E_AlterSequence:
- return "ALTER SEQUENCE";
- case E_AlterServer:
- return "ALTER SERVER";
- case E_AlterSchema:
- return "ALTER SCHEMA";
- case E_AlterTable:
- return "ALTER TABLE";
- case E_AlterTextSearchConfiguration:
- return "ALTER TEXT SEARCH CONFIGURATION";
- case E_AlterTextSearchDictionary:
- return "ALTER TEXT SEARCH DICTIONARY";
- case E_AlterTextSearchParser:
- return "ALTER TEXT SEARCH PARSER";
- case E_AlterTextSearchTemplate:
- return "ALTER TEXT SEARCH TEMPLATE";
- case E_AlterTrigger:
- return "ALTER TRIGGER";
- case E_AlterType:
- return "ALTER TYPE";
- case E_AlterUserMapping:
- return "ALTER USER MAPPING";
- case E_AlterView:
- return "ALTER VIEW";
- case E_Cluster:
- return "CLUSTER";
- case E_CreateAggregate:
- return "CREATE AGGREGATE";
- case E_CreateCast:
- return "CREATE CAST";
- case E_CreateCollation:
- return "CREATE COLLATION";
- case E_CreateConversion:
- return "CREATE CONVERSION";
- case E_CreateDomain:
- return "CREATE DOMAIN";
- case E_CreateExtension:
- return "CREATE EXTENSION";
- case E_CreateForeignDataWrapper:
- return "CREATE FOREIGN DATA WRAPPER";
- case E_CreateForeignTable:
- return "CREATE FOREIGN TABLE";
- case E_CreateFunction:
- return "CREATE FUNCTION";
- case E_CreateIndex:
- return "CREATE INDEX";
- case E_CreateLanguage:
- return "CREATE LANGUAGE";
- case E_CreateOperator:
- return "CREATE OPERATOR";
- case E_CreateOperatorClass:
- return "CREATE OPERATOR CLASS";
- case E_CreateOperatorFamily:
- return "CREATE OPERATOR FAMILY";
- case E_CreateRule:
- return "CREATE RULE";
- case E_CreateSequence:
- return "CREATE SEQUENCE";
- case E_CreateServer:
- return "CREATE SERVER";
- case E_CreateSchema:
- return "CREATE SCHEMA";
- case E_CreateTable:
- return "CREATE TABLE";
- case E_CreateTableAs:
- return "CREATE TABLE AS";
- case E_CreateTextSearchConfiguration:
- return "CREATE TEXT SEARCH CONFIGURATION";
- case E_CreateTextSearchDictionary:
- return "CREATE TEXT SEARCH DICTIONARY";
- case E_CreateTextSearchParser:
- return "CREATE TEXT SEARCH PARSER";
- case E_CreateTextSearchTemplate:
- return "CREATE TEXT SEARCH TEMPLATE";
- case E_CreateTrigger:
- return "CREATE TRIGGER";
- case E_CreateType:
- return "CREATE TYPE";
- case E_CreateUserMapping:
- return "CREATE USER MAPPING";
- case E_CreateView:
- return "CREATE VIEW";
- case E_DropAggregate:
- return "DROP AGGREGATE";
- case E_DropCast:
- return "DROP CAST";
- case E_DropCollation:
- return "DROP COLLATION";
- case E_DropConversion:
- return "DROP CONVERSION";
- case E_DropDomain:
- return "DROP DOMAIN";
- case E_DropExtension:
- return "DROP EXTENSION";
- case E_DropForeignDataWrapper:
- return "DROP FOREIGN DATA WRAPPER";
- case E_DropForeignTable:
- return "DROP FOREIGN TABLE";
- case E_DropFunction:
- return "DROP FUNCTION";
- case E_DropIndex:
- return "DROP INDEX";
- case E_DropLanguage:
- return "DROP LANGUAGE";
- case E_DropOperator:
- return "DROP OPERATOR";
- case E_DropOperatorClass:
- return "DROP OPERATOR CLASS";
- case E_DropOperatorFamily:
- return "DROP OPERATOR FAMILY";
- case E_DropRule:
- return "DROP RULE";
- case E_DropSchema:
- return "DROP SCHEMA";
- case E_DropSequence:
- return "DROP SEQUENCE";
- case E_DropServer:
- return "DROP SERVER";
- case E_DropTable:
- return "DROP TABLE";
- case E_DropTextSearchConfiguration:
- return "DROP TEXT SEARCH CONFIGURATION";
- case E_DropTextSearchDictionary:
- return "DROP TEXT SEARCH DICTIONARY";
- case E_DropTextSearchParser:
- return "DROP TEXT SEARCH PARSER";
- case E_DropTextSearchTemplate:
- return "DROP TEXT SEARCH TEMPLATE";
- case E_DropTrigger:
- return "DROP TRIGGER";
- case E_DropType:
- return "DROP TYPE";
- case E_DropUserMapping:
- return "DROP USER MAPPING";
- case E_DropView:
- return "DROP VIEW";
- case E_Load:
- return "LOAD";
- case E_Reindex:
- return "REINDEX";
- case E_SelectInto:
- return "SELECT INTO";
- case E_Vacuum:
- return "VACUUM";
- }
- return NULL;
-}
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 78ef831..19f9895 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -70,10 +70,6 @@ ExecRenameStmt(RenameStmt *stmt)
RenameDatabase(stmt->subname, stmt->newname);
break;
- case OBJECT_EVENT_TRIGGER:
- RenameEventTrigger(stmt->subname, stmt->newname);
- break;
-
case OBJECT_FDW:
RenameForeignDataWrapper(stmt->subname, stmt->newname);
break;
@@ -82,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;
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 1b8529e..8f5d7e0 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
index 4ea2848..502e34b 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -32,7 +32,6 @@
#include "miscadmin.h"
#include "utils/acl.h"
#include "utils/builtins.h"
-#include "utils/evtcache.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
@@ -49,23 +48,6 @@ static void AlterEventTriggerOwner_internal(Relation rel,
Oid newOwnerId);
/*
- * Check permission: command triggers are only available for superusers. Raise
- * an exception when requirements are not fullfilled.
- *
- * It's not clear how to accept that database owners be able to create command
- * triggers, a superuser could run a command that fires a trigger's procedure
- * written by the database owner and now running with superuser privileges.
- */
-static void
-CheckEventTriggerPrivileges()
-{
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- (errmsg("must be superuser to use command triggers"))));
-}
-
-/*
* Insert Command Trigger Tuple
*
* Insert the new pg_event_trigger row, and return the OID assigned to the new
@@ -111,7 +93,7 @@ InsertEventTriggerTuple(char *trigname, TrigEvent event, Oid evtOwner,
{
TrigEventCommand cmd = lfirst_int(lc);
char *cmdstr = command_to_string(cmd);
- if (cmd == E_UNKNOWN || cmdstr == NULL)
+ if (cmd == ETC_UNKNOWN || cmdstr == NULL)
elog(ERROR, "Unknown command %d", cmd);
tags[i++] = PointerGetDatum(cstring_to_text(cmdstr));
}
@@ -158,7 +140,16 @@ CreateEventTrigger(CreateEventTrigStmt *stmt, const char *queryString)
Oid funcrettype;
Oid evtowner = GetUserId();
- CheckEventTriggerPrivileges();
+ /*
+ * 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,
+ (errmsg("permission denied to create event trigger \"%s\"",
+ stmt->trigname),
+ errhint("Must be superuser to create an event trigger.")));
/*
* Find and validate the trigger function.
@@ -171,7 +162,7 @@ CreateEventTrigger(CreateEventTrigStmt *stmt, const char *queryString)
if (funcrettype != EVTTRIGGEROID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("function \"%s\" must return type \"command_trigger\"",
+ errmsg("function \"%s\" must return type \"event_trigger\"",
NameListToString(stmt->funcname))));
/*
@@ -214,7 +205,7 @@ RemoveEventTriggerById(Oid trigOid)
}
/*
- * ALTER EVENT TRIGGER foo ON COMMAND ... ENABLE|DISABLE|ENABLE ALWAYS|REPLICA
+ * ALTER EVENT TRIGGER foo ENABLE|DISABLE|ENABLE ALWAYS|REPLICA
*/
void
AlterEventTrigger(AlterEventTrigStmt *stmt)
@@ -224,15 +215,18 @@ AlterEventTrigger(AlterEventTrigStmt *stmt)
Form_pg_event_trigger evtForm;
char tgenabled = stmt->tgenabled;
- CheckEventTriggerPrivileges();
-
tgrel = heap_open(EventTriggerRelationId, RowExclusiveLock);
- tup = SearchSysCacheCopy1(EVENTTRIGGERNAME, CStringGetDatum(stmt->trigname));
+ tup = SearchSysCacheCopy1(EVENTTRIGGERNAME,
+ CStringGetDatum(stmt->trigname));
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("event trigger \"%s\" does not exist", stmt->trigname)));
+ 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);
@@ -248,7 +242,7 @@ AlterEventTrigger(AlterEventTrigStmt *stmt)
/*
- * Rename command trigger
+ * Rename event trigger
*/
void
RenameEventTrigger(const char *trigname, const char *newname)
@@ -257,8 +251,6 @@ RenameEventTrigger(const char *trigname, const char *newname)
Relation rel;
Form_pg_event_trigger evtForm;
- CheckEventTriggerPrivileges();
-
rel = heap_open(EventTriggerRelationId, RowExclusiveLock);
/* newname must be available */
@@ -273,6 +265,9 @@ RenameEventTrigger(const char *trigname, const char *newname)
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);
@@ -344,22 +339,31 @@ AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
{
Form_pg_event_trigger form;
- CheckEventTriggerPrivileges();
-
form = (Form_pg_event_trigger) GETSTRUCT(tup);
- if (form->evtowner != newOwnerId)
- {
- form->evtowner = newOwnerId;
+ if (form->evtowner == newOwnerId)
+ return;
- simple_heap_update(rel, &tup->t_self, tup);
- CatalogUpdateIndexes(rel, tup);
+ if (!pg_event_trigger_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER,
+ NameStr(form->evtname));
- /* Update owner dependency reference */
- changeDependencyOnOwner(EventTriggerRelationId,
- HeapTupleGetOid(tup),
- newOwnerId);
- }
+ /* 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);
}
/*
@@ -382,9 +386,9 @@ get_event_trigger_oid(const char *trigname, bool missing_ok)
}
/*
- * Functions to execute the command triggers.
+ * Functions to execute the event triggers.
*
- * We call the functions that matches the command triggers definitions in
+ * We call the functions that matches the event triggers definitions in
* alphabetical order, and give them those arguments:
*
* toplevel command tag, text
@@ -394,7 +398,7 @@ get_event_trigger_oid(const char *trigname, bool missing_ok)
* objectname, text
*
* Those are passed down as special "context" magic variables and need specific
- * support in each PL that wants to support command triggers. All core PL do.
+ * support in each PL that wants to support event triggers. All core PL do.
*/
static void
@@ -409,17 +413,27 @@ call_event_trigger_procedure(EventContext ev_ctx, TrigEvent tev,
fmgr_info(proc, &flinfo);
/*
- * Prepare the command trigger function context from the Command Context.
+ * Prepare the event trigger function context from the Command Context.
* We prepare a dedicated Node here so as not to publish internal data.
*/
- trigdata.type = T_EventTriggerData;
- trigdata.toplevel = ev_ctx->toplevel;
- trigdata.tag = ev_ctx->tag;
- trigdata.objectId = ev_ctx->objectId;
+ trigdata.type = T_EventTriggerData;
+ trigdata.event = pstrdup(event_to_string(tev));
+ trigdata.toplevel = ev_ctx->toplevel;
+ trigdata.tag = ev_ctx->tag;
+ trigdata.objectId = ev_ctx->objectId;
trigdata.schemaname = ev_ctx->schemaname;
trigdata.objectname = ev_ctx->objectname;
- trigdata.parsetree = ev_ctx->parsetree;
- trigdata.when = pstrdup(event_to_string(tev));
+ trigdata.parsetree = ev_ctx->parsetree;
+
+ if (ev_ctx->operation == NULL)
+ trigdata.operation = NULL;
+ else
+ trigdata.operation = pstrdup(ev_ctx->operation);
+
+ if (ev_ctx->objecttype == -1)
+ trigdata.objecttype = NULL;
+ else
+ trigdata.objecttype = pstrdup(objecttype_to_string(ev_ctx->objecttype));
/*
* Call the function, passing no arguments but setting a context.
@@ -436,599 +450,55 @@ call_event_trigger_procedure(EventContext ev_ctx, TrigEvent tev,
/*
* Routine to call to setup a EventContextData evt.
+ *
+ * The fields 'objecttype' must be set before calling other entry points. The
+ * fields 'operation', 'objectId', 'objectname' and 'schemaname' might be set
+ * to interesting values.
*/
void
InitEventContext(EventContext evt, const Node *parsetree)
{
- evt->command = E_UNKNOWN;
- evt->toplevel = NULL;
- evt->tag = (char *) CreateCommandTag((Node *)parsetree);
- evt->parsetree = (Node *)parsetree;
- evt->objectId = InvalidOid;
+ evt->command = ETC_UNSET;
+ evt->toplevel = NULL;
+ evt->tag = (char *) CreateCommandTag((Node *)parsetree);
+ evt->parsetree = (Node *)parsetree;
+ evt->operation = NULL;
+ evt->objecttype = -1;
+ evt->objectId = InvalidOid;
evt->objectname = NULL;
evt->schemaname = NULL;
- /*
- * Fill in the event command, which is an enum constant to match against
- * what's stored into catalogs. As we are storing that on disk, we need the
- * enum values to be stable, see src/include/catalog/pg_event_trigger.h for
- * details.
- */
- switch (nodeTag(parsetree))
- {
- case T_CreateSchemaStmt:
- evt->command = E_CreateSchema;
- break;
-
- case T_CreateStmt:
- evt->command = E_CreateTable;
- break;
-
- case T_CreateForeignTableStmt:
- evt->command = E_CreateForeignTable;
- break;
-
- case T_CreateExtensionStmt:
- evt->command = E_CreateExtension;
- break;
-
- case T_AlterExtensionStmt:
- case T_AlterExtensionContentsStmt:
- evt->command = E_AlterExtension;
- break;
-
- case T_CreateFdwStmt:
- evt->command = E_CreateForeignDataWrapper;
- break;
-
- case T_AlterFdwStmt:
- evt->command = E_AlterForeignDataWrapper;
- break;
-
- case T_CreateForeignServerStmt:
- evt->command = E_CreateServer;
- break;
-
- case T_AlterForeignServerStmt:
- evt->command = E_AlterServer;
- break;
-
- case T_CreateUserMappingStmt:
- evt->command = E_CreateUserMapping;
- break;
-
- case T_AlterUserMappingStmt:
- evt->command = E_AlterUserMapping;
- break;
-
- case T_DropUserMappingStmt:
- evt->command = E_DropUserMapping;
- break;
-
- case T_DropStmt:
- switch (((DropStmt *) parsetree)->removeType)
- {
- case OBJECT_AGGREGATE:
- evt->command = E_DropAggregate;
- break;
- case OBJECT_CAST:
- evt->command = E_DropCast;
- break;
- case OBJECT_COLLATION:
- evt->command = E_DropCollation;
- break;
- case OBJECT_CONVERSION:
- evt->command = E_DropConversion;
- break;
- case OBJECT_DOMAIN:
- evt->command = E_DropDomain;
- break;
- case OBJECT_EXTENSION:
- evt->command = E_DropExtension;
- break;
- case OBJECT_FDW:
- evt->command = E_DropForeignDataWrapper;
- break;
- case OBJECT_FOREIGN_SERVER:
- evt->command = E_DropServer;
- break;
- case OBJECT_FOREIGN_TABLE:
- evt->command = E_DropForeignTable;
- break;
- case OBJECT_FUNCTION:
- evt->command = E_DropFunction;
- break;
- case OBJECT_INDEX:
- evt->command = E_DropIndex;
- break;
- case OBJECT_LANGUAGE:
- evt->command = E_DropLanguage;
- break;
- case OBJECT_OPCLASS:
- evt->command = E_DropOperatorClass;
- break;
- case OBJECT_OPERATOR:
- evt->command = E_DropOperator;
- break;
- case OBJECT_OPFAMILY:
- evt->command = E_DropOperatorFamily;
- break;
- case OBJECT_SCHEMA:
- evt->command = E_DropSchema;
- break;
- case OBJECT_SEQUENCE:
- evt->command = E_DropSequence;
- break;
- case OBJECT_TABLE:
- evt->command = E_DropTable;
- break;
- case OBJECT_TRIGGER:
- evt->command = E_DropTrigger;
- break;
- case OBJECT_TSCONFIGURATION:
- evt->command = E_DropTextSearchConfiguration;
- break;
- case OBJECT_TSDICTIONARY:
- evt->command = E_DropTextSearchDictionary;
- break;
- case OBJECT_TSPARSER:
- evt->command = E_DropTextSearchParser;
- break;
- case OBJECT_TSTEMPLATE:
- evt->command = E_DropTextSearchTemplate;
- break;
- case OBJECT_TYPE:
- evt->command = E_DropType;
- break;
- case OBJECT_VIEW:
- evt->command = E_DropView;
- break;
- case OBJECT_ROLE:
- case OBJECT_EVENT_TRIGGER:
- case OBJECT_ATTRIBUTE:
- case OBJECT_COLUMN:
- case OBJECT_CONSTRAINT:
- case OBJECT_DATABASE:
- case OBJECT_LARGEOBJECT:
- case OBJECT_RULE:
- case OBJECT_TABLESPACE:
- /* no support for specific command triggers */
- break;
- }
- break;
-
- case T_RenameStmt:
- switch (((RenameStmt *) parsetree)->renameType)
- {
- case OBJECT_ATTRIBUTE:
- evt->command = E_AlterType;
- break;
- case OBJECT_AGGREGATE:
- evt->command = E_AlterAggregate;
- break;
- case OBJECT_CAST:
- evt->command = E_AlterCast;
- break;
- case OBJECT_COLLATION:
- evt->command = E_AlterCollation;
- break;
- case OBJECT_COLUMN:
- evt->command = E_AlterTable;
- break;
- case OBJECT_CONVERSION:
- evt->command = E_AlterConversion;
- break;
- case OBJECT_DOMAIN:
- evt->command = E_AlterDomain;
- break;
- case OBJECT_EXTENSION:
- evt->command = E_AlterExtension;
- break;
- case OBJECT_FDW:
- evt->command = E_AlterForeignDataWrapper;
- break;
- case OBJECT_FOREIGN_SERVER:
- evt->command = E_AlterServer;
- break;
- case OBJECT_FOREIGN_TABLE:
- evt->command = E_AlterForeignTable;
- break;
- case OBJECT_FUNCTION:
- evt->command = E_AlterFunction;
- break;
- case OBJECT_INDEX:
- evt->command = E_AlterIndex;
- break;
- case OBJECT_LANGUAGE:
- evt->command = E_AlterLanguage;
- break;
- case OBJECT_OPCLASS:
- evt->command = E_AlterOperatorClass;
- break;
- case OBJECT_OPERATOR:
- evt->command = E_AlterOperator;
- break;
- case OBJECT_OPFAMILY:
- evt->command = E_AlterOperatorFamily;
- break;
- case OBJECT_SCHEMA:
- evt->command = E_AlterSchema;
- break;
- case OBJECT_SEQUENCE:
- evt->command = E_AlterSequence;
- break;
- case OBJECT_TABLE:
- evt->command = E_AlterTable;
- break;
- case OBJECT_TRIGGER:
- evt->command = E_AlterTrigger;
- break;
- case OBJECT_TSCONFIGURATION:
- evt->command = E_AlterTextSearchConfiguration;
- break;
- case OBJECT_TSDICTIONARY:
- evt->command = E_AlterTextSearchDictionary;
- break;
- case OBJECT_TSPARSER:
- evt->command = E_AlterTextSearchParser;
- break;
- case OBJECT_TSTEMPLATE:
- evt->command = E_AlterTextSearchTemplate;
- break;
- case OBJECT_TYPE:
- evt->command = E_AlterType;
- break;
- case OBJECT_VIEW:
- evt->command = E_AlterView;
- break;
- case OBJECT_ROLE:
- case OBJECT_EVENT_TRIGGER:
- case OBJECT_CONSTRAINT:
- case OBJECT_DATABASE:
- case OBJECT_LARGEOBJECT:
- case OBJECT_RULE:
- case OBJECT_TABLESPACE:
- /* no support for specific command triggers */
- break;
- }
- break;
-
- case T_AlterObjectSchemaStmt:
- switch (((AlterObjectSchemaStmt *) parsetree)->objectType)
- {
- case OBJECT_AGGREGATE:
- evt->command = E_AlterAggregate;
- break;
- case OBJECT_CAST:
- evt->command = E_AlterCast;
- break;
- case OBJECT_COLLATION:
- evt->command = E_AlterCollation;
- break;
- case OBJECT_CONVERSION:
- evt->command = E_AlterConversion;
- break;
- case OBJECT_DOMAIN:
- evt->command = E_AlterDomain;
- break;
- case OBJECT_EXTENSION:
- evt->command = E_AlterExtension;
- break;
- case OBJECT_FDW:
- evt->command = E_AlterForeignDataWrapper;
- break;
- case OBJECT_FOREIGN_SERVER:
- evt->command = E_AlterServer;
- break;
- case OBJECT_FOREIGN_TABLE:
- evt->command = E_AlterForeignTable;
- break;
- case OBJECT_FUNCTION:
- evt->command = E_AlterFunction;
- break;
- case OBJECT_INDEX:
- evt->command = E_AlterIndex;
- break;
- case OBJECT_LANGUAGE:
- evt->command = E_AlterLanguage;
- break;
- case OBJECT_OPCLASS:
- evt->command = E_AlterOperatorClass;
- break;
- case OBJECT_OPERATOR:
- evt->command = E_AlterOperator;
- break;
- case OBJECT_OPFAMILY:
- evt->command = E_AlterOperatorFamily;
- break;
- case OBJECT_SCHEMA:
- evt->command = E_AlterSchema;
- break;
- case OBJECT_SEQUENCE:
- evt->command = E_AlterSequence;
- break;
- case OBJECT_TABLE:
- evt->command = E_AlterTable;
- break;
- case OBJECT_TRIGGER:
- evt->command = E_AlterTrigger;
- break;
- case OBJECT_TSCONFIGURATION:
- evt->command = E_AlterTextSearchConfiguration;
- break;
- case OBJECT_TSDICTIONARY:
- evt->command = E_AlterTextSearchDictionary;
- break;
- case OBJECT_TSPARSER:
- evt->command = E_AlterTextSearchParser;
- break;
- case OBJECT_TSTEMPLATE:
- evt->command = E_AlterTextSearchTemplate;
- break;
- case OBJECT_TYPE:
- evt->command = E_AlterType;
- break;
- case OBJECT_VIEW:
- evt->command = E_AlterView;
- break;
- case OBJECT_ROLE:
- case OBJECT_EVENT_TRIGGER:
- case OBJECT_ATTRIBUTE:
- case OBJECT_COLUMN:
- case OBJECT_CONSTRAINT:
- case OBJECT_DATABASE:
- case OBJECT_LARGEOBJECT:
- case OBJECT_RULE:
- case OBJECT_TABLESPACE:
- /* no support for specific command triggers */
- break;
- }
- break;
-
- case T_AlterOwnerStmt:
- switch (((AlterOwnerStmt *) parsetree)->objectType)
- {
- case OBJECT_AGGREGATE:
- evt->command = E_AlterAggregate;
- break;
- case OBJECT_CAST:
- evt->command = E_AlterCast;
- break;
- case OBJECT_COLLATION:
- evt->command = E_AlterCollation;
- break;
- case OBJECT_CONVERSION:
- evt->command = E_AlterConversion;
- break;
- case OBJECT_DOMAIN:
- evt->command = E_AlterDomain;
- break;
- case OBJECT_EXTENSION:
- evt->command = E_AlterExtension;
- break;
- case OBJECT_FDW:
- evt->command = E_AlterForeignDataWrapper;
- break;
- case OBJECT_FOREIGN_SERVER:
- evt->command = E_AlterServer;
- break;
- case OBJECT_FOREIGN_TABLE:
- evt->command = E_AlterForeignTable;
- break;
- case OBJECT_FUNCTION:
- evt->command = E_AlterFunction;
- break;
- case OBJECT_INDEX:
- evt->command = E_AlterIndex;
- break;
- case OBJECT_LANGUAGE:
- evt->command = E_AlterLanguage;
- break;
- case OBJECT_OPCLASS:
- evt->command = E_AlterOperatorClass;
- break;
- case OBJECT_OPERATOR:
- evt->command = E_AlterOperator;
- break;
- case OBJECT_OPFAMILY:
- evt->command = E_AlterOperatorFamily;
- break;
- case OBJECT_SCHEMA:
- evt->command = E_AlterSchema;
- break;
- case OBJECT_SEQUENCE:
- evt->command = E_AlterSequence;
- break;
- case OBJECT_TABLE:
- evt->command = E_AlterTable;
- break;
- case OBJECT_TRIGGER:
- evt->command = E_AlterTrigger;
- break;
- case OBJECT_TSCONFIGURATION:
- evt->command = E_AlterTextSearchConfiguration;
- break;
- case OBJECT_TSDICTIONARY:
- evt->command = E_AlterTextSearchDictionary;
- break;
- case OBJECT_TSPARSER:
- evt->command = E_AlterTextSearchParser;
- break;
- case OBJECT_TSTEMPLATE:
- evt->command = E_AlterTextSearchTemplate;
- break;
- case OBJECT_TYPE:
- evt->command = E_AlterType;
- break;
- case OBJECT_VIEW:
- evt->command = E_AlterView;
- break;
- case OBJECT_ROLE:
- case OBJECT_EVENT_TRIGGER:
- case OBJECT_ATTRIBUTE:
- case OBJECT_COLUMN:
- case OBJECT_CONSTRAINT:
- case OBJECT_DATABASE:
- case OBJECT_LARGEOBJECT:
- case OBJECT_RULE:
- case OBJECT_TABLESPACE:
- /* no support for specific command triggers */
- break;
- }
- break;
-
- case T_AlterTableStmt:
- evt->command = E_AlterTable;
- break;
-
- case T_AlterDomainStmt:
- evt->command = E_AlterDomain;
- break;
-
- case T_DefineStmt:
- switch (((DefineStmt *) parsetree)->kind)
- {
- case OBJECT_AGGREGATE:
- evt->command = E_CreateAggregate;
- break;
- case OBJECT_OPERATOR:
- evt->command = E_CreateOperator;
- break;
- case OBJECT_TYPE:
- evt->command = E_CreateType;
- break;
- case OBJECT_TSPARSER:
- evt->command = E_CreateTextSearchParser;
- break;
- case OBJECT_TSDICTIONARY:
- evt->command = E_CreateTextSearchDictionary;;
- break;
- case OBJECT_TSTEMPLATE:
- evt->command = E_CreateTextSearchTemplate;
- break;
- case OBJECT_TSCONFIGURATION:
- evt->command = E_CreateTextSearchConfiguration;
- break;
- case OBJECT_COLLATION:
- evt->command = E_CreateCollation;
- break;
- default:
- elog(ERROR, "unrecognized define stmt type: %d",
- (int) ((DefineStmt *) parsetree)->kind);
- break;
- }
- break;
-
- case T_CompositeTypeStmt: /* CREATE TYPE (composite) */
- case T_CreateEnumStmt: /* CREATE TYPE AS ENUM */
- case T_CreateRangeStmt: /* CREATE TYPE AS RANGE */
- evt->command = E_CreateType;
- break;
-
- case T_AlterEnumStmt: /* ALTER TYPE (enum) */
- evt->command = E_AlterType;
- break;
-
- case T_ViewStmt: /* CREATE VIEW */
- evt->command = E_CreateView;
- break;
-
- case T_CreateFunctionStmt: /* CREATE FUNCTION */
- evt->command = E_CreateFunction;
- break;
-
- case T_AlterFunctionStmt: /* ALTER FUNCTION */
- evt->command = E_AlterFunction;
- break;
-
- case T_IndexStmt: /* CREATE INDEX */
- evt->command = E_CreateIndex;
- break;
-
- case T_CreateSeqStmt:
- evt->command = E_CreateSequence;
- break;
-
- case T_AlterSeqStmt:
- evt->command = E_AlterSequence;
- break;
-
- case T_LoadStmt:
- evt->command = E_Load;
- break;
-
- case T_ClusterStmt:
- evt->command = E_Cluster;
- break;
-
- case T_VacuumStmt:
- evt->command = E_Vacuum;
- break;
-
- case T_CreateTableAsStmt:
- evt->command = E_CreateTableAs;
- break;
-
- case T_CreateTrigStmt:
- evt->command = E_CreateTrigger;
- break;
-
- case T_CreateDomainStmt:
- evt->command = E_CreateDomain;
- break;
-
- case T_ReindexStmt:
- evt->command = E_Reindex;
- break;
-
- case T_CreateConversionStmt:
- evt->command = E_CreateConversion;
- break;
-
- case T_CreateCastStmt:
- evt->command = E_CreateCast;
- break;
-
- case T_CreateOpClassStmt:
- evt->command = E_CreateOperatorClass;
- break;
-
- case T_CreateOpFamilyStmt:
- evt->command = E_CreateOperatorFamily;
- break;
-
- case T_AlterOpFamilyStmt:
- evt->command = E_AlterOperatorFamily;
- break;
-
- case T_AlterTSDictionaryStmt:
- evt->command = E_AlterTextSearchDictionary;
- break;
-
- case T_AlterTSConfigurationStmt:
- evt->command = E_AlterTextSearchConfiguration;
- break;
-
- default:
- /* reaching that part of the code only means that we are not
- * supporting command triggers for the given command, which still
- * needs to execute.
- */
- break;
- }
+ /* guess the ongoing operation from the command tag */
+ if (strncmp(evt->tag, "CREATE ", 7) == 0)
+ evt->operation = pstrdup("CREATE");
+ else if (strncmp(evt->tag, "DROP ", 5) == 0)
+ evt->operation = pstrdup("DROP");
+ else if (strncmp(evt->tag, "ALTER ", 6) == 0)
+ evt->operation = pstrdup("ALTER");
}
/*
- * InitEventContext() must have been called first. When
- * CommandFiresTriggersForEvent() returns false, the EventContext structure
- * needs not be initialized further.
+ * InitEventContext() must have been called first, then the event context field
+ * 'objectype' must have been "manually" for command tags supporting several
+ * kinds of object, such as T_DropStmt, T_RenameStmt, T_AlterObjectSchemaStmt,
+ * T_AlterOwnerStmt or T_DefineStmt.
+ *
+ * When CommandFiresTriggersForEvent() returns false, the EventContext
+ * structure needs not be initialized further.
*/
bool
CommandFiresTriggersForEvent(EventContext ev_ctx, TrigEvent tev)
{
EventCommandTriggers *triggers;
- if (ev_ctx == NULL || ev_ctx->command == E_UNKNOWN)
+ if (ev_ctx == NULL)
+ return false;
+
+ if (ev_ctx->command == ETC_UNSET)
+ ev_ctx->command = get_command_from_nodetag(nodeTag(ev_ctx->parsetree),
+ ev_ctx->objecttype, true);
+
+ if (ev_ctx->command == ETC_UNKNOWN)
return false;
triggers = get_event_triggers(tev, ev_ctx->command);
@@ -1037,7 +507,7 @@ CommandFiresTriggersForEvent(EventContext ev_ctx, TrigEvent tev)
}
/*
- * Actually run command triggers of a specific command. We first run ANY
+ * Actually run event triggers for a specific command. We first run ANY
* command triggers.
*/
void
@@ -1046,7 +516,14 @@ ExecEventTriggers(EventContext ev_ctx, TrigEvent tev)
EventCommandTriggers *triggers;
ListCell *lc;
- if (ev_ctx == NULL || ev_ctx->command == E_UNKNOWN)
+ if (ev_ctx == NULL)
+ return;
+
+ if (ev_ctx->command == ETC_UNSET)
+ ev_ctx->command = get_command_from_nodetag(nodeTag(ev_ctx->parsetree),
+ ev_ctx->objecttype, true);
+
+ if (ev_ctx->command == ETC_UNKNOWN)
return;
triggers = get_event_triggers(tev, ev_ctx->command);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index ec5c8f8..f3ab8e9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -214,7 +214,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
CreateUserStmt CreateUserMappingStmt CreateRoleStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
- DropAssertStmt DropTrigStmt DropEventTrigStmt DropRuleStmt DropCastStmt
+ DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt
DropRoleStmt DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
@@ -755,7 +755,6 @@ stmt :
| DropStmt
| DropTableSpaceStmt
| DropTrigStmt
- | DropEventTrigStmt
| DropRoleStmt
| DropUserStmt
| DropUserMappingStmt
@@ -4368,7 +4367,7 @@ trigger_command:
SCONST
{
TrigEventCommand cmdtag = parse_event_tag($1, true);
- if (cmdtag == E_UNKNOWN)
+ if (cmdtag == ETC_UNKNOWN)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unrecognized command \"%s\"", $1),
@@ -4378,27 +4377,6 @@ trigger_command:
;
-DropEventTrigStmt:
- DROP EVENT TRIGGER name opt_drop_behavior
- {
- DropStmt *n = makeNode(DropStmt);
- n->removeType = OBJECT_EVENT_TRIGGER;
- n->objects = list_make1(list_make1(makeString($4)));
- n->behavior = $5;
- n->missing_ok = false;
- $$ = (Node *) n;
- }
- | DROP EVENT TRIGGER IF_P EXISTS name opt_drop_behavior
- {
- DropStmt *n = makeNode(DropStmt);
- n->removeType = OBJECT_EVENT_TRIGGER;
- n->objects = list_make1(list_make1(makeString($6)));
- n->behavior = $7;
- n->missing_ok = true;
- $$ = (Node *) n;
- }
- ;
-
AlterEventTrigStmt:
ALTER EVENT TRIGGER name enable_trigger
{
@@ -5002,6 +4980,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; }
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 29eec11..4e1a8c0 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -63,6 +63,7 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
+
/* Hook for plugins to get control in ProcessUtility() */
ProcessUtility_hook_type ProcessUtility_hook = NULL;
@@ -355,12 +356,7 @@ standard_ProcessUtility(Node *parsetree,
completionTag[0] = '\0';
/* Event Trigger support for command_start */
- InitEventContext(&evt, (Node *)parsetree);
-
- if (CommandFiresTriggersForEvent(&evt, E_CommandStart))
- {
- ExecEventTriggers(&evt, E_CommandStart);
- }
+ InitEventContext(&evt, parsetree);
switch (nodeTag(parsetree))
{
@@ -513,6 +509,7 @@ standard_ProcessUtility(Node *parsetree,
* relation and attribute manipulation
*/
case T_CreateSchemaStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
CreateSchemaCommand((CreateSchemaStmt *) parsetree,
queryString);
break;
@@ -525,6 +522,9 @@ standard_ProcessUtility(Node *parsetree,
Oid relOid = InvalidOid;
CreateStmt *stmt = (CreateStmt *) parsetree;
+ /* possibly run event triggers */
+ ExecEventTriggers(&evt, EVT_CommandStart);
+
/* Run parse analysis ... */
stmts = transformCreateStmt(stmt, queryString);
@@ -589,59 +589,75 @@ standard_ProcessUtility(Node *parsetree,
case T_CreateTableSpaceStmt:
PreventTransactionChain(isTopLevel, "CREATE TABLESPACE");
+ ExecEventTriggers(&evt, EVT_CommandStart);
CreateTableSpace((CreateTableSpaceStmt *) parsetree);
break;
case T_DropTableSpaceStmt:
PreventTransactionChain(isTopLevel, "DROP TABLESPACE");
+ ExecEventTriggers(&evt, EVT_CommandStart);
DropTableSpace((DropTableSpaceStmt *) parsetree);
break;
case T_AlterTableSpaceOptionsStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
AlterTableSpaceOptions((AlterTableSpaceOptionsStmt *) parsetree);
break;
case T_CreateExtensionStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
CreateExtension((CreateExtensionStmt *) parsetree);
break;
case T_AlterExtensionStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
ExecAlterExtensionStmt((AlterExtensionStmt *) parsetree);
break;
case T_AlterExtensionContentsStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
ExecAlterExtensionContentsStmt((AlterExtensionContentsStmt *) parsetree);
break;
case T_CreateFdwStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
CreateForeignDataWrapper((CreateFdwStmt *) parsetree);
break;
case T_AlterFdwStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
AlterForeignDataWrapper((AlterFdwStmt *) parsetree);
break;
case T_CreateForeignServerStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
CreateForeignServer((CreateForeignServerStmt *) parsetree);
break;
case T_AlterForeignServerStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
AlterForeignServer((AlterForeignServerStmt *) parsetree);
break;
case T_CreateUserMappingStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
CreateUserMapping((CreateUserMappingStmt *) parsetree);
break;
case T_AlterUserMappingStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
AlterUserMapping((AlterUserMappingStmt *) parsetree);
break;
case T_DropUserMappingStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
RemoveUserMapping((DropUserMappingStmt *) parsetree);
break;
case T_DropStmt:
+ evt.objecttype = ((DropStmt *) parsetree)->removeType;
+ ExecEventTriggers(&evt, EVT_CommandStart);
+
switch (((DropStmt *) parsetree)->removeType)
{
case OBJECT_INDEX:
@@ -705,14 +721,20 @@ standard_ProcessUtility(Node *parsetree,
* schema
*/
case T_RenameStmt:
+ evt.objecttype = ((RenameStmt *) parsetree)->renameType;
+ ExecEventTriggers(&evt, EVT_CommandStart);
ExecRenameStmt((RenameStmt *) parsetree);
break;
case T_AlterObjectSchemaStmt:
+ evt.objecttype = ((AlterObjectSchemaStmt *) parsetree)->objectType;
+ ExecEventTriggers(&evt, EVT_CommandStart);
ExecAlterObjectSchemaStmt((AlterObjectSchemaStmt *) parsetree);
break;
case T_AlterOwnerStmt:
+ evt.objecttype = ((AlterOwnerStmt *) parsetree)->objectType;
+ ExecEventTriggers(&evt, EVT_CommandStart);
ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree);
break;
@@ -724,6 +746,9 @@ standard_ProcessUtility(Node *parsetree,
ListCell *l;
LOCKMODE lockmode;
+ /* run command_start event triggers, if any */
+ ExecEventTriggers(&evt, EVT_CommandStart);
+
/*
* Figure out lock mode, and acquire lock. This also does
* basic permissions checks, so that we won't wait for a lock
@@ -775,6 +800,9 @@ standard_ProcessUtility(Node *parsetree,
{
AlterDomainStmt *stmt = (AlterDomainStmt *) parsetree;
+ /* run command_start event triggers, if any */
+ ExecEventTriggers(&evt, EVT_CommandStart);
+
/*
* Some or all of these functions are recursive to cover
* inherited things, so permission checks are done there.
@@ -839,6 +867,9 @@ standard_ProcessUtility(Node *parsetree,
{
DefineStmt *stmt = (DefineStmt *) parsetree;
+ evt.objecttype = stmt->kind;
+ ExecEventTriggers(&evt, EVT_CommandStart);
+
switch (stmt->kind)
{
case OBJECT_AGGREGATE:
@@ -885,15 +916,18 @@ standard_ProcessUtility(Node *parsetree,
{
CompositeTypeStmt *stmt = (CompositeTypeStmt *) parsetree;
+ ExecEventTriggers(&evt, EVT_CommandStart);
DefineCompositeType(stmt->typevar, stmt->coldeflist);
}
break;
case T_CreateEnumStmt: /* CREATE TYPE AS ENUM */
+ ExecEventTriggers(&evt, EVT_CommandStart);
DefineEnum((CreateEnumStmt *) parsetree);
break;
case T_CreateRangeStmt: /* CREATE TYPE AS RANGE */
+ ExecEventTriggers(&evt, EVT_CommandStart);
DefineRange((CreateRangeStmt *) parsetree);
break;
@@ -905,18 +939,22 @@ standard_ProcessUtility(Node *parsetree,
* defining pg_enum entries go away.
*/
PreventTransactionChain(isTopLevel, "ALTER TYPE ... ADD");
+ ExecEventTriggers(&evt, EVT_CommandStart);
AlterEnum((AlterEnumStmt *) parsetree);
break;
case T_ViewStmt: /* CREATE VIEW */
+ ExecEventTriggers(&evt, EVT_CommandStart);
DefineView((ViewStmt *) parsetree, queryString);
break;
case T_CreateFunctionStmt: /* CREATE FUNCTION */
+ ExecEventTriggers(&evt, EVT_CommandStart);
CreateFunction((CreateFunctionStmt *) parsetree, queryString);
break;
case T_AlterFunctionStmt: /* ALTER FUNCTION */
+ ExecEventTriggers(&evt, EVT_CommandStart);
AlterFunction((AlterFunctionStmt *) parsetree);
break;
@@ -924,6 +962,8 @@ standard_ProcessUtility(Node *parsetree,
{
IndexStmt *stmt = (IndexStmt *) parsetree;
+ ExecEventTriggers(&evt, EVT_CommandStart);
+
if (stmt->concurrent)
PreventTransactionChain(isTopLevel,
"CREATE INDEX CONCURRENTLY");
@@ -958,14 +998,17 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_RuleStmt: /* CREATE RULE */
+ ExecEventTriggers(&evt, EVT_CommandStart);
DefineRule((RuleStmt *) parsetree, queryString);
break;
case T_CreateSeqStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
DefineSequence((CreateSeqStmt *) parsetree);
break;
case T_AlterSeqStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
AlterSequence((AlterSeqStmt *) parsetree);
break;
@@ -1032,6 +1075,8 @@ standard_ProcessUtility(Node *parsetree,
{
LoadStmt *stmt = (LoadStmt *) parsetree;
+ ExecEventTriggers(&evt, EVT_CommandStart);
+
closeAllVfds(); /* probably not necessary... */
/* Allowed names are restricted if you're not superuser */
load_file(stmt->filename, !superuser());
@@ -1040,12 +1085,14 @@ standard_ProcessUtility(Node *parsetree,
case T_ClusterStmt:
/* we choose to allow this during "read only" transactions */
+ ExecEventTriggers(&evt, EVT_CommandStart);
PreventCommandDuringRecovery("CLUSTER");
cluster((ClusterStmt *) parsetree, isTopLevel);
break;
case T_VacuumStmt:
/* we choose to allow this during "read only" transactions */
+ ExecEventTriggers(&evt, EVT_CommandStart);
PreventCommandDuringRecovery("VACUUM");
vacuum((VacuumStmt *) parsetree, InvalidOid, true, NULL, false,
isTopLevel);
@@ -1056,6 +1103,7 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_CreateTableAsStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
ExecCreateTableAs((CreateTableAsStmt *) parsetree,
queryString, params, completionTag);
break;
@@ -1079,6 +1127,7 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_CreateTrigStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
(void) CreateTrigger((CreateTrigStmt *) parsetree, queryString,
InvalidOid, InvalidOid, false);
break;
@@ -1092,6 +1141,7 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_CreatePLangStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
CreateProceduralLanguage((CreatePLangStmt *) parsetree);
break;
@@ -1099,6 +1149,7 @@ standard_ProcessUtility(Node *parsetree,
* ******************************** DOMAIN statements ****
*/
case T_CreateDomainStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
DefineDomain((CreateDomainStmt *) parsetree);
break;
@@ -1164,6 +1215,8 @@ standard_ProcessUtility(Node *parsetree,
{
ReindexStmt *stmt = (ReindexStmt *) parsetree;
+ ExecEventTriggers(&evt, EVT_CommandStart);
+
/* we choose to allow this during "read only" transactions */
PreventCommandDuringRecovery("REINDEX");
switch (stmt->kind)
@@ -1197,30 +1250,37 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_CreateConversionStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
CreateConversionCommand((CreateConversionStmt *) parsetree);
break;
case T_CreateCastStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
CreateCast((CreateCastStmt *) parsetree);
break;
case T_CreateOpClassStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
DefineOpClass((CreateOpClassStmt *) parsetree);
break;
case T_CreateOpFamilyStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
DefineOpFamily((CreateOpFamilyStmt *) parsetree);
break;
case T_AlterOpFamilyStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
AlterOpFamily((AlterOpFamilyStmt *) parsetree);
break;
case T_AlterTSDictionaryStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
AlterTSDictionary((AlterTSDictionaryStmt *) parsetree);
break;
case T_AlterTSConfigurationStmt:
+ ExecEventTriggers(&evt, EVT_CommandStart);
AlterTSConfiguration((AlterTSConfigurationStmt *) parsetree);
break;
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index d7770b8..8590f3c 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/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 17b62b6..ec93149 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -39,11 +39,9 @@
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/tlist.h"
-#include "parser/analyze.h"
#include "parser/keywords.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
-#include "parser/parse_type.h"
#include "parser/parser.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h"
@@ -261,6 +259,7 @@ static char *flatten_reloptions(Oid relid);
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
+
/* ----------
* get_ruledef - Do it all and return a text
* that could be used as a statement
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index 65bc6be..bced1e0 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -18,6 +18,7 @@
#include "access/heapam.h"
#include "catalog/catalog.h"
+#include "catalog/pg_collation.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
@@ -28,9 +29,11 @@
#include "catalog/pg_type.h"
#include "commands/event_trigger.h"
#include "commands/trigger.h"
+#include "nodes/parsenodes.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/evtcache.h"
+#include "utils/formatting.h"
#include "utils/hsearch.h"
#include "utils/inval.h"
#include "utils/memutils.h"
@@ -39,6 +42,962 @@
#include "utils/syscache.h"
/*
+ * EventTriggerCommandTags
+ *
+ * This array provides meta data allowing to parse and rewrite command tags
+ * from the command and catalogs to the internal integers we use to have fast
+ * lookups.
+ *
+ * Lookups have to be fast because they are done for each and every DDL as soon
+ * as some Event Triggers are defined.
+ */
+typedef struct
+{
+ TrigEventCommand command; /* internal command value */
+ char *tag; /* command tag */
+ NodeTag node; /* internal parser node tag */
+ ObjectType type; /* internal object type */
+} EventTriggerCommandTagsType;
+
+/*
+ * Hash table to cache the content of EventTriggerCommandTags, which is
+ * searched by command tag when building the EventTriggerProcsCache, and by
+ * NodeTag and ObjectType from ProcessUtility.
+ *
+ * In both cases we want to avoid to have to scan the whole array each time, so
+ * we cache a dedicated hash table in the session's memory.
+ */
+static HTAB *EventTriggerCommandTagsCache = NULL;
+static HTAB *EventTriggerCommandNodeCache = NULL;
+
+/* entry for the Tags cache (key is NameData of NAMEDATALEN) */
+typedef struct
+{
+ NameData tag;
+ TrigEventCommand command;
+} EventTriggerCommandTagsEntry;
+
+/* key and entry for the Node cache */
+typedef struct
+{
+ NodeTag node; /* internal parser node tag */
+ ObjectType type; /* internal object type */
+} EventTriggerCommandNodeKey;
+
+typedef struct
+{
+ EventTriggerCommandNodeKey key; /* lookup key, must be first */
+ TrigEventCommand command; /* internal command value */
+} EventTriggerCommandNodeEntry;
+
+static EventTriggerCommandTagsType EventTriggerCommandTags[] =
+{
+ {
+ ETC_CreateAggregate,
+ "CREATE AGGREGATE",
+ T_DefineStmt,
+ OBJECT_AGGREGATE
+ },
+ {
+ ETC_CreateCast,
+ "CREATE CAST",
+ T_CreateCastStmt,
+ -1
+ },
+ {
+ ETC_CreateCollation,
+ "CREATE COLLATION",
+ T_DefineStmt,
+ OBJECT_COLLATION
+ },
+ {
+ ETC_CreateConversion,
+ "CREATE CONVERSION",
+ T_CreateConversionStmt,
+ -1
+ },
+ {
+ ETC_CreateDomain,
+ "CREATE DOMAIN",
+ T_CreateDomainStmt,
+ -1
+ },
+ {
+ ETC_CreateExtension,
+ "CREATE EXTENSION",
+ T_CreateExtensionStmt,
+ -1
+ },
+ {
+ ETC_CreateForeignDataWrapper,
+ "CREATE FOREIGN DATA WRAPPER",
+ T_CreateFdwStmt,
+ -1
+ },
+ {
+ ETC_CreateForeignTable,
+ "CREATE FOREIGN TABLE",
+ T_CreateForeignTableStmt,
+ -1
+ },
+ {
+ ETC_CreateFunction,
+ "CREATE FUNCTION",
+ T_CreateFunctionStmt,
+ -1
+ },
+ {
+ ETC_CreateIndex,
+ "CREATE INDEX",
+ T_IndexStmt,
+ -1
+ },
+ {
+ ETC_CreateLanguage,
+ "CREATE LANGUAGE",
+ T_CreatePLangStmt,
+ -1
+ },
+ {
+ ETC_CreateOperator,
+ "CREATE OPERATOR",
+ T_DefineStmt,
+ OBJECT_OPERATOR
+ },
+ {
+ ETC_CreateOperatorClass,
+ "CREATE OPERATOR CLASS",
+ T_CreateOpClassStmt,
+ -1
+ },
+ {
+ ETC_CreateOperatorFamily,
+ "CREATE OPERATOR FAMILY",
+ T_CreateOpFamilyStmt,
+ -1
+ },
+ {
+ ETC_CreateRule,
+ "CREATE RULE",
+ T_RuleStmt,
+ -1
+ },
+ {
+ ETC_CreateSchema,
+ "CREATE SCHEMA",
+ T_CreateSchemaStmt,
+ -1
+ },
+ {
+ ETC_CreateSequence,
+ "CREATE SEQUENCE",
+ T_CreateSeqStmt,
+ -1
+ },
+ {
+ ETC_CreateServer,
+ "CREATE SERVER",
+ T_CreateForeignServerStmt,
+ -1
+ },
+ {
+ ETC_CreateTable,
+ "CREATE TABLE",
+ T_CreateStmt,
+ -1
+ },
+ {
+ ETC_CreateTableAs,
+ "CREATE TABLE AS",
+ T_CreateTableAsStmt,
+ -1
+ },
+ {
+ ETC_SelectInto,
+ "SELECT INTO",
+ T_CreateTableAsStmt,
+ -1
+ },
+ {
+ ETC_CreateTextSearchParser,
+ "CREATE TEXT SEARCH PARSER",
+ T_DefineStmt,
+ OBJECT_TSPARSER
+ },
+ {
+ ETC_CreateTextSearchConfiguration,
+ "CREATE TEXT SEARCH CONFIGURATION",
+ T_DefineStmt,
+ OBJECT_TSCONFIGURATION
+ },
+ {
+ ETC_CreateTextSearchDictionary,
+ "CREATE TEXT SEARCH DICTIONARY",
+ T_DefineStmt,
+ OBJECT_TSDICTIONARY
+ },
+ {
+ ETC_CreateTextSearchTemplate,
+ "CREATE TEXT SEARCH TEMPLATE",
+ T_DefineStmt,
+ OBJECT_TSTEMPLATE
+ },
+ {
+ ETC_CreateTrigger,
+ "CREATE TRIGGER",
+ T_CreateTrigStmt,
+ -1
+ },
+ {
+ ETC_CreateType,
+ "CREATE TYPE",
+ T_DefineStmt,
+ OBJECT_TYPE
+ },
+ {
+ ETC_CreateType,
+ "CREATE TYPE",
+ T_CompositeTypeStmt,
+ -1
+ },
+ {
+ ETC_CreateType,
+ "CREATE TYPE",
+ T_CreateEnumStmt,
+ -1
+ },
+ {
+ ETC_CreateType,
+ "CREATE TYPE",
+ T_CreateRangeStmt,
+ -1
+ },
+ {
+ ETC_CreateUserMapping,
+ "CREATE USER MAPPING",
+ T_CreateUserMappingStmt,
+ -1
+ },
+ {
+ ETC_CreateView,
+ "CREATE VIEW",
+ T_ViewStmt,
+ -1
+ },
+ {
+ ETC_AlterTable,
+ "ALTER TABLE",
+ T_AlterTableStmt,
+ -1
+ },
+ {
+ ETC_DropAggregate,
+ "DROP AGGREGATE",
+ T_DropStmt,
+ OBJECT_AGGREGATE
+ },
+ {
+ ETC_DropCast,
+ "DROP CAST",
+ T_DropStmt,
+ OBJECT_CAST
+ },
+ {
+ ETC_DropCollation,
+ "DROP COLLATION",
+ T_DropStmt,
+ OBJECT_COLLATION
+ },
+ {
+ ETC_DropConversion,
+ "DROP CONVERSION",
+ T_DropStmt,
+ OBJECT_CONVERSION
+ },
+ {
+ ETC_DropDomain,
+ "DROP DOMAIN",
+ T_DropStmt,
+ OBJECT_DOMAIN
+ },
+ {
+ ETC_DropExtension,
+ "DROP EXTENSION",
+ T_DropStmt,
+ OBJECT_EXTENSION
+ },
+ {
+ ETC_DropForeignDataWrapper,
+ "DROP FOREIGN DATA WRAPPER",
+ T_DropStmt,
+ OBJECT_FDW
+ },
+ {
+ ETC_DropForeignTable,
+ "DROP FOREIGN TABLE",
+ T_DropStmt,
+ OBJECT_FOREIGN_TABLE
+ },
+ {
+ ETC_DropFunction,
+ "DROP FUNCTION",
+ T_DropStmt,
+ OBJECT_FUNCTION
+ },
+ {
+ ETC_DropIndex,
+ "DROP INDEX",
+ T_DropStmt,
+ OBJECT_INDEX
+ },
+ {
+ ETC_DropLanguage,
+ "DROP LANGUAGE",
+ T_DropStmt,
+ OBJECT_LANGUAGE
+ },
+ {
+ ETC_DropOperator,
+ "DROP OPERATOR",
+ T_DropStmt,
+ OBJECT_OPERATOR
+ },
+ {
+ ETC_DropOperatorClass,
+ "DROP OPERATOR CLASS",
+ T_DropStmt,
+ OBJECT_OPCLASS
+ },
+ {
+ ETC_DropOperatorFamily,
+ "DROP OPERATOR FAMILY",
+ T_DropStmt,
+ OBJECT_OPFAMILY
+ },
+ {
+ ETC_DropRule,
+ "DROP RULE",
+ T_DropStmt,
+ OBJECT_RULE
+ },
+ {
+ ETC_DropSchema,
+ "DROP SCHEMA",
+ T_DropStmt,
+ OBJECT_SCHEMA
+ },
+ {
+ ETC_DropSequence,
+ "DROP SEQUENCE",
+ T_DropStmt,
+ OBJECT_SEQUENCE
+ },
+ {
+ ETC_DropServer,
+ "DROP SERVER",
+ T_DropStmt,
+ OBJECT_FOREIGN_SERVER
+ },
+ {
+ ETC_DropTable,
+ "DROP TABLE",
+ T_DropStmt,
+ OBJECT_TABLE
+ },
+ {
+ ETC_DropTextSearchParser,
+ "DROP TEXT SEARCH PARSER",
+ T_DropStmt,
+ OBJECT_TSPARSER
+ },
+ {
+ ETC_DropTextSearchConfiguration,
+ "DROP TEXT SEARCH CONFIGURATION",
+ T_DropStmt,
+ OBJECT_TSCONFIGURATION
+ },
+ {
+ ETC_DropTextSearchDictionary,
+ "DROP TEXT SEARCH DICTIONARY",
+ T_DropStmt,
+ OBJECT_TSDICTIONARY
+ },
+ {
+ ETC_DropTextSearchTemplate,
+ "DROP TEXT SEARCH TEMPLATE",
+ T_DropStmt,
+ OBJECT_TSTEMPLATE
+ },
+ {
+ ETC_DropTrigger,
+ "DROP TRIGGER",
+ T_DropStmt,
+ OBJECT_TRIGGER
+ },
+ {
+ ETC_DropType,
+ "DROP TYPE",
+ T_DropStmt,
+ OBJECT_TYPE
+ },
+ {
+ ETC_DropUserMapping,
+ "DROP USER MAPPING",
+ T_DropUserMappingStmt,
+ -1
+ },
+ {
+ ETC_DropView,
+ "DROP VIEW",
+ T_DropStmt,
+ OBJECT_VIEW
+ },
+ {
+ ETC_Vacuum,
+ "VACUUM",
+ T_VacuumStmt,
+ -1
+ },
+ {
+ ETC_Cluster,
+ "CLUSTER",
+ T_ClusterStmt,
+ -1
+ },
+ {
+ ETC_Load,
+ "LOAD",
+ T_LoadStmt,
+ -1
+ },
+ {
+ ETC_Reindex,
+ "REINDEX",
+ T_ReindexStmt,
+ -1
+ },
+ {
+ ETC_AlterSequence,
+ "ALTER SEQUENCE",
+ T_AlterSeqStmt,
+ -1
+ },
+ {
+ ETC_AlterUserMapping,
+ "ALTER USER MAPPING",
+ T_CreateUserMappingStmt,
+ -1
+ },
+ {
+ ETC_AlterFunction,
+ "ALTER FUNCTION",
+ T_AlterFunctionStmt,
+ -1
+ },
+ {
+ ETC_AlterDomain,
+ "ALTER DOMAIN",
+ T_AlterDomainStmt,
+ -1
+ },
+ /* ALTER