+ pg_event_trigger> Columns
+
+
+
+
+ Name
+ Type
+ References
+ Description
+
+
+
+
+
+ evtname
+ name
+
+ Trigger name (must be unique)
+
+
+
+ evtevent
+ name
+
+ Identifies the event for which this trigger fires
+
+
+
+ evtowner
+ oid
+ pg_authid.oid
+ Owner of the event trigger
+
+
+
+ evtfoid
+ oid
+ pg_proc.oid
+ The function to be called
+
+
+
+ evtenabled
+ char
+
+
+ Controls in which modes
+ the event trigger fires.
+ O> = trigger fires in origin> and local> modes,
+ D> = trigger is disabled,
+ R> = trigger fires in replica> mode,
+ A> = trigger fires always.
+
+
+
+
+ evttags
+ text[]
+
+
+ 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.
+
+
+
+
+
+
+
pg_constraint
*** /dev/null
--- b/doc/src/sgml/event-trigger.sgml
***************
*** 0 ****
--- 1,469 ----
+
+
+
+ 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 ddl_command_start>. Support for
+ additional events may be added in future releases.
+
+
+
+ The ddl_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 ddl_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 ddl_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 ddl_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 ddl_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
+ ddl_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
+
+
+
+
+
+
+
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 61,66 ****
--- 61,67 ----
+
*** a/doc/src/sgml/plperl.sgml
--- b/doc/src/sgml/plperl.sgml
***************
*** 1026,1031 **** $$ LANGUAGE plperl;
--- 1026,1039 ----
PL/Perl Triggers
+
+ Trigger Procedures on Data Modification in PL/Perl
+
+
+ trigger
+ in PL/Perl
+
+
PL/Perl can be used to write trigger functions. In a trigger function,
the hash reference $_TD contains information about the
***************
*** 1209,1214 **** CREATE TRIGGER test_valid_id_trig
--- 1217,1296 ----
FOR EACH ROW EXECUTE PROCEDURE valid_id();
+
+
+
+ Trigger Procedures on Events in PL/Perl
+
+
+ event trigger
+ in PL/Perl
+
+
+
+ Event trigger procedures can be written in PL/Perl.
+ 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>.
+
+
+
+ The information from the trigger manager is passed to the procedure body
+ in the following variables:
+
+
+
+
+ $_TD->{when}
+
+
+ The name of the event for which the trigger is called.
+
+
+
+
+
+ $_TD->{tag}
+
+
+ The command tag for which the trigger is fired.
+
+
+
+
+
+ $_TD->{objectid}
+
+
+ The object ID of the object that caused the trigger procedure
+ to be invoked.
+
+
+
+
+
+ $_TD->{objectname}
+
+
+ The name of the object that caused the trigger procedure
+ to be invoked.
+
+
+
+
+
+ $_TD->{schemaname}
+
+
+ The schema of the object that caused the trigger procedure to be
+ invoked, or NULL if the object is not qualified.
+
+
+
+
+
+
+
*** a/doc/src/sgml/plpgsql.sgml
--- b/doc/src/sgml/plpgsql.sgml
***************
*** 3377,3383 **** RAISE unique_violation USING MESSAGE = 'Duplicate user ID: ' || user_id;
in PL/pgSQL
! PL/pgSQL can be used to define trigger
procedures. A trigger procedure is created with the
CREATE FUNCTION> command, declaring it as a function with
--- 3377,3386 ----
in PL/pgSQL
!
! Triggers on data changes
!
! PL/pgSQL can be used to define trigger
procedures. A trigger procedure is created with the
CREATE FUNCTION> command, declaring it as a function with
***************
*** 3924,3929 **** UPDATE sales_fact SET units_sold = units_sold * 2;
--- 3927,4029 ----
SELECT * FROM sales_summary_bytime;
+
+
+
+ Triggers on events
+
+
+ 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:
+
+
+
+ TG_TAG
+
+
+ Data type text; variable that contains the command tag
+ for which the trigger is fired.
+
+
+
+
+
+ TG_WHEN
+
+
+ Data type text; a string representing the event the
+ trigger is fired for.
+
+
+
+
+
+ TG_OBJECTID
+
+
+ Data type oid; the object ID of the object that caused
+ the trigger invocation. NULL when the function is
+ called AFTER a drop command.
+
+
+
+
+
+ TG_SCHEMANAME
+
+
+ Data type name; the name of the schema of the object
+ that caused the trigger invocation. Can be NULL
+ for objects not located in a schema.
+
+
+
+
+
+ TG_OBJECTNAME
+
+
+ Data type name; the name of the object that caused the trigger
+ invocation. Can be NULL.
+
+
+
+
+
+
+
+ shows an example of a
+ event trigger procedure in PL/pgSQL.
+
+
+
+ A PL/pgSQL Event Trigger Procedure
+
+
+ This example trigger simply raises a NOTICE message
+ each time a supported command is executed.
+
+
+
+ CREATE OR REPLACE FUNCTION snitch() RETURNS event_trigger AS $$
+ BEGIN
+ RAISE NOTICE 'snitch: % % %.% [%]',
+ tg_when, tg_tag, tg_schemaname, tg_objectname, tg_objectid;
+ END;
+ $$ LANGUAGE plpgsql;
+
+ CREATE EVENT TRIGGER snitch ON ddl_command_start EXECUTE PROCEDURE snitch();
+
+
+
*** a/doc/src/sgml/plpython.sgml
--- b/doc/src/sgml/plpython.sgml
***************
*** 756,761 **** $$ LANGUAGE plpythonu;
--- 756,769 ----
in PL/Python
+
+ Trigger Procedures on Data Modification in PL/Python
+
+
+ trigger
+ in PL/Python
+
+
When a function is used as a trigger, the dictionary
TD contains trigger-related values:
***************
*** 861,866 **** $$ LANGUAGE plpythonu;
--- 869,947 ----
"MODIFY"> to indicate you've modified the new row.
Otherwise the return value is ignored.
+
+
+
+ Trigger Procedures on Events in PL/Python
+
+
+ event trigger
+ in PL/Python
+
+
+
+ Event trigger procedures can be written in PL/Python.
+ 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>.
+
+
+
+ The information from the trigger manager is passed to the procedure body
+ in the following variables:
+
+
+
+
+ TD["when"]
+
+
+ The name of the event for which the trigger is called.
+
+
+
+
+
+ TD["tag"]
+
+
+ The command tag for which the trigger is fired.
+
+
+
+
+
+ TD["objectid"]
+
+
+ The object ID of the object that caused the trigger procedure
+ to be invoked.
+
+
+
+
+
+ TD["objectname"]
+
+
+ The name of the object that caused the trigger procedure
+ to be invoked.
+
+
+
+
+
+ TD["schemaname"]
+
+
+ The schema of the object that caused the trigger procedure to be
+ invoked, or NULL if the object is not qualified.
+
+
+
+
+
+
*** a/doc/src/sgml/pltcl.sgml
--- b/doc/src/sgml/pltcl.sgml
***************
*** 516,525 **** SELECT 'doesn''t' AS ret
Trigger Procedures in PL/Tcl
!
! trigger
! in PL/Tcl
!
Trigger procedures can be written in PL/Tcl.
--- 516,529 ----
Trigger Procedures in PL/Tcl
!
!
! Trigger Procedures on Data Modification in PL/Tcl
!
!
! trigger
! in PL/Tcl
!
Trigger procedures can be written in PL/Tcl.
***************
*** 709,714 **** CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab
--- 713,791 ----
name; that's supplied from the trigger arguments. This lets the
trigger procedure be reused with different tables.
+
+
+
+ Trigger Procedures on Events in PL/Tcl
+
+
+ event trigger
+ in PL/Tcl
+
+
+
+ Event trigger procedures can be written in PL/Tcl.
+ 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>.
+
+
+
+ The information from the trigger manager is passed to the procedure body
+ in the following variables:
+
+
+
+
+ $TG_when
+
+
+ The name of the event for which the trigger is called.
+
+
+
+
+
+ $TG_tag
+
+
+ The command tag for which the trigger is fired.
+
+
+
+
+
+ $TG_objectid
+
+
+ The object ID of the object that caused the trigger procedure
+ to be invoked.
+
+
+
+
+
+ $TG_objectname
+
+
+ The name of the object that caused the trigger procedure
+ to be invoked.
+
+
+
+
+
+ $TG_schemaname
+
+
+ The schema of the object that caused the trigger procedure to be
+ invoked, or NULL if the object is not qualified.
+
+
+
+
+
+
*** a/doc/src/sgml/postgres.sgml
--- b/doc/src/sgml/postgres.sgml
***************
*** 208,213 ****
--- 208,214 ----
&extend;
&trigger;
+ &event-trigger;
&rules;
&xplang;
*** a/doc/src/sgml/ref/allfiles.sgml
--- b/doc/src/sgml/ref/allfiles.sgml
***************
*** 12,17 **** Complete list of usable sgml source files in this directory.
--- 12,18 ----
+
***************
*** 53,58 **** Complete list of usable sgml source files in this directory.
--- 54,60 ----
+
***************
*** 91,96 **** Complete list of usable sgml source files in this directory.
--- 93,99 ----
+
*** /dev/null
--- b/doc/src/sgml/ref/alter_event_trigger.sgml
***************
*** 0 ****
--- 1,113 ----
+
+
+
+
+ ALTER EVENT TRIGGER
+ 7
+ SQL - Language Statements
+
+
+
+ ALTER EVENT TRIGGER
+ change the definition of an event trigger
+
+
+
+ ALTER EVENT TRIGGER
+
+
+
+
+ ALTER EVENT TRIGGER nameenabled
+ ALTER EVENT TRIGGER name OWNER TO new_owner
+ ALTER EVENT TRIGGER name RENAME TO new_name
+
+ where enabled can be one of:
+
+ ENABLE
+ ENABLE ALWAYS
+ ENABLE REPLICA
+ DISABLE
+
+ and where command can be one of the same list as in .
+
+
+
+
+
+ Description
+
+
+ ALTER EVENT TRIGGER changes properties of an
+ existing event trigger.
+
+
+
+ You must be superuser to alter an event trigger.
+
+
+
+
+ Parameters
+
+
+
+ name
+
+
+ The name of an existing trigger to alter.
+
+
+
+
+
+ new_owner
+
+
+ The user name of the new owner of the event trigger.
+
+
+
+
+
+ new_name
+
+
+ The new name of the event trigger.
+
+
+
+
+
+ enabled
+
+
+ When to enable this trigger. See
+ the session_replication_role parameter.
+
+
+
+
+
+
+
+ Compatibility
+
+
+ There is no ALTER EVENT TRIGGER statement in the
+ SQL standard.
+
+
+
+
+ See Also
+
+
+
+
+
+
+
*** /dev/null
--- b/doc/src/sgml/ref/create_event_trigger.sgml
***************
*** 0 ****
--- 1,184 ----
+
+
+
+
+ CREATE EVENT TRIGGER
+ 7
+ SQL - Language Statements
+
+
+
+ CREATE EVENT TRIGGER
+ define a new event trigger
+
+
+
+ CREATE EVENT TRIGGER
+
+
+
+
+ CREATE EVENT TRIGGER name
+ ON event
+ [ WHEN filter_variable IN (filter_value [ , ... ] ) ]
+ EXECUTE PROCEDURE function_name()
+
+
+
+
+ Description
+
+
+ CREATE EVENT TRIGGER creates a new event trigger.
+ 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.
+
+
+
+
+ Parameters
+
+
+
+ name
+
+
+ The name to give the new trigger. This name must be unique within
+ the database.
+
+
+
+
+
+ event
+
+
+ The name of the event that triggers a call to the given function.
+ See for more information
+ on event names.
+
+
+
+
+
+ filter_variable
+
+
+ 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
+
+
+ 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'>).
+
+
+
+
+
+ function_name
+
+
+ A user-supplied function that is declared as taking no argument and
+ returning type event_trigger.
+
+
+ If your event trigger is implemented in C then it
+ will be called with an argument, of
+ type internal, which is a pointer to
+ the Node * parse tree.
+
+
+
+
+
+
+
+
+ Notes
+
+
+ To create a trigger on a event, the user must be superuser.
+
+
+
+
+ Examples
+
+
+ Forbid the execution of any ddl command:
+
+
+ CREATE OR REPLACE FUNCTION abort_any_command()
+ RETURNS event_trigger
+ LANGUAGE plpgsql
+ AS $$
+ BEGIN
+ RAISE EXCEPTION 'command % is disabled', tg_tag;
+ END;
+ $$;
+
+ CREATE EVENT TRIGGER abort_ddl ON ddl_command_start
+ EXECUTE PROCEDURE abort_any_command();
+
+
+ Execute the function enforce_local_style> each time
+ a CREATE TABLE command is run:
+
+
+ CREATE OR REPLACE FUNCTION enforce_local_style()
+ RETURNS event_trigger
+ LANGUAGE plpgsql
+ AS $$
+ BEGIN
+ IF substring(tg_objectname, 0, 4) NOT IN ('ab_', 'cz_', 'fr_')
+ THEN
+ RAISE EXCEPTION 'invalid relation name: %', tg_objectname;
+ END IF;
+ END;
+ $$;
+
+ CREATE EVENT TRIGGER check_style
+ ON ddl_command_start
+ WHEN tag IN ('CREATE TABLE', 'CREATE TABLE AS', 'SELECT INTO')
+ EXECUTE PROCEDURE enforce_local_style();
+
+
+
+
+
+ Compatibility
+
+
+ There is no CREATE EVENT TRIGGER statement in the
+ SQL standard.
+
+
+
+
+
+ See Also
+
+
+
+
+
+
+
+
*** /dev/null
--- b/doc/src/sgml/ref/drop_event_trigger.sgml
***************
*** 0 ****
--- 1,113 ----
+
+
+
+
+ DROP EVENT TRIGGER
+ 7
+ SQL - Language Statements
+
+
+
+ DROP EVENT TRIGGER
+ remove an event trigger
+
+
+
+ DROP EVENT TRIGGER
+
+
+
+
+ DROP EVENT TRIGGER [ IF EXISTS ] name [ CASCADE | RESTRICT ]
+
+
+
+
+ Description
+
+
+ DROP EVENT TRIGGER removes an existing event trigger.
+ To execute this command, the current user must be the owner of the event
+ trigger.
+
+
+
+
+ Parameters
+
+
+
+
+ IF EXISTS
+
+
+ Do not throw an error if the event trigger does not exist. A notice
+ is issued in this case.
+
+
+
+
+
+ name
+
+
+ The name of the event trigger to remove.
+
+
+
+
+
+ CASCADE
+
+
+ Automatically drop objects that depend on the trigger.
+
+
+
+
+
+ RESTRICT
+
+
+ Refuse to drop the trigger if any objects depend on it. This is
+ the default.
+
+
+
+
+
+
+
+ Examples
+
+
+ Destroy the trigger snitch:
+
+
+ DROP EVENT TRIGGER snitch;
+
+
+
+
+ Compatibility
+
+
+ There is no DROP EVENT TRIGGER statement in the
+ SQL standard.
+
+
+
+
+
+ See Also
+
+
+
+
+
+
+
+
*** a/doc/src/sgml/reference.sgml
--- b/doc/src/sgml/reference.sgml
***************
*** 41,46 ****
--- 41,47 ----
&alterDefaultPrivileges;
&alterDomain;
&alterExtension;
+ &alterEventTrigger;
&alterForeignDataWrapper;
&alterForeignTable;
&alterFunction;
***************
*** 82,87 ****
--- 83,89 ----
&createDatabase;
&createDomain;
&createExtension;
+ &createEventTrigger;
&createForeignDataWrapper;
&createForeignTable;
&createFunction;
***************
*** 120,125 ****
--- 122,128 ----
&dropDatabase;
&dropDomain;
&dropExtension;
+ &dropEventTrigger;
&dropForeignDataWrapper;
&dropForeignTable;
&dropFunction;
*** a/src/backend/catalog/Makefile
--- b/src/backend/catalog/Makefile
***************
*** 31,37 **** 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_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 \
--- 31,37 ----
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_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 \
*** a/src/backend/catalog/aclchk.c
--- b/src/backend/catalog/aclchk.c
***************
*** 29,34 ****
--- 29,35 ----
#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,282 **** restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
--- 278,287 ----
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,3291 **** static const char *const no_priv_msg[MAX_ACL_KIND] =
--- 3291,3298 ----
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,3335 **** static const char *const not_owner_msg[MAX_ACL_KIND] =
--- 3337,3344 ----
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,3460 **** pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum, Oid roleid,
--- 3464,3473 ----
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,4881 **** pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid)
--- 4889,4921 ----
}
/*
+ * 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
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 34,39 ****
--- 34,40 ----
#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,61 ****
--- 57,63 ----
#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,164 **** static const Oid object_classes[MAX_OCLASS] = {
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
UserMappingRelationId, /* OCLASS_USER_MAPPING */
DefaultAclRelationId, /* OCLASS_DEFACL */
! ExtensionRelationId /* OCLASS_EXTENSION */
};
--- 160,167 ----
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
UserMappingRelationId, /* OCLASS_USER_MAPPING */
DefaultAclRelationId, /* OCLASS_DEFACL */
! ExtensionRelationId, /* OCLASS_EXTENSION */
! EventTriggerRelationId /* OCLASS_EVENT_TRIGGER */
};
***************
*** 1112,1117 **** doDeletion(const ObjectAddress *object, int flags)
--- 1115,1124 ----
break;
}
+ case OCLASS_EVENT_TRIGGER:
+ RemoveEventTriggerById(object->objectId);
+ break;
+
case OCLASS_PROC:
RemoveFunctionById(object->objectId);
break;
***************
*** 2269,2274 **** getObjectClass(const ObjectAddress *object)
--- 2276,2284 ----
case ExtensionRelationId:
return OCLASS_EXTENSION;
+
+ case EventTriggerRelationId:
+ return OCLASS_EVENT_TRIGGER;
}
/* shouldn't get here */
***************
*** 2903,2908 **** getObjectDescription(const ObjectAddress *object)
--- 2913,2933 ----
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,
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
***************
*** 21,26 ****
--- 21,27 ----
#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,51 ****
--- 47,53 ----
#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,209 **** static ObjectPropertyType ObjectProperty[] =
--- 206,217 ----
InvalidAttrNumber
},
{
+ EventTriggerRelationId,
+ EventTriggerOidIndexId,
+ -1,
+ InvalidAttrNumber
+ },
+ {
TSConfigRelationId,
TSConfigOidIndexId,
TSCONFIGOID,
***************
*** 325,330 **** get_object_address(ObjectType objtype, List *objname, List *objargs,
--- 333,339 ----
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,551 **** get_object_address_unqualified(ObjectType objtype,
--- 555,563 ----
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,606 **** get_object_address_unqualified(ObjectType objtype,
--- 613,623 ----
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,985 **** check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
--- 997,1007 ----
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,
*** a/src/backend/catalog/pg_shdepend.c
--- b/src/backend/catalog/pg_shdepend.c
***************
*** 25,30 ****
--- 25,31 ----
#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,47 ****
--- 43,49 ----
#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,1403 **** shdepReassignOwned(List *roleids, Oid newrole)
--- 1400,1409 ----
AlterExtensionOwner_oid(sdepForm->objid, newrole);
break;
+ case EventTriggerRelationId:
+ AlterEventTriggerOwner_oid(sdepForm->objid, newrole);
+ break;
+
default:
elog(ERROR, "unexpected classid %u", sdepForm->classid);
break;
*** a/src/backend/commands/Makefile
--- b/src/backend/commands/Makefile
***************
*** 14,21 **** 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 \
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 \
--- 14,21 ----
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 \
! 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 \
*** a/src/backend/commands/alter.c
--- b/src/backend/commands/alter.c
***************
*** 24,29 ****
--- 24,30 ----
#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,82 **** ExecRenameStmt(RenameStmt *stmt)
--- 78,87 ----
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,539 **** ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
--- 539,548 ----
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);
*** a/src/backend/commands/dropcmds.c
--- b/src/backend/commands/dropcmds.c
***************
*** 206,211 **** does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs)
--- 206,215 ----
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));
*** /dev/null
--- b/src/backend/commands/event_trigger.c
***************
*** 0 ****
--- 1,539 ----
+ /*-------------------------------------------------------------------------
+ *
+ * event_trigger.c
+ * PostgreSQL EVENT TRIGGER support code.
+ *
+ * Portions Copyright (c) 2011, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/backend/commands/event_trigger.c
+ *
+ *-------------------------------------------------------------------------
+ */
+ #include "postgres.h"
+
+ #include "access/heapam.h"
+ #include "access/sysattr.h"
+ #include "access/xact.h"
+ #include "catalog/catalog.h"
+ #include "catalog/dependency.h"
+ #include "catalog/indexing.h"
+ #include "catalog/objectaccess.h"
+ #include "catalog/pg_event_trigger.h"
+ #include "catalog/pg_language.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"
+
+ /*
+ * local prototypes
+ */
+ static void AlterEventTriggerOwner_internal(Relation rel,
+ HeapTuple tup,
+ Oid newOwnerId);
+
+ /*
+ * Insert Command Trigger Tuple
+ *
+ * Insert the new pg_event_trigger row, and return the OID assigned to the new
+ * row.
+ */
+ static Oid
+ InsertEventTriggerTuple(char *trigname, TrigEvent event, Oid evtOwner,
+ Oid funcoid, List *cmdlist)
+ {
+ Relation tgrel;
+ Oid trigoid;
+ HeapTuple tuple;
+ Datum values[Natts_pg_trigger];
+ bool nulls[Natts_pg_trigger];
+ ObjectAddress myself, referenced;
+ ArrayType *tagArray;
+ char *evtevent = event_to_string(event);
+
+ 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(evtevent);
+ 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 (cmdlist == NIL)
+ nulls[Anum_pg_event_trigger_evttags - 1] = true;
+ else
+ {
+ ListCell *lc;
+ Datum *tags;
+ int i = 0, l = list_length(cmdlist);
+
+ tags = (Datum *) palloc(l * sizeof(Datum));
+
+ foreach(lc, cmdlist)
+ {
+ TrigEventCommand cmd = lfirst_int(lc);
+ char *cmdstr = command_to_string(cmd);
+ if (cmd == ETC_UNKNOWN || cmdstr == NULL)
+ elog(ERROR, "Unknown command %d", cmd);
+ tags[i++] = PointerGetDatum(cstring_to_text(cmdstr));
+ }
+ tagArray = construct_array(tags, l, TEXTOID, -1, false, 'i');
+
+ values[Anum_pg_event_trigger_evttags - 1] = PointerGetDatum(tagArray);
+ }
+
+ tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
+
+ trigoid = simple_heap_insert(tgrel, tuple);
+ CatalogUpdateIndexes(tgrel, tuple);
+
+ heap_freetuple(tuple);
+
+ /*
+ * Record dependencies for trigger. Always place a normal dependency on
+ * the function.
+ */
+ recordDependencyOnOwner(EventTriggerRelationId, trigoid, evtOwner);
+
+ myself.classId = EventTriggerRelationId;
+ myself.objectId = trigoid;
+ myself.objectSubId = 0;
+
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = funcoid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ heap_close(tgrel, RowExclusiveLock);
+
+ return trigoid;
+ }
+
+ /*
+ * Create a trigger. Returns the OID of the created trigger.
+ */
+ Oid
+ CreateEventTrigger(CreateEventTrigStmt *stmt, const char *queryString)
+ {
+ HeapTuple tuple;
+ Oid funcoid, trigoid;
+ Oid funcrettype;
+ Oid evtowner = GetUserId();
+
+ /*
+ * 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.
+ */
+ funcoid = LookupFuncName(stmt->funcname, 0, NULL, false);
+
+ /* we need the trigger type to validate the return type */
+ 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))));
+
+ /*
+ * Give user a nice error message in case 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)));
+
+ /* Insert the catalog entry */
+ trigoid = InsertEventTriggerTuple(stmt->trigname, stmt->event,
+ evtowner, funcoid, stmt->cmdlist);
+
+ return trigoid;
+ }
+
+ /*
+ * 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;
+ }
+
+ /*
+ * Functions to execute the event triggers.
+ *
+ * We call the functions that matches the event triggers definitions in
+ * alphabetical order, and give them those arguments:
+ *
+ * toplevel command tag, text
+ * command tag, text
+ * objectId, oid
+ * schemaname, text
+ * objectname, text
+ *
+ * Those are passed down as special "context" magic variables and need specific
+ * support in each PL that wants to support event triggers. All core PL do.
+ */
+
+ static void
+ call_event_trigger_procedure(EventContext ev_ctx, TrigEvent tev,
+ RegProcedure proc)
+ {
+ FmgrInfo flinfo;
+ FunctionCallInfoData fcinfo;
+ PgStat_FunctionCallUsage fcusage;
+ EventTriggerData trigdata;
+
+ fmgr_info(proc, &flinfo);
+
+ /*
+ * 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.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;
+
+ 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.
+ */
+ InitFunctionCallInfoData(fcinfo, &flinfo, 0, InvalidOid,
+ (Node *) &trigdata, NULL);
+
+ pgstat_init_function_usage(&fcinfo, &fcusage);
+ FunctionCallInvoke(&fcinfo);
+ pgstat_end_function_usage(&fcusage, true);
+
+ return;
+ }
+
+ /*
+ * 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 = 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;
+
+ /* 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, 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)
+ 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);
+
+ return triggers->procs != NIL;
+ }
+
+ /*
+ * Actually run event triggers for a specific command. We first run ANY
+ * command triggers.
+ */
+ void
+ ExecEventTriggers(EventContext ev_ctx, TrigEvent tev)
+ {
+ EventCommandTriggers *triggers;
+ ListCell *lc;
+
+ 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);
+
+ foreach(lc, triggers->procs)
+ {
+ RegProcedure proc = (RegProcedure) lfirst_oid(lc);
+
+ call_event_trigger_procedure(ev_ctx, tev, proc);
+ CommandCounterIncrement();
+ }
+ return;
+ }
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 3465,3470 **** _copyCreateTrigStmt(const CreateTrigStmt *from)
--- 3465,3495 ----
return newnode;
}
+ static CreateEventTrigStmt *
+ _copyCreateEventTrigStmt(const CreateEventTrigStmt *from)
+ {
+ CreateEventTrigStmt *newnode = makeNode(CreateEventTrigStmt);
+
+ COPY_STRING_FIELD(trigname);
+ COPY_SCALAR_FIELD(event);
+ COPY_NODE_FIELD(funcname);
+ COPY_STRING_FIELD(variable);
+ COPY_NODE_FIELD(cmdlist);
+
+ 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)
{
***************
*** 4316,4321 **** copyObject(const void *from)
--- 4341,4352 ----
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;
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 1792,1797 **** _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
--- 1792,1818 ----
}
static bool
+ _equalCreateEventTrigStmt(const CreateEventTrigStmt *a, const CreateEventTrigStmt *b)
+ {
+ COMPARE_STRING_FIELD(trigname);
+ COMPARE_SCALAR_FIELD(event);
+ COMPARE_NODE_FIELD(funcname);
+ COMPARE_STRING_FIELD(variable);
+ COMPARE_NODE_FIELD(cmdlist);
+
+ 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);
***************
*** 2871,2876 **** equal(const void *a, const void *b)
--- 2892,2903 ----
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;
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 53,60 ****
--- 53,62 ----
#include "catalog/index.h"
#include "catalog/namespace.h"
+ #include "catalog/pg_event_trigger.h"
#include "catalog/pg_trigger.h"
#include "commands/defrem.h"
+ #include "commands/event_trigger.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/gramparse.h"
***************
*** 194,199 **** static void processCASbits(int cas_bits, int location, const char *constrType,
--- 196,202 ----
}
%type stmt schema_stmt
+ AlterEventTrigStmt
AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
***************
*** 207,218 **** static void processCASbits(int cas_bits, int location, const char *constrType,
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
! CreateAssertStmt CreateTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
! DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
! DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
LockStmt NotifyStmt ExplainableStmt PreparableStmt
--- 210,221 ----
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
! CreateAssertStmt CreateTrigStmt CreateEventTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
! DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt
! DropRoleStmt DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
LockStmt NotifyStmt ExplainableStmt PreparableStmt
***************
*** 264,272 **** static void processCASbits(int cas_bits, int location, const char *constrType,
%type TriggerForSpec TriggerForType
%type TriggerActionTime
! %type TriggerEvents TriggerOneEvent
%type TriggerFuncArg
%type TriggerWhen
%type copy_file_name
database_name access_method_clause access_method attr_name
--- 267,277 ----
%type TriggerForSpec TriggerForType
%type TriggerActionTime
! %type TriggerEvents TriggerOneEvent trigger_command_list
%type TriggerFuncArg
%type TriggerWhen
+ %type event_trigger_variable
+ %type event_name trigger_command enable_trigger
%type copy_file_name
database_name access_method_clause access_method attr_name
***************
*** 505,511 **** 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
EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
EXTENSION EXTERNAL EXTRACT
--- 510,516 ----
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 EVENT EXCEPT
EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
EXTENSION EXTERNAL EXTRACT
***************
*** 674,680 **** stmtmulti: stmtmulti ';' stmt
;
stmt :
! AlterDatabaseStmt
| AlterDatabaseSetStmt
| AlterDefaultPrivilegesStmt
| AlterDomainStmt
--- 679,686 ----
;
stmt :
! AlterEventTrigStmt
! | AlterDatabaseStmt
| AlterDatabaseSetStmt
| AlterDefaultPrivilegesStmt
| AlterDomainStmt
***************
*** 725,730 **** stmt :
--- 731,737 ----
| CreateStmt
| CreateTableSpaceStmt
| CreateTrigStmt
+ | CreateEventTrigStmt
| CreateRoleStmt
| CreateUserStmt
| CreateUserMappingStmt
***************
*** 4285,4290 **** DropTrigStmt:
--- 4292,4402 ----
/*****************************************************************************
*
* QUERIES :
+ * CREATE EVENT TRIGGER ...
+ * DROP EVENT TRIGGER ...
+ * ALTER EVENT TRIGGER ...
+ *
+ *****************************************************************************/
+
+ CreateEventTrigStmt:
+ CREATE EVENT TRIGGER name ON event_name
+ EXECUTE PROCEDURE func_name '(' ')'
+ {
+ CreateEventTrigStmt *n = makeNode(CreateEventTrigStmt);
+ n->trigname = $4;
+ n->event = $6;
+ n->funcname = $9;
+ n->variable = NULL;
+ $$ = (Node *)n;
+ }
+ | CREATE EVENT TRIGGER name ON event_name
+ WHEN event_trigger_variable IN_P '(' trigger_command_list ')'
+ EXECUTE PROCEDURE func_name '(' ')'
+ {
+ CreateEventTrigStmt *n = makeNode(CreateEventTrigStmt);
+ n->trigname = $4;
+ n->event = $6;
+ n->variable = $8;
+ n->cmdlist = $11;
+ n->funcname = $15;
+ $$ = (Node *)n;
+ }
+ ;
+
+ event_name:
+ IDENT
+ {
+ $$ = parse_event_name($1);
+ }
+ ;
+
+ event_trigger_variable:
+ IDENT
+ {
+ if (strcmp($1, "tag") == 0)
+ $$ = "TAG";
+ /*
+ * We aim to support more variables here, but as of now only the current
+ * command tag is supported.
+ *
+
+ else if (strcmp($1, "toplevel") == 0)
+ $$ = "toplevel";
+ */
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unrecognized event variable \"%s\"", $1),
+ parser_errposition(@1)));
+ }
+ ;
+
+ /*
+ * that will get matched against what CreateCommandTag returns
+ *
+ * we don't support Command Triggers on every possible command that PostgreSQL
+ * supports, this list should match with the implementation.
+ */
+ trigger_command_list:
+ trigger_command { $$ = list_make1_int($1); }
+ | trigger_command_list ',' trigger_command { $$ = lappend_int($1, $3); }
+ ;
+
+
+ trigger_command:
+ SCONST
+ {
+ TrigEventCommand cmdtag = parse_event_tag($1, true);
+ if (cmdtag == ETC_UNKNOWN)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unrecognized command \"%s\"", $1),
+ parser_errposition(@1)));
+ $$ = cmdtag;
+ }
+ ;
+
+
+ AlterEventTrigStmt:
+ ALTER EVENT TRIGGER name enable_trigger
+ {
+ AlterEventTrigStmt *n = makeNode(AlterEventTrigStmt);
+ n->trigname = $4;
+ n->tgenabled = $5;
+ $$ = (Node *) n;
+ }
+ ;
+
+ enable_trigger:
+ ENABLE_P { $$ = 'O'; }
+ | ENABLE_P REPLICA { $$ = 'R'; }
+ | ENABLE_P ALWAYS { $$ = 'A'; }
+ | DISABLE_P { $$ = 'D'; }
+ ;
+
+ /*****************************************************************************
+ *
+ * QUERIES :
* CREATE ASSERTION ...
* DROP ASSERTION ...
*
***************
*** 4868,4873 **** drop_type: TABLE { $$ = OBJECT_TABLE; }
--- 4980,4986 ----
| 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; }
***************
*** 6843,6848 **** RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
--- 6956,6969 ----
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);
***************
*** 7322,7327 **** AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId
--- 7443,7456 ----
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;
+ }
;
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 33,38 ****
--- 33,39 ----
#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"
***************
*** 59,64 ****
--- 60,66 ----
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/guc.h"
+ #include "utils/lsyscache.h"
#include "utils/syscache.h"
***************
*** 183,188 **** check_xact_readonly(Node *parsetree)
--- 185,192 ----
case T_CommentStmt:
case T_DefineStmt:
case T_CreateCastStmt:
+ case T_CreateEventTrigStmt:
+ case T_AlterEventTrigStmt:
case T_CreateConversionStmt:
case T_CreatedbStmt:
case T_CreateDomainStmt:
***************
*** 344,354 **** standard_ProcessUtility(Node *parsetree,
--- 348,363 ----
DestReceiver *dest,
char *completionTag)
{
+ EventContextData evt;
+
check_xact_readonly(parsetree);
if (completionTag)
completionTag[0] = '\0';
+ /* Event Trigger support for ddl_command_start */
+ InitEventContext(&evt, parsetree);
+
switch (nodeTag(parsetree))
{
/*
***************
*** 500,505 **** standard_ProcessUtility(Node *parsetree,
--- 509,516 ----
* relation and attribute manipulation
*/
case T_CreateSchemaStmt:
+ evt.objecttype = OBJECT_SCHEMA;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
CreateSchemaCommand((CreateSchemaStmt *) parsetree,
queryString);
break;
***************
*** 509,519 **** standard_ProcessUtility(Node *parsetree,
{
List *stmts;
ListCell *l;
! Oid relOid;
/* Run parse analysis ... */
! stmts = transformCreateStmt((CreateStmt *) parsetree,
! queryString);
/* ... and do it */
foreach(l, stmts)
--- 520,537 ----
{
List *stmts;
ListCell *l;
! Oid relOid = InvalidOid;
! CreateStmt *stmt = (CreateStmt *) parsetree;
!
! /* possibly run event triggers */
! if (nodeTag(parsetree) == T_CreateStmt)
! evt.objecttype = OBJECT_TABLE;
! else
! evt.objecttype = OBJECT_FOREIGN_TABLE;
! ExecEventTriggers(&evt, EVT_DDLCommandStart);
/* Run parse analysis ... */
! stmts = transformCreateStmt(stmt, queryString);
/* ... and do it */
foreach(l, stmts)
***************
*** 589,634 **** standard_ProcessUtility(Node *parsetree,
--- 607,675 ----
break;
case T_CreateExtensionStmt:
+ evt.objecttype = OBJECT_EXTENSION;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
CreateExtension((CreateExtensionStmt *) parsetree);
break;
case T_AlterExtensionStmt:
+ evt.objecttype = OBJECT_EXTENSION;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
ExecAlterExtensionStmt((AlterExtensionStmt *) parsetree);
break;
case T_AlterExtensionContentsStmt:
+ evt.objecttype = OBJECT_EXTENSION;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
ExecAlterExtensionContentsStmt((AlterExtensionContentsStmt *) parsetree);
break;
case T_CreateFdwStmt:
+ evt.objecttype = OBJECT_FDW;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
CreateForeignDataWrapper((CreateFdwStmt *) parsetree);
break;
case T_AlterFdwStmt:
+ evt.objecttype = OBJECT_FDW;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
AlterForeignDataWrapper((AlterFdwStmt *) parsetree);
break;
case T_CreateForeignServerStmt:
+ evt.objecttype = OBJECT_FOREIGN_SERVER;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
CreateForeignServer((CreateForeignServerStmt *) parsetree);
break;
case T_AlterForeignServerStmt:
+ evt.objecttype = OBJECT_FOREIGN_SERVER;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
AlterForeignServer((AlterForeignServerStmt *) parsetree);
break;
case T_CreateUserMappingStmt:
+ /* there's no OBJECT_ for user mappings */
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
CreateUserMapping((CreateUserMappingStmt *) parsetree);
break;
case T_AlterUserMappingStmt:
+ /* there's no OBJECT_ for user mappings */
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
AlterUserMapping((AlterUserMappingStmt *) parsetree);
break;
case T_DropUserMappingStmt:
+ /* there's no OBJECT_ for user mappings */
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
RemoveUserMapping((DropUserMappingStmt *) parsetree);
break;
case T_DropStmt:
+ evt.objecttype = ((DropStmt *) parsetree)->removeType;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
+
switch (((DropStmt *) parsetree)->removeType)
{
case OBJECT_INDEX:
***************
*** 692,705 **** standard_ProcessUtility(Node *parsetree,
--- 733,752 ----
* schema
*/
case T_RenameStmt:
+ evt.objecttype = ((RenameStmt *) parsetree)->renameType;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
ExecRenameStmt((RenameStmt *) parsetree);
break;
case T_AlterObjectSchemaStmt:
+ evt.objecttype = ((AlterObjectSchemaStmt *) parsetree)->objectType;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
ExecAlterObjectSchemaStmt((AlterObjectSchemaStmt *) parsetree);
break;
case T_AlterOwnerStmt:
+ evt.objecttype = ((AlterOwnerStmt *) parsetree)->objectType;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree);
break;
***************
*** 711,716 **** standard_ProcessUtility(Node *parsetree,
--- 758,767 ----
ListCell *l;
LOCKMODE lockmode;
+ /* run ddl_command_start event triggers, if any */
+ evt.objecttype = OBJECT_TABLE;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
+
/*
* Figure out lock mode, and acquire lock. This also does
* basic permissions checks, so that we won't wait for a lock
***************
*** 762,767 **** standard_ProcessUtility(Node *parsetree,
--- 813,822 ----
{
AlterDomainStmt *stmt = (AlterDomainStmt *) parsetree;
+ /* run ddl_command_start event triggers, if any */
+ evt.objecttype = OBJECT_DOMAIN;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
+
/*
* Some or all of these functions are recursive to cover
* inherited things, so permission checks are done there.
***************
*** 826,831 **** standard_ProcessUtility(Node *parsetree,
--- 881,889 ----
{
DefineStmt *stmt = (DefineStmt *) parsetree;
+ evt.objecttype = stmt->kind;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
+
switch (stmt->kind)
{
case OBJECT_AGGREGATE:
***************
*** 872,886 **** standard_ProcessUtility(Node *parsetree,
--- 930,950 ----
{
CompositeTypeStmt *stmt = (CompositeTypeStmt *) parsetree;
+ evt.objecttype = OBJECT_TYPE;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
DefineCompositeType(stmt->typevar, stmt->coldeflist);
}
break;
case T_CreateEnumStmt: /* CREATE TYPE AS ENUM */
+ evt.objecttype = OBJECT_TYPE;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
DefineEnum((CreateEnumStmt *) parsetree);
break;
case T_CreateRangeStmt: /* CREATE TYPE AS RANGE */
+ evt.objecttype = OBJECT_TYPE;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
DefineRange((CreateRangeStmt *) parsetree);
break;
***************
*** 891,909 **** standard_ProcessUtility(Node *parsetree,
--- 955,981 ----
* with enum OID values getting into indexes and then having their
* defining pg_enum entries go away.
*/
+ evt.objecttype = OBJECT_TYPE;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
PreventTransactionChain(isTopLevel, "ALTER TYPE ... ADD");
AlterEnum((AlterEnumStmt *) parsetree);
break;
case T_ViewStmt: /* CREATE VIEW */
+ evt.objecttype = OBJECT_VIEW;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
DefineView((ViewStmt *) parsetree, queryString);
break;
case T_CreateFunctionStmt: /* CREATE FUNCTION */
+ evt.objecttype = OBJECT_FUNCTION;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
CreateFunction((CreateFunctionStmt *) parsetree, queryString);
break;
case T_AlterFunctionStmt: /* ALTER FUNCTION */
+ evt.objecttype = OBJECT_FUNCTION;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
AlterFunction((AlterFunctionStmt *) parsetree);
break;
***************
*** 911,916 **** standard_ProcessUtility(Node *parsetree,
--- 983,991 ----
{
IndexStmt *stmt = (IndexStmt *) parsetree;
+ evt.objecttype = OBJECT_INDEX;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
+
if (stmt->concurrent)
PreventTransactionChain(isTopLevel,
"CREATE INDEX CONCURRENTLY");
***************
*** 945,958 **** standard_ProcessUtility(Node *parsetree,
--- 1020,1039 ----
break;
case T_RuleStmt: /* CREATE RULE */
+ evt.objecttype = OBJECT_RULE;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
DefineRule((RuleStmt *) parsetree, queryString);
break;
case T_CreateSeqStmt:
+ evt.objecttype = OBJECT_SEQUENCE;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
DefineSequence((CreateSeqStmt *) parsetree);
break;
case T_AlterSeqStmt:
+ evt.objecttype = OBJECT_SEQUENCE;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
AlterSequence((AlterSeqStmt *) parsetree);
break;
***************
*** 1043,1048 **** standard_ProcessUtility(Node *parsetree,
--- 1124,1133 ----
break;
case T_CreateTableAsStmt:
+ evt.objecttype = OBJECT_TABLE;
+ if (((CreateTableAsStmt *) parsetree)->is_select_into)
+ evt.operation = "CREATE";
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
ExecCreateTableAs((CreateTableAsStmt *) parsetree,
queryString, params, completionTag);
break;
***************
*** 1066,1076 **** standard_ProcessUtility(Node *parsetree,
--- 1151,1173 ----
break;
case T_CreateTrigStmt:
+ evt.objecttype = OBJECT_TRIGGER;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
(void) CreateTrigger((CreateTrigStmt *) parsetree, queryString,
InvalidOid, InvalidOid, false);
break;
+ case T_CreateEventTrigStmt:
+ CreateEventTrigger((CreateEventTrigStmt *) parsetree, queryString);
+ break;
+
+ case T_AlterEventTrigStmt:
+ (void) AlterEventTrigger((AlterEventTrigStmt *) parsetree);
+ break;
+
case T_CreatePLangStmt:
+ evt.objecttype = OBJECT_LANGUAGE;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
CreateProceduralLanguage((CreatePLangStmt *) parsetree);
break;
***************
*** 1078,1083 **** standard_ProcessUtility(Node *parsetree,
--- 1175,1182 ----
* ******************************** DOMAIN statements ****
*/
case T_CreateDomainStmt:
+ evt.objecttype = OBJECT_DOMAIN;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
DefineDomain((CreateDomainStmt *) parsetree);
break;
***************
*** 1176,1205 **** standard_ProcessUtility(Node *parsetree,
--- 1275,1318 ----
break;
case T_CreateConversionStmt:
+ evt.objecttype = OBJECT_CONVERSION;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
CreateConversionCommand((CreateConversionStmt *) parsetree);
break;
case T_CreateCastStmt:
+ evt.objecttype = OBJECT_CAST;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
CreateCast((CreateCastStmt *) parsetree);
break;
case T_CreateOpClassStmt:
+ evt.objecttype = OBJECT_OPCLASS;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
DefineOpClass((CreateOpClassStmt *) parsetree);
break;
case T_CreateOpFamilyStmt:
+ evt.objecttype = OBJECT_OPFAMILY;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
DefineOpFamily((CreateOpFamilyStmt *) parsetree);
break;
case T_AlterOpFamilyStmt:
+ evt.objecttype = OBJECT_OPFAMILY;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
AlterOpFamily((AlterOpFamilyStmt *) parsetree);
break;
case T_AlterTSDictionaryStmt:
+ evt.objecttype = OBJECT_TSDICTIONARY;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
AlterTSDictionary((AlterTSDictionaryStmt *) parsetree);
break;
case T_AlterTSConfigurationStmt:
+ evt.objecttype = OBJECT_TSCONFIGURATION;
+ ExecEventTriggers(&evt, EVT_DDLCommandStart);
AlterTSConfiguration((AlterTSConfigurationStmt *) parsetree);
break;
***************
*** 1486,1491 **** AlterObjectTypeCommandTag(ObjectType objtype)
--- 1599,1607 ----
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;
***************
*** 1755,1760 **** CreateCommandTag(Node *parsetree)
--- 1871,1879 ----
case OBJECT_TRIGGER:
tag = "DROP TRIGGER";
break;
+ case OBJECT_EVENT_TRIGGER:
+ tag = "DROP EVENT TRIGGER";
+ break;
case OBJECT_RULE:
tag = "DROP RULE";
break;
***************
*** 2008,2013 **** CreateCommandTag(Node *parsetree)
--- 2127,2140 ----
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;
***************
*** 2503,2508 **** GetCommandLogLevel(Node *parsetree)
--- 2630,2643 ----
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;
*** a/src/backend/utils/adt/pseudotypes.c
--- b/src/backend/utils/adt/pseudotypes.c
***************
*** 293,298 **** trigger_out(PG_FUNCTION_ARGS)
--- 293,325 ----
/*
+ * 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
*** a/src/backend/utils/cache/Makefile
--- b/src/backend/utils/cache/Makefile
***************
*** 12,18 **** subdir = src/backend/utils/cache
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
! OBJS = attoptcache.o catcache.o inval.o plancache.o relcache.o relmapper.o \
! spccache.o syscache.o lsyscache.o typcache.o ts_cache.o
include $(top_srcdir)/src/backend/common.mk
--- 12,19 ----
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
! OBJS = attoptcache.o catcache.o evtcache.o inval.o plancache.o \
! relcache.o relmapper.o spccache.o syscache.o lsyscache.o \
! typcache.o ts_cache.o
include $(top_srcdir)/src/backend/common.mk
*** /dev/null
--- b/src/backend/utils/cache/evtcache.c
***************
*** 0 ****
--- 1,1541 ----
+ /*-------------------------------------------------------------------------
+ *
+ * evtcache.c
+ * Per Command Event Trigger cache management.
+ *
+ * Event trigger command cache is maintained separately from the event name
+ * catalog cache.
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/utils/cache/evtcache.c
+ *
+ *-------------------------------------------------------------------------
+ */
+ #include "postgres.h"
+
+ #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"
+ #include "catalog/indexing.h"
+ #include "catalog/pg_event_trigger.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_trigger.h"
+ #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"
+ #include "utils/rel.h"
+ #include "utils/tqual.h"
+ #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,
+ OBJECT_CAST
+ },
+ {
+ ETC_CreateCollation,
+ "CREATE COLLATION",
+ T_DefineStmt,
+ OBJECT_COLLATION
+ },
+ {
+ ETC_CreateConversion,
+ "CREATE CONVERSION",
+ T_CreateConversionStmt,
+ OBJECT_CONVERSION
+ },
+ {
+ ETC_CreateDomain,
+ "CREATE DOMAIN",
+ T_CreateDomainStmt,
+ OBJECT_DOMAIN
+ },
+ {
+ ETC_CreateExtension,
+ "CREATE EXTENSION",
+ T_CreateExtensionStmt,
+ OBJECT_EXTENSION
+ },
+ {
+ ETC_CreateForeignDataWrapper,
+ "CREATE FOREIGN DATA WRAPPER",
+ T_CreateFdwStmt,
+ OBJECT_FDW
+ },
+ {
+ ETC_CreateForeignTable,
+ "CREATE FOREIGN TABLE",
+ T_CreateForeignTableStmt,
+ OBJECT_FOREIGN_TABLE
+ },
+ {
+ ETC_CreateFunction,
+ "CREATE FUNCTION",
+ T_CreateFunctionStmt,
+ OBJECT_FUNCTION
+ },
+ {
+ ETC_CreateIndex,
+ "CREATE INDEX",
+ T_IndexStmt,
+ OBJECT_INDEX
+ },
+ {
+ ETC_CreateLanguage,
+ "CREATE LANGUAGE",
+ T_CreatePLangStmt,
+ OBJECT_LANGUAGE
+ },
+ {
+ ETC_CreateOperator,
+ "CREATE OPERATOR",
+ T_DefineStmt,
+ OBJECT_OPERATOR
+ },
+ {
+ ETC_CreateOperatorClass,
+ "CREATE OPERATOR CLASS",
+ T_CreateOpClassStmt,
+ OBJECT_OPCLASS
+ },
+ {
+ ETC_CreateOperatorFamily,
+ "CREATE OPERATOR FAMILY",
+ T_CreateOpFamilyStmt,
+ OBJECT_OPFAMILY
+ },
+ {
+ ETC_CreateRule,
+ "CREATE RULE",
+ T_RuleStmt,
+ -1
+ },
+ {
+ ETC_CreateSchema,
+ "CREATE SCHEMA",
+ T_CreateSchemaStmt,
+ OBJECT_SCHEMA
+ },
+ {
+ ETC_CreateSequence,
+ "CREATE SEQUENCE",
+ T_CreateSeqStmt,
+ OBJECT_SEQUENCE
+ },
+ {
+ ETC_CreateServer,
+ "CREATE SERVER",
+ T_CreateForeignServerStmt,
+ OBJECT_FOREIGN_SERVER
+ },
+ {
+ ETC_CreateTable,
+ "CREATE TABLE",
+ T_CreateStmt,
+ OBJECT_TABLE
+ },
+ {
+ ETC_CreateTableAs,
+ "CREATE TABLE AS",
+ T_CreateTableAsStmt,
+ OBJECT_TABLE
+ },
+ {
+ ETC_SelectInto,
+ "SELECT INTO",
+ T_CreateTableAsStmt,
+ OBJECT_TABLE
+ },
+ {
+ 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,
+ OBJECT_TRIGGER
+ },
+ {
+ ETC_CreateType,
+ "CREATE TYPE",
+ T_DefineStmt,
+ OBJECT_TYPE
+ },
+ {
+ ETC_CreateType,
+ "CREATE TYPE",
+ T_CompositeTypeStmt,
+ OBJECT_TYPE
+ },
+ {
+ ETC_CreateType,
+ "CREATE TYPE",
+ T_CreateEnumStmt,
+ OBJECT_TYPE
+ },
+ {
+ ETC_CreateType,
+ "CREATE TYPE",
+ T_CreateRangeStmt,
+ OBJECT_TYPE
+ },
+ {
+ ETC_CreateUserMapping,
+ "CREATE USER MAPPING",
+ T_CreateUserMappingStmt,
+ -1
+ },
+ {
+ ETC_CreateView,
+ "CREATE VIEW",
+ T_ViewStmt,
+ OBJECT_VIEW
+ },
+ {
+ ETC_AlterTable,
+ "ALTER TABLE",
+ T_AlterTableStmt,
+ OBJECT_TABLE
+ },
+ {
+ 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_AlterSequence,
+ "ALTER SEQUENCE",
+ T_AlterSeqStmt,
+ OBJECT_SEQUENCE
+ },
+ {
+ ETC_AlterUserMapping,
+ "ALTER USER MAPPING",
+ T_CreateUserMappingStmt,
+ -1
+ },
+ {
+ ETC_AlterFunction,
+ "ALTER FUNCTION",
+ T_AlterFunctionStmt,
+ OBJECT_FUNCTION
+ },
+ {
+ ETC_AlterDomain,
+ "ALTER DOMAIN",
+ T_AlterDomainStmt,
+ OBJECT_DOMAIN
+ },
+ /* ALTER