diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 2b46b75..b100a42 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -99,11 +99,6 @@ - pg_event_trigger - event triggers - - - pg_constraint check constraints, unique constraints, primary key constraints, foreign key constraints @@ -149,6 +144,11 @@ + pg_event_trigger + event triggers + + + pg_extension installed extensions @@ -1871,6 +1871,7 @@ The catalog pg_event_trigger stores event triggers. + See for more information. @@ -1891,23 +1892,28 @@ evtnamename - Trigger name (unique) + Trigger name (must be unique) evtevent name - The event this trigger fires for. + Identifies the event for which this trigger fires + + + + evtowner + oid + pg_authid.oid + Owner of the event trigger evtfoid oid pg_proc.oid - - The OID of the function called by this event trigger. - + The function to be called @@ -1928,7 +1934,10 @@ evttags text[] - 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/Perl event 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 changes PL/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/Python event 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 name CREATE 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 name name - 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 name RENAME TO */ + { + ETC_AlterAggregate, + "ALTER AGGREGATE", + T_RenameStmt, + OBJECT_AGGREGATE + }, + { + ETC_AlterType, + "ALTER TYPE", + T_RenameStmt, + OBJECT_ATTRIBUTE + }, + { + ETC_AlterCast, + "ALTER CAST", + T_RenameStmt, + OBJECT_CAST + }, + { + ETC_AlterCollation, + "ALTER COLLATION", + T_RenameStmt, + OBJECT_COLLATION + }, + { + ETC_AlterTable, + "ALTER TABLE", + T_RenameStmt, + OBJECT_COLUMN + }, + { + ETC_AlterTable, + "ALTER TABLE", + T_RenameStmt, + OBJECT_CONSTRAINT + }, + { + ETC_AlterConversion, + "ALTER CONVERSION", + T_RenameStmt, + OBJECT_CONVERSION + }, + { + ETC_AlterDomain, + "ALTER DOMAIN", + OBJECT_DOMAIN, + T_RenameStmt + }, + { + ETC_AlterExtension, + "ALTER EXTENSION", + T_RenameStmt, + OBJECT_EXTENSION + }, + { + ETC_AlterForeignDataWrapper, + "ALTER FOREIGN DATA WRAPPER", + OBJECT_FDW, + T_RenameStmt + }, + { + ETC_AlterServer, + "ALTER SERVER", + T_RenameStmt, + OBJECT_FOREIGN_SERVER + }, + { + ETC_AlterForeignTable, + "ALTER FOREIGN TABLE", + T_RenameStmt, + OBJECT_FOREIGN_TABLE + }, + { + ETC_AlterFunction, + "ALTER FUNCTION", + T_RenameStmt, + OBJECT_FUNCTION + }, + { + ETC_AlterIndex, + "ALTER INDEX", + T_RenameStmt, + OBJECT_INDEX + }, + { + ETC_AlterLanguage, + "ALTER LANGUAGE", + T_RenameStmt, + OBJECT_LANGUAGE + }, + { + ETC_AlterOperator, + "ALTER OPERATOR", + T_RenameStmt, + OBJECT_OPERATOR + }, + { + ETC_AlterOperatorClass, + "ALTER OPERATOR CLASS", + T_RenameStmt, + OBJECT_OPCLASS + }, + { + ETC_AlterOperatorFamily, + "ALTER OPERATOR FAMILY", + T_RenameStmt, + OBJECT_OPFAMILY + }, + { + ETC_AlterRule, + "ALTER RULE", + T_RenameStmt, + OBJECT_RULE + }, + { + ETC_AlterSchema, + "ALTER SCHEMA", + T_RenameStmt, + OBJECT_SCHEMA + }, + { + ETC_AlterSequence, + "ALTER SEQUENCE", + T_RenameStmt, + OBJECT_SEQUENCE + }, + { + ETC_AlterTable, + "ALTER TABLE", + T_RenameStmt, + OBJECT_TABLE + }, + { + ETC_AlterTrigger, + "ALTER TRIGGER", + T_RenameStmt, + OBJECT_TRIGGER + }, + { + ETC_AlterTextSearchParser, + "ALTER TEXT SEARCH PARSER", + T_RenameStmt, + OBJECT_TSPARSER + }, + { + ETC_AlterTextSearchConfiguration, + "ALTER TEXT SEARCH CONFIGURATION", + T_RenameStmt, + OBJECT_TSCONFIGURATION + }, + { + ETC_AlterTextSearchDictionary, + "ALTER TEXT SEARCH DICTIONARY", + T_RenameStmt, + OBJECT_TSDICTIONARY + }, + { + ETC_AlterTextSearchTemplate, + "ALTER TEXT SEARCH TEMPLATE", + T_RenameStmt, + OBJECT_TSTEMPLATE + }, + { + ETC_AlterType, + "ALTER TYPE", + T_RenameStmt, + OBJECT_TYPE + }, + { + ETC_AlterView, + "ALTER VIEW", + T_RenameStmt, + OBJECT_VIEW + }, + /* ALTER name SET SCHEMA */ + { + ETC_AlterAggregate, + "ALTER AGGREGATE", + T_AlterObjectSchemaStmt, + OBJECT_AGGREGATE + }, + { + ETC_AlterCast, + "ALTER CAST", + T_AlterObjectSchemaStmt, + OBJECT_CAST + }, + { + ETC_AlterCollation, + "ALTER COLLATION", + T_AlterObjectSchemaStmt, + OBJECT_COLLATION + }, + { + ETC_AlterConversion, + "ALTER CONVERSION", + T_AlterObjectSchemaStmt, + OBJECT_CONVERSION + }, + { + ETC_AlterDomain, + "ALTER DOMAIN", + T_AlterObjectSchemaStmt, + OBJECT_DOMAIN + }, + { + ETC_AlterExtension, + "ALTER EXTENSION", + T_AlterObjectSchemaStmt, + OBJECT_EXTENSION + }, + { + ETC_AlterForeignDataWrapper, + "ALTER FOREIGN DATA WRAPPER", + T_AlterObjectSchemaStmt, + OBJECT_FDW + }, + { + ETC_AlterForeignTable, + "ALTER FOREIGN TABLE", + T_AlterObjectSchemaStmt, + OBJECT_FOREIGN_TABLE + }, + { + ETC_AlterFunction, + "ALTER FUNCTION", + T_AlterObjectSchemaStmt, + OBJECT_FUNCTION + }, + { + ETC_AlterIndex, + "ALTER CAST", + T_AlterObjectSchemaStmt, + OBJECT_INDEX + }, + { + ETC_AlterLanguage, + "ALTER LANGUAGE", + T_AlterObjectSchemaStmt, + OBJECT_LANGUAGE + }, + { + ETC_AlterOperator, + "ALTER OPERATOR", + T_AlterObjectSchemaStmt, + OBJECT_OPERATOR + }, + { + ETC_AlterOperatorClass, + "ALTER OPERATOR CLASS", + T_AlterObjectSchemaStmt, + OBJECT_OPCLASS + }, + { + ETC_AlterOperatorFamily, + "ALTER OPERATOR FAMILY", + T_AlterObjectSchemaStmt, + OBJECT_OPFAMILY + }, + { + ETC_AlterSchema, + "ALTER SCHEMA", + T_AlterObjectSchemaStmt, + OBJECT_SCHEMA + }, + { + ETC_AlterSequence, + "ALTER SEQUENCE", + T_AlterObjectSchemaStmt, + OBJECT_SEQUENCE + }, + { + ETC_AlterServer, + "ALTER SERVER", + T_AlterObjectSchemaStmt, + OBJECT_FOREIGN_SERVER + }, + { + ETC_AlterTable, + "ALTER TABLE", + T_AlterObjectSchemaStmt, + OBJECT_TABLE + }, + { + ETC_AlterTextSearchParser, + "ALTER TEXT SEARCH PARSER", + T_AlterObjectSchemaStmt, + OBJECT_TSPARSER + }, + { + ETC_AlterTextSearchConfiguration, + "ALTER TEXT SEARCH CONFIGURATION", + T_AlterObjectSchemaStmt, + OBJECT_TSCONFIGURATION + }, + { + ETC_AlterTextSearchDictionary, + "ALTER TEXT SEARCH DICTIONARY", + T_AlterObjectSchemaStmt, + OBJECT_TSDICTIONARY + }, + { + ETC_AlterTextSearchTemplate, + "ALTER TEXT SEARCH TEMPLATE", + T_AlterObjectSchemaStmt, + OBJECT_TSTEMPLATE + }, + { + ETC_AlterTrigger, + "ALTER TRIGGER", + T_AlterObjectSchemaStmt, + OBJECT_TRIGGER + }, + { + ETC_AlterType, + "ALTER TYPE", + T_AlterEnumStmt, + -1 + }, + { + ETC_AlterType, + "ALTER TYPE", + T_AlterObjectSchemaStmt, + OBJECT_ATTRIBUTE + }, + { + ETC_AlterType, + "ALTER TYPE", + T_AlterObjectSchemaStmt, + OBJECT_TYPE + }, + { + ETC_AlterView, + "ALTER VIEW", + T_AlterObjectSchemaStmt, + OBJECT_VIEW + }, + { + ETC_AlterTextSearchDictionary, + "ALTER TEXT SEARCH DICTIONARY", + T_AlterTSDictionaryStmt, + -1 + }, + /* ALTER name OWNER TO */ + { + ETC_AlterAggregate, + "ALTER AGGREGATE", + T_AlterOwnerStmt, + OBJECT_AGGREGATE + }, + { + ETC_AlterCast, + "ALTER CAST", + T_AlterOwnerStmt, + OBJECT_CAST + }, + { + ETC_AlterCollation, + "ALTER COLLATION", + T_AlterOwnerStmt, + OBJECT_COLLATION + }, + { + ETC_AlterConversion, + "ALTER CONVERSION", + T_AlterOwnerStmt, + OBJECT_CONVERSION + }, + { + ETC_AlterDomain, + "ALTER DOMAIN", + T_AlterOwnerStmt, + OBJECT_DOMAIN + }, + { + ETC_AlterExtension, + "ALTER EXTENSION", + T_AlterOwnerStmt, + OBJECT_EXTENSION + }, + { + ETC_AlterForeignDataWrapper, + "ALTER FOREIGN DATA WRAPPER", + T_AlterOwnerStmt, + OBJECT_FDW + }, + { + ETC_AlterForeignTable, + "ALTER FOREIGN TABLE", + T_AlterOwnerStmt, + OBJECT_FOREIGN_TABLE + }, + { + ETC_AlterFunction, + "ALTER FUNCTION", + T_AlterOwnerStmt, + OBJECT_FUNCTION + }, + { + ETC_AlterIndex, + "ALTER CAST", + T_AlterOwnerStmt, + OBJECT_INDEX + }, + { + ETC_AlterLanguage, + "command_start", + T_AlterOwnerStmt, + OBJECT_LANGUAGE + }, + { + ETC_AlterOperator, + "ALTER OPERATOR", + T_AlterOwnerStmt, + OBJECT_OPERATOR + }, + { + ETC_AlterOperatorClass, + "ALTER OPERATOR CLASS", + T_AlterOwnerStmt, + OBJECT_OPCLASS + }, + { + ETC_AlterOperatorFamily, + "ALTER OPERATOR FAMILY", + T_AlterOwnerStmt, + OBJECT_OPFAMILY + }, + { + ETC_AlterSchema, + "ALTER SCHEMA", + T_AlterOwnerStmt, + OBJECT_SCHEMA + }, + { + ETC_AlterSequence, + "ALTER SEQUENCE", + T_AlterOwnerStmt, + OBJECT_SEQUENCE + }, + { + ETC_AlterServer, + "ALTER SERVER", + T_AlterOwnerStmt, + OBJECT_FOREIGN_SERVER + }, + { + ETC_AlterTextSearchParser, + "ALTER TEXT SEARCH PARSER", + T_AlterOwnerStmt, + OBJECT_TSPARSER + }, + { + ETC_AlterTextSearchConfiguration, + "ALTER TEXT SEARCH CONFIGURATION", + T_AlterOwnerStmt, + OBJECT_TSCONFIGURATION + }, + { + ETC_AlterTextSearchDictionary, + "ALTER TEXT SEARCH DICTIONARY", + T_AlterOwnerStmt, + OBJECT_TSDICTIONARY + }, + { + ETC_AlterTextSearchTemplate, + "ALTER TEXT SEARCH TEMPLATE", + T_AlterOwnerStmt, + OBJECT_TSTEMPLATE + }, + { + ETC_AlterTrigger, + "ALTER TRIGGER", + T_AlterOwnerStmt, + OBJECT_TRIGGER + }, + { + ETC_AlterType, + "ALTER TYPE", + T_AlterOwnerStmt, + OBJECT_ATTRIBUTE + }, + { + ETC_AlterType, + "ALTER TYPE", + T_AlterOwnerStmt, + OBJECT_TYPE + }, + { + ETC_AlterView, + "ALTER VIEW", + T_AlterOwnerStmt, + OBJECT_VIEW + } +}; + +/* * Cache the event triggers in a format that's suitable to finding which * function to call at "hook" points in the code. The catalogs are not helpful * at search time, because we can't both edit a single catalog entry per each @@ -48,47 +1007,47 @@ * This cache is indexed by Event id then Event Command id (see * pg_event_trigger.h). It's containing a list of function oid. */ -static HTAB *EventCommandTriggerCache = NULL; +static HTAB *EventTriggerProcsCache = NULL; /* event and command form the lookup key, and must appear first */ typedef struct { TrigEvent event; TrigEventCommand command; -} EventCommandTriggerCacheKey; +} EventTriggerProcsCacheKey; /* entry for command event trigger lookup hashtable */ typedef struct { - EventCommandTriggerCacheKey key; /* lookup key, must be first */ + EventTriggerProcsCacheKey key; /* lookup key, must be first */ List *names; /* list of names of the triggers to call */ List *procs; /* list of triggers to call */ -} EventCommandTriggerCacheEntry; +} EventTriggerProcsCacheEntry; /* - * Add a new function to EventCommandTriggerCache for given command and event, + * Add a new function to EventTriggerProcsCache for given command and event, * creating a new hash table entry when necessary. * * Returns the new hash entry value. */ -static EventCommandTriggerCacheEntry * +static EventTriggerProcsCacheEntry * add_funcall_to_command_event(TrigEvent event, TrigEventCommand command, NameData evtname, Oid proc) { bool found; - EventCommandTriggerCacheKey key; - EventCommandTriggerCacheEntry *hresult; + EventTriggerProcsCacheKey key; + EventTriggerProcsCacheEntry *hresult; MemoryContext old = MemoryContextSwitchTo(CacheMemoryContext); memset(&key, 0, sizeof(key)); key.event = event; key.command = command; - hresult = (EventCommandTriggerCacheEntry *) - hash_search(EventCommandTriggerCache, (void *)&key, HASH_ENTER, &found); + hresult = (EventTriggerProcsCacheEntry *) + hash_search(EventTriggerProcsCache, (void *)&key, HASH_ENTER, &found); if (found) { @@ -113,10 +1072,10 @@ add_funcall_to_command_event(TrigEvent event, * The idea is that the code to fetch the list of functions to process gets as * simple as the following: * - * foreach(cell, EventCommandTriggerCache[TrigEventCommand][TrigEvent]) + * foreach(cell, EventTriggerProcsCache[TrigEventCommand][TrigEvent]) */ static void -BuildEventTriggerCache() +BuildEventTriggerCache(void) { HASHCTL info; Relation rel, irel; @@ -125,13 +1084,13 @@ BuildEventTriggerCache() /* build the new hash table */ MemSet(&info, 0, sizeof(info)); - info.keysize = sizeof(EventCommandTriggerCacheKey); - info.entrysize = sizeof(EventCommandTriggerCacheEntry); + info.keysize = sizeof(EventTriggerProcsCacheKey); + info.entrysize = sizeof(EventTriggerProcsCacheEntry); info.hash = tag_hash; info.hcxt = CacheMemoryContext; /* Create the hash table holding our cache */ - EventCommandTriggerCache = + EventTriggerProcsCache = hash_create("Event Trigger Command Cache", 1024, &info, @@ -144,8 +1103,9 @@ BuildEventTriggerCache() indexScan = index_beginscan(rel, irel, SnapshotNow, 0, 0); index_rescan(indexScan, NULL, 0, NULL, 0); - /* we use a full indexscan to guarantee that we see event triggers ordered - * by name, this way we only even have to append the trigger's function Oid + /* + * We use a full indexscan to guarantee that we see event triggers ordered + * by name. This way, we only even have to append the trigger's function Oid * to the target cache Oid list. */ while (HeapTupleIsValid(tuple = index_getnext(indexScan, ForwardScanDirection))) @@ -190,7 +1150,7 @@ BuildEventTriggerCache() /* event triggers created without WHEN clause are targetting all * commands (ANY command trigger) */ - add_funcall_to_command_event(event, E_ANY, name, proc); + add_funcall_to_command_event(event, ETC_ANY, name, proc); } else { @@ -232,8 +1192,8 @@ static void InvalidateEvtTriggerCommandCacheCallback(Datum arg, int cacheid, uint32 hashvalue) { - hash_destroy(EventCommandTriggerCache); - EventCommandTriggerCache = NULL; + hash_destroy(EventTriggerProcsCache); + EventTriggerProcsCache = NULL; } /* @@ -249,7 +1209,7 @@ InitEventTriggerCache(void) if (!CacheMemoryContext) CreateCacheMemoryContext(); - EventCommandTriggerCache = NULL; + EventTriggerProcsCache = NULL; /* Watch for invalidation events. */ CacheRegisterSyscacheCallback(EVENTTRIGGERNAME, @@ -265,30 +1225,30 @@ get_event_triggers(TrigEvent event, TrigEventCommand command) { EventCommandTriggers *triggers = (EventCommandTriggers *) palloc(sizeof(EventCommandTriggers)); - EventCommandTriggerCacheKey anykey, cmdkey; - EventCommandTriggerCacheEntry *any, *cmd; + EventTriggerProcsCacheKey anykey, cmdkey; + EventTriggerProcsCacheEntry *any, *cmd; triggers->event = event; triggers->command = command; triggers->procs = NIL; /* Find existing cache entry, if any. */ - if (!EventCommandTriggerCache) + if (!EventTriggerProcsCache) BuildEventTriggerCache(); /* ANY command triggers */ memset(&anykey, 0, sizeof(anykey)); anykey.event = event; - anykey.command = E_ANY; - any = (EventCommandTriggerCacheEntry *) - hash_search(EventCommandTriggerCache, (void *)&anykey, HASH_FIND, NULL); + anykey.command = ETC_ANY; + any = (EventTriggerProcsCacheEntry *) + hash_search(EventTriggerProcsCache, (void *)&anykey, HASH_FIND, NULL); /* Specific command triggers */ memset(&cmdkey, 0, sizeof(cmdkey)); cmdkey.event = event; cmdkey.command = command; - cmd = (EventCommandTriggerCacheEntry *) - hash_search(EventCommandTriggerCache, (void *)&cmdkey, HASH_FIND, NULL); + cmd = (EventTriggerProcsCacheEntry *) + hash_search(EventTriggerProcsCache, (void *)&cmdkey, HASH_FIND, NULL); if (any == NULL && cmd == NULL) return triggers; @@ -327,7 +1287,8 @@ get_event_triggers(TrigEvent event, TrigEventCommand command) lc_any_procs = lnext(lc_any_procs); } - /* now append as many elements from CMD list named before next ANY + /* + * now append as many elements from CMD list named before next ANY * entry */ do @@ -352,3 +1313,253 @@ get_event_triggers(TrigEvent event, TrigEventCommand command) } return triggers; } + +char * +event_to_string(TrigEvent event) +{ + switch (event) + { + case EVT_CommandStart: + return "command_start"; + } + return NULL; +} + +TrigEvent +parse_event_name(char *event) +{ + if (pg_strcasecmp(event, "command_start") == 0) + return EVT_CommandStart; + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("unrecognized event \"%s\"", event))); + + /* make compiler happy */ + return -1; +} + +TrigEventCommand +parse_event_tag(char *cmdtag, bool noerror) +{ + char *uctag; + NameData key; + EventTriggerCommandTagsEntry *entry; + + if (EventTriggerCommandTagsCache == NULL) + { + int index; + HASHCTL info; + MemoryContext old = MemoryContextSwitchTo(CacheMemoryContext); + + /* build the new hash table */ + MemSet(&info, 0, sizeof(info)); + info.keysize = NAMEDATALEN; + info.entrysize = sizeof(EventTriggerCommandTagsEntry); + info.hash = tag_hash; + info.hcxt = CacheMemoryContext; + + /* Create the hash table holding our cache */ + EventTriggerCommandTagsCache = + hash_create("Event Trigger Command Tags Cache", + 1024, + &info, + HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT); + + for (index = 0; index < lengthof(EventTriggerCommandTags); index++) + { + bool found; + char *tag = EventTriggerCommandTags[index].tag; + NameData key; + EventTriggerCommandTagsEntry *hresult; + + memset(&key, 0, NAMEDATALEN); + key = *(DatumGetName(DirectFunctionCall1(namein, + CStringGetDatum(tag)))); + + hresult = (EventTriggerCommandTagsEntry *) + hash_search(EventTriggerCommandTagsCache, + (void *)&key, HASH_ENTER, &found); + + hresult->command = EventTriggerCommandTags[index].command; + } + MemoryContextSwitchTo(old); + } + + memset(&key, 0, NAMEDATALEN); + uctag = str_toupper(cmdtag, strlen(cmdtag), DEFAULT_COLLATION_OID); + key = *(DatumGetName( + DirectFunctionCall1(namein, CStringGetDatum(uctag)))); + + entry = (EventTriggerCommandTagsEntry *) + hash_search(EventTriggerCommandTagsCache, + (void *)&key, HASH_FIND, NULL); + + if (entry == NULL) + { + if (!noerror) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("unrecognized command \"%s\"", cmdtag))); + return ETC_UNKNOWN; + } + return entry->command; +} + +char * +command_to_string(TrigEventCommand command) +{ + int index; + for (index = 0; index < lengthof(EventTriggerCommandTags); index++) + { + if (command == EventTriggerCommandTags[index].command) + return EventTriggerCommandTags[index].tag; + } + return NULL; +} + +/* + * Cache lookup support for ProcessUtility + */ +TrigEventCommand +get_command_from_nodetag(NodeTag node, ObjectType type, bool noerror) +{ + EventTriggerCommandNodeKey key; + EventTriggerCommandNodeEntry *entry; + + if (EventTriggerCommandNodeCache == NULL) + { + int index; + HASHCTL info; + MemoryContext old = MemoryContextSwitchTo(CacheMemoryContext); + + /* build the new hash table */ + MemSet(&info, 0, sizeof(info)); + info.keysize = sizeof(EventTriggerCommandNodeKey); + info.entrysize = sizeof(EventTriggerCommandNodeEntry); + info.hash = tag_hash; + info.hcxt = CacheMemoryContext; + + /* Create the hash table holding our cache */ + EventTriggerCommandNodeCache = + hash_create("Event Trigger Command Node Cache", + 1024, + &info, + HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT); + + for (index = 0; index < lengthof(EventTriggerCommandTags); index++) + { + bool found; + EventTriggerCommandNodeKey key; + EventTriggerCommandNodeEntry *hresult; + + memset(&key, 0, sizeof(key)); + key.node = EventTriggerCommandTags[index].node; + key.type = EventTriggerCommandTags[index].type; + + hresult = (EventTriggerCommandNodeEntry *) + hash_search(EventTriggerCommandNodeCache, + (void *)&key, HASH_ENTER, &found); + + hresult->command = EventTriggerCommandTags[index].command; + } + MemoryContextSwitchTo(old); + } + + memset(&key, 0, sizeof(key)); + key.node = node; + key.type = type; + + entry = (EventTriggerCommandNodeEntry *) + hash_search(EventTriggerCommandNodeCache, + (void *)&key, HASH_FIND, NULL); + + if (entry == NULL) + { + if (!noerror) + /* fixme: should not happen, use elog? */ + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("unrecognized node %d and object %d", node, type))); + return ETC_UNKNOWN; + } + return entry->command; +} + +char * +objecttype_to_string(ObjectType type) +{ + switch(type) + { + case OBJECT_AGGREGATE: + return "AGGREGATE"; + case OBJECT_ATTRIBUTE: + return "ATTRIBUTE"; + case OBJECT_CAST: + return "CAST"; + case OBJECT_COLUMN: + return "COLUMN"; + case OBJECT_CONSTRAINT: + return "CONSTRAINT"; + case OBJECT_COLLATION: + return "COLLATION"; + case OBJECT_CONVERSION: + return "CONVERSION"; + case OBJECT_DATABASE: + return "DATABASE"; + case OBJECT_DOMAIN: + return "DOMAIN"; + case OBJECT_EVENT_TRIGGER: + return "EVENT TRIGGER"; + case OBJECT_EXTENSION: + return "EXTENSION"; + case OBJECT_FDW: + return "FDW"; + case OBJECT_FOREIGN_SERVER: + return "FOREIGN SERVER"; + case OBJECT_FOREIGN_TABLE: + return "FOREIGN TABLE"; + case OBJECT_FUNCTION: + return "FUNCTION"; + case OBJECT_INDEX: + return "INDEX"; + case OBJECT_LANGUAGE: + return "LANGUAGE"; + case OBJECT_LARGEOBJECT: + return "LARGE OBJECT"; + case OBJECT_OPCLASS: + return "OPERATOR CLASS"; + case OBJECT_OPERATOR: + return "OPERATOR"; + case OBJECT_OPFAMILY: + return "OPERATOR FAMILY"; + case OBJECT_ROLE: + return "ROLE"; + case OBJECT_RULE: + return "RULE"; + case OBJECT_SCHEMA: + return "SCHEMA"; + case OBJECT_SEQUENCE: + return "SEQUENCE"; + case OBJECT_TABLE: + return "TABLE"; + case OBJECT_TABLESPACE: + return "TABLESPACE"; + case OBJECT_TRIGGER: + return "TRIGGER"; + case OBJECT_TSCONFIGURATION: + return "TEXT SEARCH CONFIGURATION"; + case OBJECT_TSDICTIONARY: + return "TEXT SEARCH DICTIONARY"; + case OBJECT_TSPARSER: + return "TEXT SEARCH PARSER"; + case OBJECT_TSTEMPLATE: + return "TEXT SEARCH TEMPLATE"; + case OBJECT_TYPE: + return "TYPE"; + case OBJECT_VIEW: + return "VIEW"; + } + /* silence compiler */ + return NULL; +} diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c index 993053c..611c8e3 100644 --- a/src/bin/pg_dump/common.c +++ b/src/bin/pg_dump/common.c @@ -100,7 +100,7 @@ getSchemaData(Archive *fout, int *numTablesPtr) int numForeignDataWrappers; int numForeignServers; int numDefaultACLs; - int numEvtTriggers; + int numEventTriggers; if (g_verbose) write_msg(NULL, "reading schemas\n"); @@ -242,8 +242,8 @@ getSchemaData(Archive *fout, int *numTablesPtr) getTriggers(fout, tblinfo, numTables); if (g_verbose) - write_msg(NULL, "reading command triggers\n"); - getEvtTriggers(fout, &numEvtTriggers); + write_msg(NULL, "reading event triggers\n"); + getEventTriggers(fout, &numEventTriggers); *numTablesPtr = numTables; return tblinfo; diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 75b7ff0..09ca6dd 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -187,7 +187,7 @@ static void dumpConversion(Archive *fout, ConvInfo *convinfo); static void dumpRule(Archive *fout, RuleInfo *rinfo); static void dumpAgg(Archive *fout, AggInfo *agginfo); static void dumpTrigger(Archive *fout, TriggerInfo *tginfo); -static void dumpEvtTrigger(Archive *fout, EvtTriggerInfo *evtinfo); +static void dumpEventTrigger(Archive *fout, EventTriggerInfo *evtinfo); static void dumpTable(Archive *fout, TableInfo *tbinfo); static void dumpTableSchema(Archive *fout, TableInfo *tbinfo); static void dumpAttrDef(Archive *fout, AttrDefInfo *adinfo); @@ -5299,52 +5299,54 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables) } /* - * getEvtTriggers - * get information about every command trigger on a dumpable table + * getEventTriggers + * get information about event triggers */ -EvtTriggerInfo * -getEvtTriggers(Archive *fout, int *numEvtTriggers) +EventTriggerInfo * +getEventTriggers(Archive *fout, int *numEventTriggers) { int i; PQExpBuffer query = createPQExpBuffer(); PGresult *res; - EvtTriggerInfo *evtinfo; + EventTriggerInfo *evtinfo; int i_tableoid, i_oid, i_evtname, i_evtevent, - i_evtowner, + i_evtowner, i_evttags, i_evtfname, i_evtenabled; int ntups; + /* Before 9.3, there are no event triggers */ + if (fout->remoteVersion < 90300) + { + *numEventTriggers = 0; + return NULL; + } + /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); - if (fout->remoteVersion >= 90200) - { - appendPQExpBuffer(query, - "SELECT e.tableoid, e.oid, evtname, evtenabled, " - "evtevent, (%s evtowner) AS evtowner, " - "array_to_string(array(" - "select '''' || x || '''' " - " from unnest(evttags) as t(x)), ', ') as evttags, " - "n.nspname || '.' || p.proname as evtfname " - "FROM pg_event_trigger e " - "JOIN pg_proc p on e.evtfoid = p.oid " - "JOIN pg_namespace n ON p.pronamespace = n.oid " - "ORDER BY e.oid", - username_subquery); - } + appendPQExpBuffer(query, + "SELECT e.tableoid, e.oid, evtname, evtenabled, " + "evtevent, (%s evtowner) AS evtowner, " + "array_to_string(array(" + "select quote_literal(x) " + " from unnest(evttags) as t(x)), ', ') as evttags, " + "e.evtfoid::regproc as evtfname " + "FROM pg_event_trigger e " + "ORDER BY e.oid", + username_subquery); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); ntups = PQntuples(res); - *numEvtTriggers = ntups; + *numEventTriggers = ntups; - evtinfo = (EvtTriggerInfo *) pg_malloc(ntups * sizeof(EvtTriggerInfo)); + evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo)); i_tableoid = PQfnumber(res, "tableoid"); i_oid = PQfnumber(res, "oid"); @@ -7248,7 +7250,7 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj) dumpTrigger(fout, (TriggerInfo *) dobj); break; case DO_EVENT_TRIGGER: - dumpEvtTrigger(fout, (EvtTriggerInfo *) dobj); + dumpEventTrigger(fout, (EventTriggerInfo *) dobj); break; case DO_CONSTRAINT: dumpConstraint(fout, (ConstraintInfo *) dobj); @@ -13743,7 +13745,7 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) } static void -dumpEvtTrigger(Archive *fout, EvtTriggerInfo *evtinfo) +dumpEventTrigger(Archive *fout, EventTriggerInfo *evtinfo) { PQExpBuffer query; PQExpBuffer labelq; @@ -13754,19 +13756,19 @@ dumpEvtTrigger(Archive *fout, EvtTriggerInfo *evtinfo) appendPQExpBuffer(query, "CREATE EVENT TRIGGER "); appendPQExpBufferStr(query, fmtId(evtinfo->dobj.name)); appendPQExpBuffer(query, " ON "); - appendPQExpBufferStr(query, evtinfo->evtevent); + appendPQExpBufferStr(query, fmtId(evtinfo->evtevent)); appendPQExpBufferStr(query, " "); if (strcmp("", evtinfo->evttags) != 0) { - appendPQExpBufferStr(query, "when tag in ("); + appendPQExpBufferStr(query, "\n WHEN TAG IN ("); appendPQExpBufferStr(query, evtinfo->evttags); appendPQExpBufferStr(query, ") "); } - appendPQExpBuffer(query, "EXECUTE PROCEDURE "); + appendPQExpBuffer(query, "\n EXECUTE PROCEDURE "); appendPQExpBufferStr(query, evtinfo->evtfname); - appendPQExpBuffer(query, " ();\n"); + appendPQExpBuffer(query, "();\n"); if (evtinfo->evtenabled != 'O') { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 758a1df..5793bca 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -363,7 +363,7 @@ typedef struct _evttriggerInfo char *evtfname; char evttype; char evtenabled; -} EvtTriggerInfo; +} EventTriggerInfo; /* * struct ConstraintInfo is used for all constraint types. However we @@ -575,6 +575,6 @@ extern ForeignServerInfo *getForeignServers(Archive *fout, extern DefaultACLInfo *getDefaultACLs(Archive *fout, int *numDefaultACLs); extern void getExtensionMembership(Archive *fout, ExtensionInfo extinfo[], int numExtensions); -extern EvtTriggerInfo *getEvtTriggers(Archive *fout, int *numEvtTriggers); +extern EventTriggerInfo *getEventTriggers(Archive *fout, int *numEventTriggers); #endif /* PG_DUMP_H */ diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c index 6d4c2ba..5318e7a 100644 --- a/src/bin/pg_dump/pg_dump_sort.c +++ b/src/bin/pg_dump/pg_dump_sort.c @@ -24,8 +24,9 @@ static const char *modulename = gettext_noop("sorter"); * Objects are sorted by priority levels, and within an equal priority level * by OID. (This is a relatively crude hack to provide semi-reasonable * behavior for old databases without full dependency info.) Note: collations, - * extensions, text search, foreign-data, and default ACL objects can't really - * happen here, so the rather bogus priorities for them don't matter. + * extensions, text search, foreign-data, event trigger, and default ACL + * objects can't really happen here, so the rather bogus priorities for them + * don't matter. * * NOTE: object-type priorities must match the section assignments made in * pg_dump.c; that is, PRE_DATA objects must sort before DO_PRE_DATA_BOUNDARY, @@ -114,7 +115,7 @@ static const int newObjectTypePriority[] = 24, /* DO_BLOB_DATA */ 22, /* DO_PRE_DATA_BOUNDARY */ 25, /* DO_POST_DATA_BOUNDARY */ - 30 /* DO_EVENT_TRIGGER */ + 32 /* DO_EVENT_TRIGGER */ }; static DumpId preDataBoundId; diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 7ae8049..939e88f 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -491,7 +491,7 @@ exec_command(const char *cmd, success = listExtensions(pattern); break; case 'y': /* Event Triggers */ - success = listEvtTriggers(pattern, show_verbose); + success = listEventTriggers(pattern, show_verbose); break; default: status = PSQL_CMD_UNKNOWN; diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 7e8c996..14c6fe3 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -2958,7 +2958,7 @@ listConversions(const char *pattern, bool verbose, bool showSystem) * Describes Event Triggers. */ bool -listEvtTriggers(const char *pattern, bool verbose) +listEventTriggers(const char *pattern, bool verbose) { PQExpBufferData buf; PGresult *res; @@ -2971,15 +2971,14 @@ listEvtTriggers(const char *pattern, bool verbose) "select evtname as \"%s\", " "evtevent as \"%s\", " "pg_catalog.pg_get_userbyid(e.evtowner) AS \"%s\", " - " case evtenabled when 'O' then 'enabled' " + "case evtenabled when 'O' then 'enabled' " " when 'R' then 'replica' " " when 'A' then 'always' " " when 'D' then 'disabled' end as \"%s\", " - "n.nspname || '.' || p.proname || '()' as \"%s\", " - " array_to_string(array(select x " + "e.evtfoid::regproc as \"%s\", " + "array_to_string(array(select x " " from unnest(evttags) as t(x)), ', ') as \"%s\" " - "FROM pg_event_trigger e JOIN pg_proc p on e.evtfoid = p.oid " - "JOIN pg_namespace n ON p.pronamespace = n.oid ", + "FROM pg_event_trigger e ", gettext_noop("Name"), gettext_noop("Event"), gettext_noop("Owner"), diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h index f2bc794..eef7733 100644 --- a/src/bin/psql/describe.h +++ b/src/bin/psql/describe.h @@ -97,6 +97,6 @@ extern bool listExtensions(const char *pattern); extern bool listExtensionContents(const char *pattern); /* \dy */ -extern bool listEvtTriggers(const char *pattern, bool verbose); +extern bool listEventTriggers(const char *pattern, bool verbose); #endif /* DESCRIBE_H */ diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c index 5807265..0843b65 100644 --- a/src/bin/psql/help.c +++ b/src/bin/psql/help.c @@ -229,7 +229,7 @@ slashUsage(unsigned short int pager) fprintf(output, _(" \\dv[S+] [PATTERN] list views\n")); fprintf(output, _(" \\dE[S+] [PATTERN] list foreign tables\n")); fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n")); - fprintf(output, _(" \\dy [PATTERN] list command triggers\n")); + fprintf(output, _(" \\dy [PATTERN] list event triggers\n")); fprintf(output, _(" \\l[+] list all databases\n")); fprintf(output, _(" \\sf[+] FUNCNAME show a function's definition\n")); fprintf(output, _(" \\z [PATTERN] same as \\dp\n")); diff --git a/src/include/catalog/pg_event_trigger.h b/src/include/catalog/pg_event_trigger.h index 153d48a..a6caaee 100644 --- a/src/include/catalog/pg_event_trigger.h +++ b/src/include/catalog/pg_event_trigger.h @@ -60,122 +60,4 @@ typedef FormData_pg_event_trigger *Form_pg_event_trigger; #define Anum_pg_event_trigger_evtenabled 5 #define Anum_pg_event_trigger_evttags 6 -/* - * Times at which an event trigger can be fired. These are the - * possible values for pg_event_trigger.evtevent. - * - * As of now we only implement the command_start firing point, we intend on - * adding more firing points later. - */ -typedef enum TrigEvent -{ - E_CommandStart = 1, -} TrigEvent; - -/* - * Supported commands - */ -typedef enum TrigEventCommand -{ - E_UNKNOWN = 0, - E_ANY = 1, - - E_AlterAggregate = 100, - E_AlterCast, - E_AlterCollation, - E_AlterConversion, - E_AlterDomain, - E_AlterExtension, - E_AlterForeignDataWrapper, - E_AlterForeignTable, - E_AlterFunction, - E_AlterIndex, - E_AlterLanguage, - E_AlterOperator, - E_AlterOperatorClass, - E_AlterOperatorFamily, - E_AlterSchema, - E_AlterSequence, - E_AlterServer, - E_AlterTable, - E_AlterTextSearchParser, - E_AlterTextSearchConfiguration, - E_AlterTextSearchDictionary, - E_AlterTextSearchTemplate, - E_AlterTrigger, - E_AlterType, - E_AlterUserMapping, - E_AlterView, - - E_Cluster = 300, - E_Load, - E_Reindex, - E_SelectInto, - E_Vacuum, - - E_CreateAggregate = 400, - E_CreateCast, - E_CreateCollation, - E_CreateConversion, - E_CreateDomain, - E_CreateExtension, - E_CreateForeignDataWrapper, - E_CreateForeignTable, - E_CreateFunction, - E_CreateIndex, - E_CreateLanguage, - E_CreateOperator, - E_CreateOperatorClass, - E_CreateOperatorFamily, - E_CreateRule, - E_CreateSchema, - E_CreateSequence, - E_CreateServer, - E_CreateTable, - E_CreateTableAs, - E_CreateTextSearchParser, - E_CreateTextSearchConfiguration, - E_CreateTextSearchDictionary, - E_CreateTextSearchTemplate, - E_CreateTrigger, - E_CreateType, - E_CreateUserMapping, - E_CreateView, - - E_DropAggregate = 600, - E_DropCast, - E_DropCollation, - E_DropConversion, - E_DropDomain, - E_DropExtension, - E_DropForeignDataWrapper, - E_DropForeignTable, - E_DropFunction, - E_DropIndex, - E_DropLanguage, - E_DropOperator, - E_DropOperatorClass, - E_DropOperatorFamily, - E_DropRule, - E_DropSchema, - E_DropSequence, - E_DropServer, - E_DropTable, - E_DropTextSearchParser, - E_DropTextSearchConfiguration, - E_DropTextSearchDictionary, - E_DropTextSearchTemplate, - E_DropTrigger, - E_DropType, - E_DropUserMapping, - E_DropView -} TrigEventCommand; - -/* implemented in src/backend/catalog/pg_event_trigger.c */ -char * event_to_string(TrigEvent event); -TrigEvent parse_event_name(char *event); - -char * command_to_string(TrigEventCommand command); -TrigEventCommand parse_event_tag(char *command, bool noerror); - #endif /* PG_EVENT_TRIGGER_H */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 340de88..743f890 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3456,6 +3456,10 @@ DATA(insert OID = 2300 ( trigger_in PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 2 DESCR("I/O"); DATA(insert OID = 2301 ( trigger_out PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "2279" _null_ _null_ _null_ _null_ trigger_out _null_ _null_ _null_ )); DESCR("I/O"); +DATA(insert OID = 3594 ( event_trigger_in PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 3838 "2275" _null_ _null_ _null_ _null_ event_trigger_in _null_ _null_ _null_ )); +DESCR("I/O"); +DATA(insert OID = 3595 ( event_trigger_out PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "3838" _null_ _null_ _null_ _null_ event_trigger_out _null_ _null_ _null_ )); +DESCR("I/O"); DATA(insert OID = 2302 ( language_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 2280 "2275" _null_ _null_ _null_ _null_ language_handler_in _null_ _null_ _null_ )); DESCR("I/O"); DATA(insert OID = 2303 ( language_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "2280" _null_ _null_ _null_ _null_ language_handler_out _null_ _null_ _null_ )); @@ -4645,6 +4649,7 @@ DESCR("SP-GiST support for suffix tree over text"); DATA(insert OID = 4031 ( spg_text_leaf_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ spg_text_leaf_consistent _null_ _null_ _null_ )); DESCR("SP-GiST support for suffix tree over text"); + /* * Symbolic values for provolatile column: these indicate whether the result * of a function is dependent *only* on the values of its explicit arguments, diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index 98c7dd5..86be998 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -650,7 +650,7 @@ DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void #define VOIDOID 2278 DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); #define TRIGGEROID 2279 -DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); #define EVTTRIGGEROID 3838 DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); #define LANGUAGE_HANDLEROID 2280 diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h index 5c4db5c..13be6bc 100644 --- a/src/include/commands/event_trigger.h +++ b/src/include/commands/event_trigger.h @@ -15,6 +15,7 @@ #include "catalog/pg_event_trigger.h" #include "nodes/parsenodes.h" +#include "utils/evtcache.h" /* * To be able to call user defined function on event triggers, the places in @@ -30,6 +31,8 @@ typedef struct EventContextData TrigEventCommand command; /* For command triggers */ char *toplevel; /* TopLevel Command Tag */ char *tag; /* Command Tag */ + char *operation; /* CREATE / ALTER / DROP, or NULL */ + ObjectType objecttype; /* TABLE, FUNCTION, CAST, etc, or NULL */ Oid objectId; /* oid of the existing object, if any */ char *schemaname; /* schemaname or NULL if not relevant */ char *objectname; /* objectname */ @@ -48,9 +51,11 @@ typedef struct EventContextData *EventContext; typedef struct EventTriggerData { NodeTag type; - char *when; /* Either BEFORE or AFTER */ + char *event; /* command_start, etc */ char *toplevel; /* TopLevel Command Tag */ char *tag; /* Command Tag */ + char *operation; /* CREATE / ALTER / DROP, or NULL */ + char *objecttype; /* TABLE, FUNCTION, CAST, etc, or NULL */ Oid objectId; /* oid of the existing object, if any */ char *schemaname; /* schemaname or NULL if not relevant */ char *objectname; /* objectname */ diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index 2d1cccb..5700e54 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -195,6 +195,7 @@ typedef enum AclObjectKind ACL_KIND_TSCONFIGURATION, /* pg_ts_config */ ACL_KIND_FDW, /* pg_foreign_data_wrapper */ ACL_KIND_FOREIGN_SERVER, /* pg_foreign_server */ + ACL_KIND_EVENT_TRIGGER, /* pg_event_trigger */ ACL_KIND_EXTENSION, /* pg_extension */ MAX_ACL_KIND /* MUST BE LAST */ } AclObjectKind; @@ -322,6 +323,7 @@ extern bool pg_ts_dict_ownercheck(Oid dict_oid, Oid roleid); extern bool pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid); extern bool pg_foreign_data_wrapper_ownercheck(Oid srv_oid, Oid roleid); extern bool pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid); +extern bool pg_event_trigger_ownercheck(Oid et_oid, Oid roleid); extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid); extern bool has_createrole_privilege(Oid roleid); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 71563ad..8b9291c 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -532,6 +532,8 @@ extern Datum void_recv(PG_FUNCTION_ARGS); extern Datum void_send(PG_FUNCTION_ARGS); extern Datum trigger_in(PG_FUNCTION_ARGS); extern Datum trigger_out(PG_FUNCTION_ARGS); +extern Datum event_trigger_in(PG_FUNCTION_ARGS); +extern Datum event_trigger_out(PG_FUNCTION_ARGS); extern Datum language_handler_in(PG_FUNCTION_ARGS); extern Datum language_handler_out(PG_FUNCTION_ARGS); extern Datum fdw_handler_in(PG_FUNCTION_ARGS); @@ -1013,7 +1015,6 @@ extern Datum pg_encoding_max_length_sql(PG_FUNCTION_ARGS); /* format_type.c */ extern Datum format_type(PG_FUNCTION_ARGS); extern char *format_type_be(Oid type_oid); -extern char *format_type_be_without_namespace(Oid type_oid); extern char *format_type_with_typemod(Oid type_oid, int32 typemod); extern Datum oidvectortypes(PG_FUNCTION_ARGS); extern int32 type_maximum_size(Oid type_oid, int32 typemod); diff --git a/src/include/utils/evtcache.h b/src/include/utils/evtcache.h index 51f6bec..ef84b90 100644 --- a/src/include/utils/evtcache.h +++ b/src/include/utils/evtcache.h @@ -15,6 +15,125 @@ #include "catalog/pg_event_trigger.h" + +/* + * Times at which an event trigger can be fired. These are the + * possible values for pg_event_trigger.evtevent. + * + * As of now we only implement the command_start firing point, we intend on + * adding more firing points later. + */ +typedef enum TrigEvent +{ + EVT_CommandStart = 1, +} TrigEvent; + +/* + * Supported commands + * + * Those values are not going to disk, so can be shuffled around at will. We + * keep the number organized so that it's easier to debug, should it be needed. + * + * See also EventTriggerCommandTags in src/backend/utils/cache/evtcache.c + */ +typedef enum TrigEventCommand +{ + ETC_UNSET = -1, + ETC_UNKNOWN = 0, + ETC_ANY = 1, + + ETC_AlterAggregate = 100, + ETC_AlterCast, + ETC_AlterCollation, + ETC_AlterConversion, + ETC_AlterDomain, + ETC_AlterExtension, + ETC_AlterForeignDataWrapper, + ETC_AlterForeignTable, + ETC_AlterFunction, + ETC_AlterIndex, + ETC_AlterLanguage, + ETC_AlterOperator, + ETC_AlterOperatorClass, + ETC_AlterOperatorFamily, + ETC_AlterRule, + ETC_AlterSchema, + ETC_AlterSequence, + ETC_AlterServer, + ETC_AlterTable, + ETC_AlterTextSearchParser, + ETC_AlterTextSearchConfiguration, + ETC_AlterTextSearchDictionary, + ETC_AlterTextSearchTemplate, + ETC_AlterTrigger, + ETC_AlterType, + ETC_AlterUserMapping, + ETC_AlterView, + + ETC_Cluster = 300, + ETC_Load, + ETC_Reindex, + ETC_SelectInto, + ETC_Vacuum, + + ETC_CreateAggregate = 400, + ETC_CreateCast, + ETC_CreateCollation, + ETC_CreateConversion, + ETC_CreateDomain, + ETC_CreateExtension, + ETC_CreateForeignDataWrapper, + ETC_CreateForeignTable, + ETC_CreateFunction, + ETC_CreateIndex, + ETC_CreateLanguage, + ETC_CreateOperator, + ETC_CreateOperatorClass, + ETC_CreateOperatorFamily, + ETC_CreateRule, + ETC_CreateSchema, + ETC_CreateSequence, + ETC_CreateServer, + ETC_CreateTable, + ETC_CreateTableAs, + ETC_CreateTextSearchParser, + ETC_CreateTextSearchConfiguration, + ETC_CreateTextSearchDictionary, + ETC_CreateTextSearchTemplate, + ETC_CreateTrigger, + ETC_CreateType, + ETC_CreateUserMapping, + ETC_CreateView, + + ETC_DropAggregate = 600, + ETC_DropCast, + ETC_DropCollation, + ETC_DropConversion, + ETC_DropDomain, + ETC_DropExtension, + ETC_DropForeignDataWrapper, + ETC_DropForeignTable, + ETC_DropFunction, + ETC_DropIndex, + ETC_DropLanguage, + ETC_DropOperator, + ETC_DropOperatorClass, + ETC_DropOperatorFamily, + ETC_DropRule, + ETC_DropSchema, + ETC_DropSequence, + ETC_DropServer, + ETC_DropTable, + ETC_DropTextSearchParser, + ETC_DropTextSearchConfiguration, + ETC_DropTextSearchDictionary, + ETC_DropTextSearchTemplate, + ETC_DropTrigger, + ETC_DropType, + ETC_DropUserMapping, + ETC_DropView +} TrigEventCommand; + /* * Event Triggers to fire for a given event and command, including ANY command * triggers. @@ -30,5 +149,16 @@ void InitEventTriggerCache(void); EventCommandTriggers *get_event_triggers(TrigEvent event, TrigEventCommand command); +char * event_to_string(TrigEvent event); +TrigEvent parse_event_name(char *event); + +char * command_to_string(TrigEventCommand command); +TrigEventCommand parse_event_tag(char *cmdtag, bool noerror); + +TrigEventCommand get_command_from_nodetag(NodeTag node, + ObjectType type, + bool noerror); + +char * objecttype_to_string(ObjectType type); #endif /* EVTCACHE_H */ diff --git a/src/pl/plperl/expected/plperl_trigger.out b/src/pl/plperl/expected/plperl_trigger.out index b61d47c..2d1ed56 100644 --- a/src/pl/plperl/expected/plperl_trigger.out +++ b/src/pl/plperl/expected/plperl_trigger.out @@ -309,10 +309,10 @@ $$ LANGUAGE plperl; SELECT direct_trigger(); ERROR: trigger functions can only be called as triggers CONTEXT: compilation of PL/Perl function "direct_trigger" --- test plperl command triggers +-- test plperl event triggers create or replace function perlsnitch() returns event_trigger language plperl as $$ elog(NOTICE, "perlsnitch: " - . $_TD->{when} . " " + . $_TD->{event} . " " . $_TD->{tag} . " " . $_TD->{schemaname} . " " . $_TD->{objectname}); diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c index 5b456aa..374a81e 100644 --- a/src/pl/plperl/plperl.c +++ b/src/pl/plperl/plperl.c @@ -1571,7 +1571,7 @@ plperl_trigger_build_args(FunctionCallInfo fcinfo) } -/* Set up the arguments for a command trigger call. */ +/* Set up the arguments for an event trigger call. */ static SV * plperl_event_trigger_build_args(FunctionCallInfo fcinfo) { @@ -1584,7 +1584,7 @@ plperl_event_trigger_build_args(FunctionCallInfo fcinfo) tdata = (EventTriggerData *) fcinfo->context; - hv_store_string(hv, "when", cstr2sv(tdata->when)); + hv_store_string(hv, "event", cstr2sv(tdata->event)); hv_store_string(hv, "tag", cstr2sv(tdata->tag)); if (tdata->objectId == InvalidOid) @@ -1594,8 +1594,14 @@ plperl_event_trigger_build_args(FunctionCallInfo fcinfo) DirectFunctionCall1(oidout, ObjectIdGetDatum(tdata->objectId))); hv_store_string(hv, "objectid", cstr2sv(objectid)); - hv_store_string(hv, "objectname", cstr2sv(tdata->objectname == NULL ? "NULL" : tdata->objectname)); - hv_store_string(hv, "schemaname", cstr2sv(tdata->schemaname == NULL ? "NULL" : tdata->schemaname)); + hv_store_string(hv, "operation", + cstr2sv(tdata->operation == NULL ? "NULL" : tdata->operation)); + hv_store_string(hv, "objecttype", + cstr2sv(tdata->objecttype == NULL ? "NULL" : tdata->objecttype)); + hv_store_string(hv, "objectname", + cstr2sv(tdata->objectname == NULL ? "NULL" : tdata->objectname)); + hv_store_string(hv, "schemaname", + cstr2sv(tdata->schemaname == NULL ? "NULL" : tdata->schemaname)); return newRV_noinc((SV *) hv); } diff --git a/src/pl/plperl/sql/plperl_trigger.sql b/src/pl/plperl/sql/plperl_trigger.sql index 9cc2408..75a9f11 100644 --- a/src/pl/plperl/sql/plperl_trigger.sql +++ b/src/pl/plperl/sql/plperl_trigger.sql @@ -170,10 +170,10 @@ $$ LANGUAGE plperl; SELECT direct_trigger(); --- test plperl command triggers +-- test plperl event triggers create or replace function perlsnitch() returns event_trigger language plperl as $$ elog(NOTICE, "perlsnitch: " - . $_TD->{when} . " " + . $_TD->{event} . " " . $_TD->{tag} . " " . $_TD->{schemaname} . " " . $_TD->{objectname}); diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index 77c5ed9..4fb1443 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -536,7 +536,7 @@ do_compile(FunctionCallInfo fcinfo, if (rettypeid == VOIDOID || rettypeid == RECORDOID) /* okay */ ; - else if (rettypeid == TRIGGEROID) + else if (rettypeid == TRIGGEROID || rettypeid == EVTTRIGGEROID) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("trigger functions can only be called as triggers"))); @@ -689,15 +689,15 @@ do_compile(FunctionCallInfo fcinfo, if (procStruct->pronargs != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("command trigger functions cannot have declared arguments"))); + errmsg("event trigger functions cannot have declared arguments"))); /* Add the variable tg_when */ - var = plpgsql_build_variable("tg_when", 0, + var = plpgsql_build_variable("tg_event", 0, plpgsql_build_datatype(TEXTOID, -1, function->fn_input_collation), true); - function->tg_when_varno = var->dno; + function->tg_event_varno = var->dno; /* Add the variable tg_tag */ var = plpgsql_build_variable("tg_tag", 0, @@ -707,6 +707,22 @@ do_compile(FunctionCallInfo fcinfo, true); function->tg_tag_varno = var->dno; + /* Add the variable tg_operation */ + var = plpgsql_build_variable("tg_operation", 0, + plpgsql_build_datatype(TEXTOID, + -1, + function->fn_input_collation), + true); + function->tg_operation_varno = var->dno; + + /* Add the variable tg_objecttype */ + var = plpgsql_build_variable("tg_objecttype", 0, + plpgsql_build_datatype(TEXTOID, + -1, + function->fn_input_collation), + true); + function->tg_objecttype_varno = var->dno; + /* Add the variable tg_objectid */ var = plpgsql_build_variable("tg_objectid", 0, plpgsql_build_datatype(OIDOID, diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd59d75..a2cf506 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -773,8 +773,8 @@ plpgsql_exec_trigger(PLpgSQL_function *func, return rettup; } -void plpgsql_exec_event_trigger(PLpgSQL_function *func, - EventTriggerData *trigdata) +void +plpgsql_exec_event_trigger(PLpgSQL_function *func, EventTriggerData *trigdata) { PLpgSQL_execstate estate; ErrorContextCallback plerrcontext; @@ -805,8 +805,8 @@ void plpgsql_exec_event_trigger(PLpgSQL_function *func, /* * Assign the special tg_ variables */ - var = (PLpgSQL_var *) (estate.datums[func->tg_when_varno]); - var->value = CStringGetTextDatum(trigdata->when); + var = (PLpgSQL_var *) (estate.datums[func->tg_event_varno]); + var->value = CStringGetTextDatum(trigdata->event); var->isnull = false; var->freeval = true; @@ -815,6 +815,30 @@ void plpgsql_exec_event_trigger(PLpgSQL_function *func, var->isnull = false; var->freeval = true; + var = (PLpgSQL_var *) (estate.datums[func->tg_operation_varno]); + if (trigdata->operation == NULL) + { + var->isnull = true; + } + else + { + var->value = CStringGetTextDatum(trigdata->operation); + var->isnull = false; + } + var->freeval = false; + + var = (PLpgSQL_var *) (estate.datums[func->tg_objecttype_varno]); + if (trigdata->objecttype == NULL) + { + var->isnull = true; + } + else + { + var->value = CStringGetTextDatum(trigdata->objecttype); + var->isnull = false; + } + var->freeval = false; + var = (PLpgSQL_var *) (estate.datums[func->tg_objectid_varno]); if (trigdata->objectId == InvalidOid) { diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index 57f556f..4e15ad8 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -721,6 +721,9 @@ typedef struct PLpgSQL_function int tg_argv_varno; int tg_tag_varno; + int tg_event_varno; + int tg_operation_varno; + int tg_objecttype_varno; int tg_objectid_varno; int tg_schemaname_varno; int tg_objectname_varno; diff --git a/src/pl/plpython/expected/plpython_trigger.out b/src/pl/plpython/expected/plpython_trigger.out index f756462..5294dc7 100644 --- a/src/pl/plpython/expected/plpython_trigger.out +++ b/src/pl/plpython/expected/plpython_trigger.out @@ -610,10 +610,10 @@ SELECT * FROM composite_trigger_nested_test; ("(,t)","(1,f)",) (3 rows) --- test plpython command triggers +-- test plpython event triggers create or replace function pysnitch() returns event_trigger language plpythonu as $$ plpy.notice(" pysnitch: %s %s %s.%s" % - (TD["when"], TD["tag"], TD["schemaname"], TD["objectname"])); + (TD["event"], TD["tag"], TD["schemaname"], TD["objectname"])); $$; create event trigger py_snitch on command_start execute procedure pysnitch(); create or replace function foobar() returns int language sql as $$select 1;$$; diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c index 63682bf..7405893 100644 --- a/src/pl/plpython/plpy_exec.c +++ b/src/pl/plpython/plpy_exec.c @@ -348,8 +348,10 @@ PLy_exec_event_trigger(FunctionCallInfo fcinfo, PLyProcedure *proc) PG_TRY(); { /* build command trigger args */ - PyObject *pltwhen, + PyObject *pltevent, *plttag, + *pltoperation, + *pltobjecttype, *pltschemaname, *pltobjectname; char *stroid; @@ -358,14 +360,32 @@ PLy_exec_event_trigger(FunctionCallInfo fcinfo, PLyProcedure *proc) if (!pltdata) PLy_elog(ERROR, "could not create new dictionary while building command trigger arguments"); - pltwhen = PyString_FromString(tdata->when); - PyDict_SetItemString(pltdata, "when", pltwhen); - Py_DECREF(pltwhen); + pltevent = PyString_FromString(tdata->event); + PyDict_SetItemString(pltdata, "event", pltevent); + Py_DECREF(pltevent); plttag = PyString_FromString(tdata->tag); PyDict_SetItemString(pltdata, "tag", plttag); Py_DECREF(plttag); + if (tdata->operation == NULL) + PyDict_SetItemString(pltdata, "operation", Py_None); + else + { + pltoperation = PyString_FromString(tdata->operation); + PyDict_SetItemString(pltdata, "operation", pltoperation); + Py_DECREF(pltoperation); + } + + if (tdata->objecttype == NULL) + PyDict_SetItemString(pltdata, "objecttype", Py_None); + else + { + pltobjecttype = PyString_FromString(tdata->objecttype); + PyDict_SetItemString(pltdata, "objecttype", pltobjecttype); + Py_DECREF(pltobjecttype); + } + if (tdata->objectId == InvalidOid) PyDict_SetItemString(pltdata, "objectId", Py_None); else diff --git a/src/pl/plpython/sql/plpython_trigger.sql b/src/pl/plpython/sql/plpython_trigger.sql index 7ec6e0b..aaa1731 100644 --- a/src/pl/plpython/sql/plpython_trigger.sql +++ b/src/pl/plpython/sql/plpython_trigger.sql @@ -388,10 +388,10 @@ INSERT INTO composite_trigger_nested_test VALUES (ROW(ROW(1, 'f'), NULL, 3)); INSERT INTO composite_trigger_nested_test VALUES (ROW(ROW(NULL, 't'), ROW(1, 'f'), NULL)); SELECT * FROM composite_trigger_nested_test; --- test plpython command triggers +-- test plpython event triggers create or replace function pysnitch() returns event_trigger language plpythonu as $$ plpy.notice(" pysnitch: %s %s %s.%s" % - (TD["when"], TD["tag"], TD["schemaname"], TD["objectname"])); + (TD["event"], TD["tag"], TD["schemaname"], TD["objectname"])); $$; create event trigger py_snitch on command_start execute procedure pysnitch(); diff --git a/src/pl/tcl/expected/pltcl_setup.out b/src/pl/tcl/expected/pltcl_setup.out index 9b9c157..cfe124c 100644 --- a/src/pl/tcl/expected/pltcl_setup.out +++ b/src/pl/tcl/expected/pltcl_setup.out @@ -519,9 +519,9 @@ select tcl_date_week(2001,10,24); 42 (1 row) --- test pltcl command triggers +-- test pltcl event triggers create or replace function tclsnitch() returns event_trigger language pltcl as $$ - elog NOTICE " tclsnitch: $TG_when $TG_tag $TG_schemaname $TG_objectname" + elog NOTICE " tclsnitch: $TG_event $TG_tag $TG_schemaname $TG_objectname" $$; create event trigger tcl_snitch on command_start execute procedure tclsnitch(); create or replace function foobar() returns int language sql as $$select 1;$$; diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c index 7df6467..3ee32f7 100644 --- a/src/pl/tcl/pltcl.c +++ b/src/pl/tcl/pltcl.c @@ -1166,7 +1166,7 @@ pltcl_event_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted) Tcl_DStringAppendElement(&tcl_cmd, prodesc->internal_proname); PG_TRY(); { - Tcl_DStringAppendElement(&tcl_cmd, tdata->when); + Tcl_DStringAppendElement(&tcl_cmd, tdata->event); Tcl_DStringAppendElement(&tcl_cmd, tdata->tag); if (tdata->objectId == InvalidOid) @@ -1177,6 +1177,8 @@ pltcl_event_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted) Tcl_DStringAppendElement(&tcl_cmd, stroid); pfree(stroid); + Tcl_DStringAppendElement(&tcl_cmd, tdata->operation); + Tcl_DStringAppendElement(&tcl_cmd, tdata->objecttype); Tcl_DStringAppendElement(&tcl_cmd, tdata->schemaname); Tcl_DStringAppendElement(&tcl_cmd, tdata->objectname); } @@ -1488,7 +1490,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid, { /* event trigger procedure has fixed args */ strcpy(proc_internal_args, - "TG_when TG_tag TG_objectid TG_schemaname TG_objectname"); + "TG_event TG_tag TG_objectid TG_operation TG_objecttype TG_schemaname TG_objectname"); } /************************************************************ diff --git a/src/pl/tcl/sql/pltcl_setup.sql b/src/pl/tcl/sql/pltcl_setup.sql index 864e23b..2cb4839 100644 --- a/src/pl/tcl/sql/pltcl_setup.sql +++ b/src/pl/tcl/sql/pltcl_setup.sql @@ -560,9 +560,9 @@ $$ language pltcl immutable; select tcl_date_week(2010,1,24); select tcl_date_week(2001,10,24); --- test pltcl command triggers +-- test pltcl event triggers create or replace function tclsnitch() returns event_trigger language pltcl as $$ - elog NOTICE " tclsnitch: $TG_when $TG_tag $TG_schemaname $TG_objectname" + elog NOTICE " tclsnitch: $TG_event $TG_tag $TG_schemaname $TG_objectname" $$; create event trigger tcl_snitch on command_start execute procedure tclsnitch(); diff --git a/src/test/regress/expected/event_triggers.out b/src/test/regress/expected/event_triggers.out index 415605b..687d1c2 100644 --- a/src/test/regress/expected/event_triggers.out +++ b/src/test/regress/expected/event_triggers.out @@ -7,12 +7,9 @@ create or replace function snitch() as $$ begin -- can't output tg_objectid here that would break pg_regress - raise notice 'snitch: % % %.%', tg_when, tg_tag, tg_schemaname, tg_objectname; + raise notice 'snitch: % % [% - %]', tg_event, tg_tag, tg_operation, tg_objecttype; end; $$; --- --- TODO: REASSIGN OWNED and DROP OWNED --- create event trigger any_t on command_start execute procedure snitch(); create event trigger foo_t on command_start @@ -62,212 +59,216 @@ alter event trigger foo_t disable; alter event trigger foo_t enable; alter event trigger foo_t rename to snitch; create schema cmd; -NOTICE: snitch: command_start CREATE SCHEMA . -NOTICE: snitch: command_start CREATE SCHEMA . +NOTICE: snitch: command_start CREATE SCHEMA [CREATE - ] +NOTICE: snitch: command_start CREATE SCHEMA [CREATE - ] create schema cmd2; -NOTICE: snitch: command_start CREATE SCHEMA . -NOTICE: snitch: command_start CREATE SCHEMA . -create role regbob; -alter event trigger snitch owner to regbob; +NOTICE: snitch: command_start CREATE SCHEMA [CREATE - ] +NOTICE: snitch: command_start CREATE SCHEMA [CREATE - ] +create role regression_bob; +alter event trigger snitch owner to regression_bob; +ERROR: permission denied to change owner of event trigger "snitch" +HINT: The owner of an event trigger must be a superuser. +alter role regression_bob superuser; +alter event trigger snitch owner to regression_bob; create table cmd.foo(id bigserial primary key); -NOTICE: snitch: command_start CREATE TABLE . -NOTICE: snitch: command_start CREATE TABLE . -NOTICE: snitch: command_start CREATE SEQUENCE . -NOTICE: snitch: command_start CREATE SEQUENCE . -NOTICE: snitch: command_start CREATE INDEX . -NOTICE: snitch: command_start ALTER SEQUENCE . -NOTICE: snitch: command_start ALTER SEQUENCE . +NOTICE: snitch: command_start CREATE TABLE [CREATE - ] +NOTICE: snitch: command_start CREATE TABLE [CREATE - ] +NOTICE: snitch: command_start CREATE SEQUENCE [CREATE - ] +NOTICE: snitch: command_start CREATE SEQUENCE [CREATE - ] +NOTICE: snitch: command_start CREATE INDEX [CREATE - ] +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] create view cmd.v as select * from cmd.foo; -NOTICE: snitch: command_start CREATE VIEW . -NOTICE: snitch: command_start CREATE VIEW . +NOTICE: snitch: command_start CREATE VIEW [CREATE - ] +NOTICE: snitch: command_start CREATE VIEW [CREATE - ] alter table cmd.foo add column t text; -NOTICE: snitch: command_start ALTER TABLE . -NOTICE: snitch: command_start ALTER TABLE . +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] create table cmd.bar as select 1; -NOTICE: snitch: command_start CREATE TABLE AS . -NOTICE: snitch: command_start CREATE TABLE AS . +NOTICE: snitch: command_start CREATE TABLE AS [CREATE - ] +NOTICE: snitch: command_start CREATE TABLE AS [CREATE - ] drop table cmd.bar; -NOTICE: snitch: command_start DROP TABLE . -NOTICE: snitch: command_start DROP TABLE . +NOTICE: snitch: command_start DROP TABLE [DROP - TABLE] +NOTICE: snitch: command_start DROP TABLE [DROP - TABLE] select 1 into cmd.bar; -NOTICE: snitch: command_start SELECT INTO . -NOTICE: snitch: command_start SELECT INTO . +NOTICE: snitch: command_start SELECT INTO [ - ] +NOTICE: snitch: command_start SELECT INTO [ - ] drop table cmd.bar; -NOTICE: snitch: command_start DROP TABLE . -NOTICE: snitch: command_start DROP TABLE . +NOTICE: snitch: command_start DROP TABLE [DROP - TABLE] +NOTICE: snitch: command_start DROP TABLE [DROP - TABLE] create table test9 (id int, stuff text); -NOTICE: snitch: command_start CREATE TABLE . -NOTICE: snitch: command_start CREATE TABLE . +NOTICE: snitch: command_start CREATE TABLE [CREATE - ] +NOTICE: snitch: command_start CREATE TABLE [CREATE - ] alter table test9 rename to test; -NOTICE: snitch: command_start ALTER TABLE . -NOTICE: snitch: command_start ALTER TABLE . +NOTICE: snitch: command_start ALTER TABLE [ALTER - TABLE] +NOTICE: snitch: command_start ALTER TABLE [ALTER - TABLE] alter table test set schema cmd; -NOTICE: snitch: command_start ALTER TABLE . -NOTICE: snitch: command_start ALTER TABLE . +NOTICE: snitch: command_start ALTER TABLE [ALTER - TABLE] +NOTICE: snitch: command_start ALTER TABLE [ALTER - TABLE] alter table cmd.test rename column stuff to things; -NOTICE: snitch: command_start ALTER TABLE . -NOTICE: snitch: command_start ALTER TABLE . +NOTICE: snitch: command_start ALTER TABLE [ALTER - COLUMN] +NOTICE: snitch: command_start ALTER TABLE [ALTER - COLUMN] alter table cmd.test add column alpha text; -NOTICE: snitch: command_start ALTER TABLE . -NOTICE: snitch: command_start ALTER TABLE . +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] alter table cmd.test alter column alpha set data type varchar(300); -NOTICE: snitch: command_start ALTER TABLE . -NOTICE: snitch: command_start ALTER TABLE . +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] alter table cmd.test alter column alpha set default 'test'; -NOTICE: snitch: command_start ALTER TABLE . -NOTICE: snitch: command_start ALTER TABLE . +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] alter table cmd.test alter column alpha drop default; -NOTICE: snitch: command_start ALTER TABLE . -NOTICE: snitch: command_start ALTER TABLE . +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] alter table cmd.test alter column alpha set statistics 78; -NOTICE: snitch: command_start ALTER TABLE . -NOTICE: snitch: command_start ALTER TABLE . +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] alter table cmd.test alter column alpha set storage plain; -NOTICE: snitch: command_start ALTER TABLE . -NOTICE: snitch: command_start ALTER TABLE . +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] alter table cmd.test alter column alpha set not null; -NOTICE: snitch: command_start ALTER TABLE . -NOTICE: snitch: command_start ALTER TABLE . +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] alter table cmd.test alter column alpha drop not null; -NOTICE: snitch: command_start ALTER TABLE . -NOTICE: snitch: command_start ALTER TABLE . +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] alter table cmd.test alter column alpha set (n_distinct = -0.78); -NOTICE: snitch: command_start ALTER TABLE . -NOTICE: snitch: command_start ALTER TABLE . +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] alter table cmd.test alter column alpha reset (n_distinct); -NOTICE: snitch: command_start ALTER TABLE . -NOTICE: snitch: command_start ALTER TABLE . +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] alter table cmd.test drop column alpha; -NOTICE: snitch: command_start ALTER TABLE . -NOTICE: snitch: command_start ALTER TABLE . +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] alter table cmd.test add check (id > 2) not valid; -NOTICE: snitch: command_start ALTER TABLE . -NOTICE: snitch: command_start ALTER TABLE . +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] alter table cmd.test add check (id < 800000); -NOTICE: snitch: command_start ALTER TABLE . -NOTICE: snitch: command_start ALTER TABLE . +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] alter table cmd.test set without cluster; -NOTICE: snitch: command_start ALTER TABLE . -NOTICE: snitch: command_start ALTER TABLE . +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] alter table cmd.test set with oids; -NOTICE: snitch: command_start ALTER TABLE . -NOTICE: snitch: command_start ALTER TABLE . +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] alter table cmd.test set without oids; -NOTICE: snitch: command_start ALTER TABLE . -NOTICE: snitch: command_start ALTER TABLE . +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] +NOTICE: snitch: command_start ALTER TABLE [ALTER - ] create sequence test_seq_; -NOTICE: snitch: command_start CREATE SEQUENCE . -NOTICE: snitch: command_start CREATE SEQUENCE . -alter sequence test_seq_ owner to regbob; -NOTICE: snitch: command_start ALTER SEQUENCE . -NOTICE: snitch: command_start ALTER SEQUENCE . +NOTICE: snitch: command_start CREATE SEQUENCE [CREATE - ] +NOTICE: snitch: command_start CREATE SEQUENCE [CREATE - ] +alter sequence test_seq_ owner to regression_bob; +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] alter sequence test_seq_ rename to test_seq; -NOTICE: snitch: command_start ALTER SEQUENCE . -NOTICE: snitch: command_start ALTER SEQUENCE . +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - SEQUENCE] +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - SEQUENCE] alter sequence test_seq set schema cmd; -NOTICE: snitch: command_start ALTER SEQUENCE . -NOTICE: snitch: command_start ALTER SEQUENCE . +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - SEQUENCE] +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - SEQUENCE] alter sequence cmd.test_seq start with 3; -NOTICE: snitch: command_start ALTER SEQUENCE . -NOTICE: snitch: command_start ALTER SEQUENCE . +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] alter sequence cmd.test_seq restart with 4; -NOTICE: snitch: command_start ALTER SEQUENCE . -NOTICE: snitch: command_start ALTER SEQUENCE . +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] alter sequence cmd.test_seq minvalue 3; -NOTICE: snitch: command_start ALTER SEQUENCE . -NOTICE: snitch: command_start ALTER SEQUENCE . +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] alter sequence cmd.test_seq no minvalue; -NOTICE: snitch: command_start ALTER SEQUENCE . -NOTICE: snitch: command_start ALTER SEQUENCE . +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] alter sequence cmd.test_seq maxvalue 900000; -NOTICE: snitch: command_start ALTER SEQUENCE . -NOTICE: snitch: command_start ALTER SEQUENCE . +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] alter sequence cmd.test_seq no maxvalue; -NOTICE: snitch: command_start ALTER SEQUENCE . -NOTICE: snitch: command_start ALTER SEQUENCE . +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] alter sequence cmd.test_seq cache 876; -NOTICE: snitch: command_start ALTER SEQUENCE . -NOTICE: snitch: command_start ALTER SEQUENCE . +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] alter sequence cmd.test_seq cycle; -NOTICE: snitch: command_start ALTER SEQUENCE . -NOTICE: snitch: command_start ALTER SEQUENCE . +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] alter sequence cmd.test_seq no cycle; -NOTICE: snitch: command_start ALTER SEQUENCE . -NOTICE: snitch: command_start ALTER SEQUENCE . +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] +NOTICE: snitch: command_start ALTER SEQUENCE [ALTER - ] create view view_test as select id, things from cmd.test; -NOTICE: snitch: command_start CREATE VIEW . -NOTICE: snitch: command_start CREATE VIEW . -alter view view_test owner to regbob; -NOTICE: snitch: command_start ALTER VIEW . -NOTICE: snitch: command_start ALTER VIEW . +NOTICE: snitch: command_start CREATE VIEW [CREATE - ] +NOTICE: snitch: command_start CREATE VIEW [CREATE - ] +alter view view_test owner to regression_bob; +NOTICE: snitch: command_start ALTER VIEW [ALTER - ] +NOTICE: snitch: command_start ALTER VIEW [ALTER - ] alter view view_test rename to view_test2; -NOTICE: snitch: command_start ALTER VIEW . -NOTICE: snitch: command_start ALTER VIEW . +NOTICE: snitch: command_start ALTER VIEW [ALTER - VIEW] +NOTICE: snitch: command_start ALTER VIEW [ALTER - VIEW] alter view view_test2 set schema cmd; -NOTICE: snitch: command_start ALTER VIEW . -NOTICE: snitch: command_start ALTER VIEW . +NOTICE: snitch: command_start ALTER VIEW [ALTER - VIEW] +NOTICE: snitch: command_start ALTER VIEW [ALTER - VIEW] alter view cmd.view_test2 alter column id set default 9; -NOTICE: snitch: command_start ALTER VIEW . -NOTICE: snitch: command_start ALTER VIEW . +NOTICE: snitch: command_start ALTER VIEW [ALTER - ] +NOTICE: snitch: command_start ALTER VIEW [ALTER - ] alter view cmd.view_test2 alter column id drop default; -NOTICE: snitch: command_start ALTER VIEW . -NOTICE: snitch: command_start ALTER VIEW . +NOTICE: snitch: command_start ALTER VIEW [ALTER - ] +NOTICE: snitch: command_start ALTER VIEW [ALTER - ] cluster cmd.foo using foo_pkey; -NOTICE: snitch: command_start CLUSTER . +NOTICE: snitch: command_start CLUSTER [ - ] vacuum cmd.foo; -NOTICE: snitch: command_start VACUUM . -NOTICE: snitch: command_start VACUUM . +NOTICE: snitch: command_start VACUUM [ - ] +NOTICE: snitch: command_start VACUUM [ - ] vacuum; -NOTICE: snitch: command_start VACUUM . -NOTICE: snitch: command_start VACUUM . +NOTICE: snitch: command_start VACUUM [ - ] +NOTICE: snitch: command_start VACUUM [ - ] reindex table cmd.foo; -NOTICE: snitch: command_start REINDEX . -NOTICE: snitch: command_start REINDEX . +NOTICE: snitch: command_start REINDEX [ - ] +NOTICE: snitch: command_start REINDEX [ - ] set session_replication_role to replica; create table cmd.bar(); -NOTICE: snitch: command_start CREATE TABLE . -NOTICE: snitch: command_start CREATE TABLE . +NOTICE: snitch: command_start CREATE TABLE [CREATE - ] +NOTICE: snitch: command_start CREATE TABLE [CREATE - ] reset session_replication_role; create index idx_foo on cmd.foo(t); -NOTICE: snitch: command_start CREATE INDEX . +NOTICE: snitch: command_start CREATE INDEX [CREATE - ] reindex index cmd.idx_foo; -NOTICE: snitch: command_start REINDEX . -NOTICE: snitch: command_start REINDEX . +NOTICE: snitch: command_start REINDEX [ - ] +NOTICE: snitch: command_start REINDEX [ - ] drop index cmd.idx_foo; -NOTICE: snitch: command_start DROP INDEX . +NOTICE: snitch: command_start DROP INDEX [DROP - INDEX] create function fun(int) returns text language sql as $$ select t from cmd.foo where id = $1; $$; -NOTICE: snitch: command_start CREATE FUNCTION . -NOTICE: snitch: command_start CREATE FUNCTION . +NOTICE: snitch: command_start CREATE FUNCTION [CREATE - ] +NOTICE: snitch: command_start CREATE FUNCTION [CREATE - ] alter function fun(int) strict; -NOTICE: snitch: command_start ALTER FUNCTION . -NOTICE: snitch: command_start ALTER FUNCTION . +NOTICE: snitch: command_start ALTER FUNCTION [ALTER - ] +NOTICE: snitch: command_start ALTER FUNCTION [ALTER - ] alter function fun(int) rename to notfun; -NOTICE: snitch: command_start ALTER FUNCTION . -NOTICE: snitch: command_start ALTER FUNCTION . +NOTICE: snitch: command_start ALTER FUNCTION [ALTER - FUNCTION] +NOTICE: snitch: command_start ALTER FUNCTION [ALTER - FUNCTION] alter function notfun(int) set schema cmd; -NOTICE: snitch: command_start ALTER FUNCTION . -NOTICE: snitch: command_start ALTER FUNCTION . -alter function cmd.notfun(int) owner to regbob; -NOTICE: snitch: command_start ALTER FUNCTION . -NOTICE: snitch: command_start ALTER FUNCTION . +NOTICE: snitch: command_start ALTER FUNCTION [ALTER - FUNCTION] +NOTICE: snitch: command_start ALTER FUNCTION [ALTER - FUNCTION] +alter function cmd.notfun(int) owner to regression_bob; +NOTICE: snitch: command_start ALTER FUNCTION [ALTER - FUNCTION] +NOTICE: snitch: command_start ALTER FUNCTION [ALTER - FUNCTION] alter function cmd.notfun(int) cost 77; -NOTICE: snitch: command_start ALTER FUNCTION . -NOTICE: snitch: command_start ALTER FUNCTION . +NOTICE: snitch: command_start ALTER FUNCTION [ALTER - ] +NOTICE: snitch: command_start ALTER FUNCTION [ALTER - ] drop function cmd.notfun(int); -NOTICE: snitch: command_start DROP FUNCTION . +NOTICE: snitch: command_start DROP FUNCTION [DROP - FUNCTION] create function cmd.plus1(int) returns bigint language sql as $$ select $1::bigint + 1; $$; -NOTICE: snitch: command_start CREATE FUNCTION . -NOTICE: snitch: command_start CREATE FUNCTION . +NOTICE: snitch: command_start CREATE FUNCTION [CREATE - ] +NOTICE: snitch: command_start CREATE FUNCTION [CREATE - ] create operator cmd.+!(procedure = cmd.plus1, leftarg = int); -NOTICE: snitch: command_start CREATE OPERATOR . -NOTICE: snitch: command_start CREATE OPERATOR . +NOTICE: snitch: command_start CREATE OPERATOR [CREATE - OPERATOR] +NOTICE: snitch: command_start CREATE OPERATOR [CREATE - OPERATOR] alter operator cmd.+!(int, NONE) set schema public; -NOTICE: snitch: command_start ALTER OPERATOR . -NOTICE: snitch: command_start ALTER OPERATOR . +NOTICE: snitch: command_start ALTER OPERATOR [ALTER - OPERATOR] +NOTICE: snitch: command_start ALTER OPERATOR [ALTER - OPERATOR] drop operator public.+!(int, NONE); -NOTICE: snitch: command_start DROP OPERATOR . +NOTICE: snitch: command_start DROP OPERATOR [DROP - OPERATOR] create aggregate cmd.avg (float8) ( sfunc = float8_accum, @@ -275,130 +276,130 @@ create aggregate cmd.avg (float8) finalfunc = float8_avg, initcond = '{0,0,0}' ); -NOTICE: snitch: command_start CREATE AGGREGATE . -NOTICE: snitch: command_start CREATE AGGREGATE . +NOTICE: snitch: command_start CREATE AGGREGATE [CREATE - AGGREGATE] +NOTICE: snitch: command_start CREATE AGGREGATE [CREATE - AGGREGATE] alter aggregate cmd.avg(float8) set schema public; -NOTICE: snitch: command_start ALTER AGGREGATE . +NOTICE: snitch: command_start ALTER AGGREGATE [ALTER - AGGREGATE] drop aggregate public.avg(float8); -NOTICE: snitch: command_start DROP AGGREGATE . -NOTICE: snitch: command_start DROP AGGREGATE . +NOTICE: snitch: command_start DROP AGGREGATE [DROP - AGGREGATE] +NOTICE: snitch: command_start DROP AGGREGATE [DROP - AGGREGATE] create collation cmd.french (LOCALE = 'fr_FR'); -NOTICE: snitch: command_start CREATE COLLATION . -NOTICE: snitch: command_start CREATE COLLATION . +NOTICE: snitch: command_start CREATE COLLATION [CREATE - COLLATION] +NOTICE: snitch: command_start CREATE COLLATION [CREATE - COLLATION] alter collation cmd.french rename to francais; -NOTICE: snitch: command_start ALTER COLLATION . -NOTICE: snitch: command_start ALTER COLLATION . +NOTICE: snitch: command_start ALTER COLLATION [ALTER - COLLATION] +NOTICE: snitch: command_start ALTER COLLATION [ALTER - COLLATION] create type cmd.compfoo AS (f1 int, f2 text); -NOTICE: snitch: command_start CREATE TYPE . -NOTICE: snitch: command_start CREATE TYPE . +NOTICE: snitch: command_start CREATE TYPE [CREATE - ] +NOTICE: snitch: command_start CREATE TYPE [CREATE - ] alter type cmd.compfoo add attribute f3 text; -NOTICE: snitch: command_start ALTER TYPE . -NOTICE: snitch: command_start ALTER TYPE . +NOTICE: snitch: command_start ALTER TYPE [ALTER - ] +NOTICE: snitch: command_start ALTER TYPE [ALTER - ] create type cmd.type_test AS (a integer, b integer, c text); -NOTICE: snitch: command_start CREATE TYPE . -NOTICE: snitch: command_start CREATE TYPE . -alter type cmd.type_test owner to regbob; -NOTICE: snitch: command_start ALTER TYPE . -NOTICE: snitch: command_start ALTER TYPE . +NOTICE: snitch: command_start CREATE TYPE [CREATE - ] +NOTICE: snitch: command_start CREATE TYPE [CREATE - ] +alter type cmd.type_test owner to regression_bob; +NOTICE: snitch: command_start ALTER TYPE [ALTER - TYPE] +NOTICE: snitch: command_start ALTER TYPE [ALTER - TYPE] alter type cmd.type_test rename to type_test2; -NOTICE: snitch: command_start ALTER TYPE . -NOTICE: snitch: command_start ALTER TYPE . +NOTICE: snitch: command_start ALTER TYPE [ALTER - TYPE] +NOTICE: snitch: command_start ALTER TYPE [ALTER - TYPE] alter type cmd.type_test2 set schema public; -NOTICE: snitch: command_start ALTER TYPE . -NOTICE: snitch: command_start ALTER TYPE . +NOTICE: snitch: command_start ALTER TYPE [ALTER - TYPE] +NOTICE: snitch: command_start ALTER TYPE [ALTER - TYPE] alter type public.type_test2 rename attribute a to z; -NOTICE: snitch: command_start ALTER TYPE . -NOTICE: snitch: command_start ALTER TYPE . +NOTICE: snitch: command_start ALTER TYPE [ALTER - ATTRIBUTE] +NOTICE: snitch: command_start ALTER TYPE [ALTER - ATTRIBUTE] alter type public.type_test2 add attribute alpha text; -NOTICE: snitch: command_start ALTER TYPE . -NOTICE: snitch: command_start ALTER TYPE . +NOTICE: snitch: command_start ALTER TYPE [ALTER - ] +NOTICE: snitch: command_start ALTER TYPE [ALTER - ] alter type public.type_test2 alter attribute alpha set data type char(90); -NOTICE: snitch: command_start ALTER TYPE . -NOTICE: snitch: command_start ALTER TYPE . +NOTICE: snitch: command_start ALTER TYPE [ALTER - ] +NOTICE: snitch: command_start ALTER TYPE [ALTER - ] alter type public.type_test2 drop attribute alpha; -NOTICE: snitch: command_start ALTER TYPE . -NOTICE: snitch: command_start ALTER TYPE . +NOTICE: snitch: command_start ALTER TYPE [ALTER - ] +NOTICE: snitch: command_start ALTER TYPE [ALTER - ] drop type cmd.compfoo; -NOTICE: snitch: command_start DROP TYPE . +NOTICE: snitch: command_start DROP TYPE [DROP - TYPE] drop type public.type_test2; -NOTICE: snitch: command_start DROP TYPE . +NOTICE: snitch: command_start DROP TYPE [DROP - TYPE] create type cmd.bug_status as enum ('new', 'open', 'closed'); -NOTICE: snitch: command_start CREATE TYPE . -NOTICE: snitch: command_start CREATE TYPE . +NOTICE: snitch: command_start CREATE TYPE [CREATE - ] +NOTICE: snitch: command_start CREATE TYPE [CREATE - ] alter type cmd.bug_status add value 'wontfix'; -NOTICE: snitch: command_start ALTER TYPE . -NOTICE: snitch: command_start ALTER TYPE . +NOTICE: snitch: command_start ALTER TYPE [ALTER - ] +NOTICE: snitch: command_start ALTER TYPE [ALTER - ] create domain cmd.us_postal_code as text check(value ~ '^\d{5}$' or value ~ '^\d{5}-\d{4}$'); -NOTICE: snitch: command_start CREATE DOMAIN . -NOTICE: snitch: command_start CREATE DOMAIN . +NOTICE: snitch: command_start CREATE DOMAIN [CREATE - ] +NOTICE: snitch: command_start CREATE DOMAIN [CREATE - ] alter domain cmd.us_postal_code set not null; -NOTICE: snitch: command_start ALTER DOMAIN . -NOTICE: snitch: command_start ALTER DOMAIN . +NOTICE: snitch: command_start ALTER DOMAIN [ALTER - ] +NOTICE: snitch: command_start ALTER DOMAIN [ALTER - ] alter domain cmd.us_postal_code set default 90210; -NOTICE: snitch: command_start ALTER DOMAIN . -NOTICE: snitch: command_start ALTER DOMAIN . +NOTICE: snitch: command_start ALTER DOMAIN [ALTER - ] +NOTICE: snitch: command_start ALTER DOMAIN [ALTER - ] alter domain cmd.us_postal_code drop default; -NOTICE: snitch: command_start ALTER DOMAIN . -NOTICE: snitch: command_start ALTER DOMAIN . +NOTICE: snitch: command_start ALTER DOMAIN [ALTER - ] +NOTICE: snitch: command_start ALTER DOMAIN [ALTER - ] alter domain cmd.us_postal_code drop not null; -NOTICE: snitch: command_start ALTER DOMAIN . -NOTICE: snitch: command_start ALTER DOMAIN . +NOTICE: snitch: command_start ALTER DOMAIN [ALTER - ] +NOTICE: snitch: command_start ALTER DOMAIN [ALTER - ] alter domain cmd.us_postal_code add constraint dummy_constraint check (value ~ '^\d{8}$'); -NOTICE: snitch: command_start ALTER DOMAIN . -NOTICE: snitch: command_start ALTER DOMAIN . +NOTICE: snitch: command_start ALTER DOMAIN [ALTER - ] +NOTICE: snitch: command_start ALTER DOMAIN [ALTER - ] alter domain cmd.us_postal_code drop constraint dummy_constraint; -NOTICE: snitch: command_start ALTER DOMAIN . -NOTICE: snitch: command_start ALTER DOMAIN . -alter domain cmd.us_postal_code owner to regbob; -NOTICE: snitch: command_start ALTER DOMAIN . -NOTICE: snitch: command_start ALTER DOMAIN . +NOTICE: snitch: command_start ALTER DOMAIN [ALTER - ] +NOTICE: snitch: command_start ALTER DOMAIN [ALTER - ] +alter domain cmd.us_postal_code owner to regression_bob; +NOTICE: snitch: command_start ALTER DOMAIN [ALTER - DOMAIN] +NOTICE: snitch: command_start ALTER DOMAIN [ALTER - DOMAIN] alter domain cmd.us_postal_code set schema cmd2; -NOTICE: snitch: command_start ALTER DOMAIN . -NOTICE: snitch: command_start ALTER DOMAIN . +NOTICE: snitch: command_start ALTER DOMAIN [ALTER - DOMAIN] +NOTICE: snitch: command_start ALTER DOMAIN [ALTER - DOMAIN] drop domain cmd2.us_postal_code; -NOTICE: snitch: command_start DROP DOMAIN . -NOTICE: snitch: command_start DROP DOMAIN . +NOTICE: snitch: command_start DROP DOMAIN [DROP - DOMAIN] +NOTICE: snitch: command_start DROP DOMAIN [DROP - DOMAIN] create function cmd.trigfunc() returns trigger language plpgsql as $$ begin raise notice 'trigfunc'; end;$$; -NOTICE: snitch: command_start CREATE FUNCTION . -NOTICE: snitch: command_start CREATE FUNCTION . +NOTICE: snitch: command_start CREATE FUNCTION [CREATE - ] +NOTICE: snitch: command_start CREATE FUNCTION [CREATE - ] create trigger footg before update on cmd.foo for each row execute procedure cmd.trigfunc(); -NOTICE: snitch: command_start CREATE TRIGGER . -NOTICE: snitch: command_start CREATE TRIGGER . +NOTICE: snitch: command_start CREATE TRIGGER [CREATE - ] +NOTICE: snitch: command_start CREATE TRIGGER [CREATE - ] alter trigger footg on cmd.foo rename to foo_trigger; -NOTICE: snitch: command_start ALTER TRIGGER . -NOTICE: snitch: command_start ALTER TRIGGER . +NOTICE: snitch: command_start ALTER TRIGGER [ALTER - TRIGGER] +NOTICE: snitch: command_start ALTER TRIGGER [ALTER - TRIGGER] drop trigger foo_trigger on cmd.foo; -NOTICE: snitch: command_start DROP TRIGGER . -NOTICE: snitch: command_start DROP TRIGGER . +NOTICE: snitch: command_start DROP TRIGGER [DROP - TRIGGER] +NOTICE: snitch: command_start DROP TRIGGER [DROP - TRIGGER] create conversion test for 'utf8' to 'sjis' from utf8_to_sjis; -NOTICE: snitch: command_start CREATE CONVERSION . +NOTICE: snitch: command_start CREATE CONVERSION [CREATE - ] create default conversion test2 for 'utf8' to 'sjis' from utf8_to_sjis; -NOTICE: snitch: command_start CREATE CONVERSION . +NOTICE: snitch: command_start CREATE CONVERSION [CREATE - ] alter conversion test2 rename to test3; -NOTICE: snitch: command_start ALTER CONVERSION . -NOTICE: snitch: command_start ALTER CONVERSION . +NOTICE: snitch: command_start ALTER CONVERSION [ALTER - CONVERSION] +NOTICE: snitch: command_start ALTER CONVERSION [ALTER - CONVERSION] drop conversion test3; -NOTICE: snitch: command_start DROP CONVERSION . +NOTICE: snitch: command_start DROP CONVERSION [DROP - CONVERSION] drop conversion test; -NOTICE: snitch: command_start DROP CONVERSION . +NOTICE: snitch: command_start DROP CONVERSION [DROP - CONVERSION] create operator class test_op_class for type anyenum using hash as operator 1 =, function 1 hashenum(anyenum); -NOTICE: snitch: command_start CREATE OPERATOR CLASS . -NOTICE: snitch: command_start CREATE OPERATOR CLASS . +NOTICE: snitch: command_start CREATE OPERATOR CLASS [CREATE - ] +NOTICE: snitch: command_start CREATE OPERATOR CLASS [CREATE - ] create text search configuration test (parser = "default"); -NOTICE: snitch: command_start CREATE TEXT SEARCH CONFIGURATION . -NOTICE: snitch: command_start CREATE TEXT SEARCH CONFIGURATION . +NOTICE: snitch: command_start CREATE TEXT SEARCH CONFIGURATION [CREATE - TEXT SEARCH CONFIGURATION] +NOTICE: snitch: command_start CREATE TEXT SEARCH CONFIGURATION [CREATE - TEXT SEARCH CONFIGURATION] create text search dictionary test_stem ( template = snowball, language = 'english', stopwords = 'english' ); -NOTICE: snitch: command_start CREATE TEXT SEARCH DICTIONARY . -NOTICE: snitch: command_start CREATE TEXT SEARCH DICTIONARY . +NOTICE: snitch: command_start CREATE TEXT SEARCH DICTIONARY [CREATE - TEXT SEARCH DICTIONARY] +NOTICE: snitch: command_start CREATE TEXT SEARCH DICTIONARY [CREATE - TEXT SEARCH DICTIONARY] alter text search dictionary test_stem (StopWords = dutch ); -NOTICE: snitch: command_start ALTER TEXT SEARCH DICTIONARY . +NOTICE: snitch: command_start ALTER TEXT SEARCH DICTIONARY [ALTER - ] create text search parser test_parser ( start = prsd_start, gettoken = prsd_nexttoken, @@ -406,38 +407,38 @@ create text search parser test_parser ( lextypes = prsd_lextype, headline = prsd_headline ); -NOTICE: snitch: command_start CREATE TEXT SEARCH PARSER . -NOTICE: snitch: command_start CREATE TEXT SEARCH PARSER . +NOTICE: snitch: command_start CREATE TEXT SEARCH PARSER [CREATE - TEXT SEARCH PARSER] +NOTICE: snitch: command_start CREATE TEXT SEARCH PARSER [CREATE - TEXT SEARCH PARSER] create text search template test_template ( init = dsimple_init, lexize = dsimple_lexize ); -NOTICE: snitch: command_start CREATE TEXT SEARCH TEMPLATE . -NOTICE: snitch: command_start CREATE TEXT SEARCH TEMPLATE . +NOTICE: snitch: command_start CREATE TEXT SEARCH TEMPLATE [CREATE - TEXT SEARCH TEMPLATE] +NOTICE: snitch: command_start CREATE TEXT SEARCH TEMPLATE [CREATE - TEXT SEARCH TEMPLATE] drop text search configuration test; -NOTICE: snitch: command_start DROP TEXT SEARCH CONFIGURATION . -NOTICE: snitch: command_start DROP TEXT SEARCH CONFIGURATION . +NOTICE: snitch: command_start DROP TEXT SEARCH CONFIGURATION [DROP - TEXT SEARCH CONFIGURATION] +NOTICE: snitch: command_start DROP TEXT SEARCH CONFIGURATION [DROP - TEXT SEARCH CONFIGURATION] drop text search dictionary test_stem; -NOTICE: snitch: command_start DROP TEXT SEARCH DICTIONARY . -NOTICE: snitch: command_start DROP TEXT SEARCH DICTIONARY . +NOTICE: snitch: command_start DROP TEXT SEARCH DICTIONARY [DROP - TEXT SEARCH DICTIONARY] +NOTICE: snitch: command_start DROP TEXT SEARCH DICTIONARY [DROP - TEXT SEARCH DICTIONARY] drop text search parser test_parser; -NOTICE: snitch: command_start DROP TEXT SEARCH PARSER . -NOTICE: snitch: command_start DROP TEXT SEARCH PARSER . +NOTICE: snitch: command_start DROP TEXT SEARCH PARSER [DROP - TEXT SEARCH PARSER] +NOTICE: snitch: command_start DROP TEXT SEARCH PARSER [DROP - TEXT SEARCH PARSER] drop text search template test_template; -NOTICE: snitch: command_start DROP TEXT SEARCH TEMPLATE . -NOTICE: snitch: command_start DROP TEXT SEARCH TEMPLATE . +NOTICE: snitch: command_start DROP TEXT SEARCH TEMPLATE [DROP - TEXT SEARCH TEMPLATE] +NOTICE: snitch: command_start DROP TEXT SEARCH TEMPLATE [DROP - TEXT SEARCH TEMPLATE] create function cmd.testcast(text) returns int4 language plpgsql as $$begin return 4::int4;end;$$; -NOTICE: snitch: command_start CREATE FUNCTION . -NOTICE: snitch: command_start CREATE FUNCTION . +NOTICE: snitch: command_start CREATE FUNCTION [CREATE - ] +NOTICE: snitch: command_start CREATE FUNCTION [CREATE - ] create cast (text as int4) with function cmd.testcast(text) as assignment; -NOTICE: snitch: command_start CREATE CAST . -NOTICE: snitch: command_start CREATE CAST . +NOTICE: snitch: command_start CREATE CAST [CREATE - ] +NOTICE: snitch: command_start CREATE CAST [CREATE - ] alter schema cmd rename to cmd1; -NOTICE: snitch: command_start ALTER SCHEMA . -NOTICE: snitch: command_start ALTER SCHEMA . +NOTICE: snitch: command_start ALTER SCHEMA [ALTER - SCHEMA] +NOTICE: snitch: command_start ALTER SCHEMA [ALTER - SCHEMA] drop schema cmd1 cascade; -NOTICE: snitch: command_start DROP SCHEMA . -NOTICE: snitch: command_start DROP SCHEMA . +NOTICE: snitch: command_start DROP SCHEMA [DROP - SCHEMA] +NOTICE: snitch: command_start DROP SCHEMA [DROP - SCHEMA] NOTICE: drop cascades to 12 other objects DETAIL: drop cascades to table cmd1.foo drop cascades to view cmd1.v @@ -452,15 +453,17 @@ drop cascades to function cmd1.trigfunc() drop cascades to function cmd1.testcast(text) drop cascades to cast from text to integer drop schema cmd2 cascade; -NOTICE: snitch: command_start DROP SCHEMA . -NOTICE: snitch: command_start DROP SCHEMA . +NOTICE: snitch: command_start DROP SCHEMA [DROP - SCHEMA] +NOTICE: snitch: command_start DROP SCHEMA [DROP - SCHEMA] -- fail because owning event trigger snitch -drop role regbob; -ERROR: role "regbob" cannot be dropped because some objects depend on it +drop role regression_bob; +ERROR: role "regression_bob" cannot be dropped because some objects depend on it DETAIL: owner of event trigger snitch drop event trigger any_t; -drop event trigger snitch; -drop role regbob; +drop event trigger if exists snitch; +drop event trigger if exists snitch; +NOTICE: event trigger "snitch" does not exist, skipping +drop role regression_bob; create table onerow(id integer); create or replace function insert_one_row() returns event_trigger diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 0dac40f..70eab92 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -134,11 +134,10 @@ WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT (p1.typelem != 0 AND p1.typlen < 0) AND NOT (p2.prorettype = p1.oid AND NOT p2.proretset) ORDER BY 1; - oid | typname | oid | proname -------+---------------+------+------------ - 1790 | refcursor | 46 | textin - 3838 | event_trigger | 2300 | trigger_in -(2 rows) + oid | typname | oid | proname +------+-----------+-----+--------- + 1790 | refcursor | 46 | textin +(1 row) -- Varlena array types will point to array_in -- Exception as of 8.1: int2vector and oidvector have their own I/O routines @@ -178,11 +177,10 @@ WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT (p2.oid = 'array_out'::regproc AND p1.typelem != 0 AND p1.typlen = -1))) ORDER BY 1; - oid | typname | oid | proname -------+---------------+------+------------- - 1790 | refcursor | 47 | textout - 3838 | event_trigger | 2301 | trigger_out -(2 rows) + oid | typname | oid | proname +------+-----------+-----+--------- + 1790 | refcursor | 47 | textout +(1 row) SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 diff --git a/src/test/regress/sql/event_triggers.sql b/src/test/regress/sql/event_triggers.sql index cf25ee7..9ca1a28 100644 --- a/src/test/regress/sql/event_triggers.sql +++ b/src/test/regress/sql/event_triggers.sql @@ -7,14 +7,10 @@ create or replace function snitch() as $$ begin -- can't output tg_objectid here that would break pg_regress - raise notice 'snitch: % % %.%', tg_when, tg_tag, tg_schemaname, tg_objectname; + raise notice 'snitch: % % [% - %]', tg_event, tg_tag, tg_operation, tg_objecttype; end; $$; --- --- TODO: REASSIGN OWNED and DROP OWNED --- - create event trigger any_t on command_start execute procedure snitch(); @@ -68,9 +64,11 @@ alter event trigger foo_t rename to snitch; create schema cmd; create schema cmd2; -create role regbob; -alter event trigger snitch owner to regbob; +create role regression_bob; +alter event trigger snitch owner to regression_bob; +alter role regression_bob superuser; +alter event trigger snitch owner to regression_bob; create table cmd.foo(id bigserial primary key); create view cmd.v as select * from cmd.foo; @@ -103,7 +101,7 @@ alter table cmd.test set with oids; alter table cmd.test set without oids; create sequence test_seq_; -alter sequence test_seq_ owner to regbob; +alter sequence test_seq_ owner to regression_bob; alter sequence test_seq_ rename to test_seq; alter sequence test_seq set schema cmd; alter sequence cmd.test_seq start with 3; @@ -117,7 +115,7 @@ alter sequence cmd.test_seq cycle; alter sequence cmd.test_seq no cycle; create view view_test as select id, things from cmd.test; -alter view view_test owner to regbob; +alter view view_test owner to regression_bob; alter view view_test rename to view_test2; alter view view_test2 set schema cmd; alter view cmd.view_test2 alter column id set default 9; @@ -142,7 +140,7 @@ as $$ select t from cmd.foo where id = $1; $$; alter function fun(int) strict; alter function fun(int) rename to notfun; alter function notfun(int) set schema cmd; -alter function cmd.notfun(int) owner to regbob; +alter function cmd.notfun(int) owner to regression_bob; alter function cmd.notfun(int) cost 77; drop function cmd.notfun(int); @@ -170,7 +168,7 @@ create type cmd.compfoo AS (f1 int, f2 text); alter type cmd.compfoo add attribute f3 text; create type cmd.type_test AS (a integer, b integer, c text); -alter type cmd.type_test owner to regbob; +alter type cmd.type_test owner to regression_bob; alter type cmd.type_test rename to type_test2; alter type cmd.type_test2 set schema public; alter type public.type_test2 rename attribute a to z; @@ -191,7 +189,7 @@ alter domain cmd.us_postal_code drop default; alter domain cmd.us_postal_code drop not null; alter domain cmd.us_postal_code add constraint dummy_constraint check (value ~ '^\d{8}$'); alter domain cmd.us_postal_code drop constraint dummy_constraint; -alter domain cmd.us_postal_code owner to regbob; +alter domain cmd.us_postal_code owner to regression_bob; alter domain cmd.us_postal_code set schema cmd2; drop domain cmd2.us_postal_code; @@ -248,11 +246,12 @@ drop schema cmd1 cascade; drop schema cmd2 cascade; -- fail because owning event trigger snitch -drop role regbob; +drop role regression_bob; drop event trigger any_t; -drop event trigger snitch; -drop role regbob; +drop event trigger if exists snitch; +drop event trigger if exists snitch; +drop role regression_bob; create table onerow(id integer);