*** a/doc/src/sgml/ref/allfiles.sgml --- b/doc/src/sgml/ref/allfiles.sgml *************** *** 8,13 **** Complete list of usable sgml source files in this directory. --- 8,14 ---- + *************** *** 50,55 **** Complete list of usable sgml source files in this directory. --- 51,57 ---- + *************** *** 88,93 **** Complete list of usable sgml source files in this directory. --- 90,96 ---- + *** /dev/null --- b/doc/src/sgml/ref/alter_command_trigger.sgml *************** *** 0 **** --- 1,100 ---- + + + + + ALTER COMMAND TRIGGER + 7 + SQL - Language Statements + + + + ALTER COMMAND TRIGGER + change the definition of a trigger + + + + ALTER COMMAND TRIGGER + + + + + ALTER COMMAND TRIGGER name ON command SET enabled + + where enabled can be one of: + + ENABLE + ENABLE ALWAYS + ENABLE REPLICA + DISABLE + + + + + + Description + + + ALTER COMMAND TRIGGER changes properties of an + existing command trigger. + + + + You must be superuser to alter a command trigger. + + + + + Parameters + + + + name + + + The name of an existing trigger to alter. + + + + + + command + + + The command tag on which this trigger acts. + + + + + + enabled + + + When to enable this trigger. See + the session_replication_role parameter. + + + + + + + + Compatibility + + + ALTER COMMAND TRIGGER is a PostgreSQL + extension of the SQL standard. + + + + + See Also + + + + + + + *** /dev/null --- b/doc/src/sgml/ref/create_command_trigger.sgml *************** *** 0 **** --- 1,323 ---- + + + + + CREATE COMMAND TRIGGER + 7 + SQL - Language Statements + + + + CREATE COMMAND TRIGGER + define a new trigger + + + + CREATE COMMAND TRIGGER + + + + + CREATE COMMAND TRIGGER name { BEFORE | AFTER } ANY COMMAND + EXECUTE PROCEDURE function_name () + + CREATE COMMAND TRIGGER name { BEFORE | AFTER } command [, ... ] + EXECUTE PROCEDURE function_name () + + where command can be one of: + + CREATE SCHEMA + CREATE EXTENSION + CREATE LANGUAGE + CREATE FUNCTION + CREATE TABLE + CREATE SERVER + CREATE FOREIGN TABLE + CREATE FOREIGN DATA WRAPPER + CREATE USER MAPPING + CREATE INDEX + CREATE SEQUENCE + CREATE VIEW + CREATE RULE + CREATE AGGREGATE + CREATE OPERATOR + CREATE COLLATION + CREATE TEXT SEARCH PARSER + CREATE TEXT SEARCH DICTIONARY + CREATE TEXT SEARCH TEMPLATE + CREATE TEXT SEARCH CONFIGURATION + CREATE TYPE + CREATE DOMAIN + CREATE TRIGGER + CREATE CONVERSION + CREATE CAST + CREATE OPERATOR CLASS + CREATE OPERATOR FAMILY + ALTER SCHEMA + ALTER EXTENSION + ALTER FUNCTION + ALTER TABLE + ALTER SERVER + ALTER FOREIGN TABLE + ALTER FOREIGN DATA WRAPPER + ALTER USER MAPPING + ALTER AGGREGATE + ALTER OPERATOR + ALTER OPERATOR CLASS + ALTER OPERATOR FAMILY + ALTER COLLATION + ALTER TEXT SEARCH PARSER + ALTER TEXT SEARCH DICTIONARY + ALTER TEXT SEARCH TEMPLATE + ALTER TEXT SEARCH CONFIGURATION + ALTER TYPE + ALTER DOMAIN + ALTER TRIGGER + DROP TABLE + DROP SEQUENCE + DROP VIEW + DROP INDEX + DROP TYPE + DROP DOMAIN + DROP COLLATION + DROP CONVERSION + DROP SCHEMA + DROP EXTENSION + DROP TEXT SEARCH PARSER + DROP TEXT SEARCH DICTIONARY + DROP TEXT SEARCH TEMPLATE + DROP TEXT SEARCH CONFIGURATION + DROP LANGUAGE + DROP SERVER + DROP FOREIGN TABLE + DROP FOREIGN DATA WRAPPER + DROP USER MAPPING + DROP TRIGGER + DROP ASSERTION + DROP OPERATOR CLASS + DROP OPERATOR FAMILY + DROP FUNCTION + DROP AGGREGATE + DROP OPERATOR + DROP CAST + DROP RULE + REINDEX + VACUUM + CLUSTER + LOAD + + + + + + Description + + + CREATE COMMAND TRIGGER creates a new command trigger. + The trigger will be associated with the specified command and will + execute the specified + function function_name when + that command is run. + + + + The command trigger can be specified to fire before or after the command + is executed. A command trigger's function must + return void, the only it can aborts the execution of + the command is by raising an exception. + + + + Refer to for more information about triggers. + + + + + Parameters + + + + 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 trigger inherits the + schema of its table. For a constraint trigger, this is also the name to + use when modifying the trigger's behavior using + SET CONSTRAINTS. + + + + + + BEFORE + AFTER + + + Determines whether the function is called before or after the command + is executed. + + + + + + command + + + 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 refers to global objects, such as databases, tablespaces + and roles are not supported. As command triggers are per-database, it + would be weird to affect e.g. a tablespace depending on which database + you are connected to. + + + Commands that exercize their own transaction control are only + supported in BEFORE command triggers, that's the + case for VACUUM, CLUSTER + CREATE INDEX CONCURRENTLY, and REINDEX + DATABASE. + + + 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. + + + Command triggers on CREATE COMMAND + TRIGGER, ALTER COMMAND TRIGGER + and DROP COMMAND TRIGGER are not supported so as + not to be able to take over control from a superuser. + + + 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. + + + + + + function_name + + + A user-supplied function that is declared as taking 5 arguments of + type text, text, oid, text, text and returning void. + + + If your command trigger is implemented in C then it + will be called with yet another argument, of + type internal, which is a pointer to + the Node * parse tree. + + + The command trigger function is called with the + parameters tg_when (which is set to either 'BEFORE' + or 'AFTER'), command + tag, objectid (can be null in case of a + BEFORE CREATE or an AFTER DROP command trigger + timing), schemaname (can be null for objects not + living in a schema, and for sequences due to an implementation limit) + and object name (can be null for any command + triggers). + + + The command CREATE SEQUENCE lacks support for + the schemaname command trigger argument, it + provides NULL in all cases. + + + + + + + + + Notes + + + To create a trigger on a command, the user must be superuser. + + + + Use to remove a command trigger. + + + + + Examples + + + Forbids the execution of any DDL command: + + + CREATE OR REPLACE FUNCTION abort_any_command + (tg_when text, cmd_tag text, objectid oid, schemaname text, objectname text) + RETURNS void LANGUAGE plpgsql AS $$ + BEGIN + RAISE EXCEPTION 'command % is disabled' % cmd_tag; + END; + $$; + + CREATE COMMAND TRIGGER abort_ddl + BEFORE ANY COMMAND + EXECUTE PROCEDURE abort_any_command(); + + + Execute the function enforce_local_style each time + a CREATE TABLE command is run: + + + CREATE OR REPLACE FUNCTION enforce_local_style + (tg_when text, cmd_tag text, objectid oid, schemaname text, objectname text) + RETURNS void LANGUAGE plpgsql AS $$ + BEGIN + IF substring(objectname, 0, 4) NOT IN ('ab_', 'cz_', 'fr_') + THEN + RAISE EXCEPTION 'invalid relation name: %', objectname; + END IF; + END; + $$; + + CREATE COMMAND TRIGGER check_style + BEFORE CREATE TABLE + EXECUTE PROCEDURE enforce_local_style(); + + + + + + Compatibility + + + CREATE COMMAND TRIGGER is a + PostgreSQL extension of the SQL + standard. + + + + + + See Also + + + + + + + + *** a/doc/src/sgml/ref/create_trigger.sgml --- b/doc/src/sgml/ref/create_trigger.sgml *************** *** 35,40 **** CREATE [ CONSTRAINT ] TRIGGER name --- 35,41 ---- UPDATE [ OF column_name [, ... ] ] DELETE TRUNCATE + *** /dev/null --- b/doc/src/sgml/ref/drop_command_trigger.sgml *************** *** 0 **** --- 1,204 ---- + + + + + DROP COMMAND TRIGGER + 7 + SQL - Language Statements + + + + DROP COMMAND TRIGGER + remove a command trigger + + + + DROP COMMAND TRIGGER + + + + + DROP COMMAND TRIGGER [ IF EXISTS ] name ON COMMAND command [, ... ] [ CASCADE | RESTRICT ] + DROP COMMAND TRIGGER [ IF EXISTS ] name ON ANY COMMAND [ CASCADE | RESTRICT ] + + where command can be one of: + + CREATE SCHEMA + CREATE EXTENSION + CREATE LANGUAGE + CREATE FUNCTION + CREATE TABLE + CREATE SERVER + CREATE FOREIGN TABLE + CREATE FOREIGN DATA WRAPPER + CREATE USER MAPPING + CREATE INDEX + CREATE SEQUENCE + CREATE VIEW + CREATE RULE + CREATE AGGREGATE + CREATE OPERATOR + CREATE COLLATION + CREATE TEXT SEARCH PARSER + CREATE TEXT SEARCH DICTIONARY + CREATE TEXT SEARCH TEMPLATE + CREATE TEXT SEARCH CONFIGURATION + CREATE TYPE + CREATE DOMAIN + CREATE TRIGGER + CREATE CONVERSION + CREATE CAST + CREATE OPERATOR CLASS + CREATE OPERATOR FAMILY + ALTER SCHEMA + ALTER EXTENSION + ALTER FUNCTION + ALTER TABLE + ALTER SERVER + ALTER FOREIGN TABLE + ALTER FOREIGN DATA WRAPPER + ALTER USER MAPPING + ALTER AGGREGATE + ALTER OPERATOR + ALTER OPERATOR CLASS + ALTER OPERATOR FAMILY + ALTER COLLATION + ALTER TEXT SEARCH PARSER + ALTER TEXT SEARCH DICTIONARY + ALTER TEXT SEARCH TEMPLATE + ALTER TEXT SEARCH CONFIGURATION + ALTER TYPE + ALTER DOMAIN + ALTER TRIGGER + DROP TABLE + DROP SEQUENCE + DROP VIEW + DROP INDEX + DROP TYPE + DROP DOMAIN + DROP COLLATION + DROP CONVERSION + DROP SCHEMA + DROP EXTENSION + DROP TEXT SEARCH PARSER + DROP TEXT SEARCH DICTIONARY + DROP TEXT SEARCH TEMPLATE + DROP TEXT SEARCH CONFIGURATION + DROP LANGUAGE + DROP SERVER + DROP FOREIGN TABLE + DROP FOREIGN DATA WRAPPER + DROP USER MAPPING + DROP TRIGGER + DROP ASSERTION + DROP OPERATOR CLASS + DROP OPERATOR FAMILY + DROP FUNCTION + DROP AGGREGATE + DROP OPERATOR + DROP CAST + DROP RULE + REINDEX + VACUUM + CLUSTER + LOAD + + + + + + Description + + + DROP COMMAND TRIGGER removes an existing trigger definition. + To execute this command, the current user must be superuser. + + + + + Parameters + + + + + IF EXISTS + + + Do not throw an error if the command trigger does not exist. A notice + is issued in this case. + + + + + + name + + + The name of the command trigger to remove. + + + + + + command + + + The name of the command for which the trigger is defined. + + + + + + CASCADE + + + Automatically drop objects that depend on the trigger. + + + + + + RESTRICT + + + Refuse to drop the trigger if any objects depend on it. This is + the default. + + + + + + + + Examples + + + Destroy the trigger snitch on any command: + + + DROP COMMAND TRIGGER snitch ON ANY COMMAND; + + + + + Compatibility + + + The DROP COMMAND TRIGGER statement is a + PostgreSQL extension. + + + + + See Also + + + + + + + + *** a/doc/src/sgml/reference.sgml --- b/doc/src/sgml/reference.sgml *************** *** 62,67 **** --- 62,68 ---- &alterTSParser; &alterTSTemplate; &alterTrigger; + &alterCommandTrigger; &alterType; &alterUser; &alterUserMapping; *************** *** 104,109 **** --- 105,111 ---- &createTSParser; &createTSTemplate; &createTrigger; + &createCommandTrigger; &createType; &createUser; &createUserMapping; *************** *** 142,147 **** --- 144,150 ---- &dropTSParser; &dropTSTemplate; &dropTrigger; + &dropCommandTrigger; &dropType; &dropUser; &dropUserMapping; *** a/doc/src/sgml/trigger.sgml --- b/doc/src/sgml/trigger.sgml *************** *** 27,32 **** --- 27,50 ---- plain SQL function language. + + PostgreSQL offers both triggers on commands + (see ) and triggers on data manipulation + (see ). + + + + Overview of 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 *** a/src/backend/bootstrap/bootparse.y --- b/src/backend/bootstrap/bootparse.y *************** *** 291,297 **** Boot_DeclareIndexStmt: $10, NULL, NIL, NIL, false, false, false, false, false, ! false, false, true, false, false); do_end(); } ; --- 291,297 ---- $10, NULL, NIL, NIL, false, false, false, false, false, ! false, false, true, false, false, NULL); do_end(); } ; *************** *** 310,316 **** Boot_DeclareUniqueIndexStmt: $11, NULL, NIL, NIL, true, false, false, false, false, ! false, false, true, false, false); do_end(); } ; --- 310,316 ---- $11, NULL, NIL, NIL, true, false, false, false, false, ! false, false, true, false, false, NULL); do_end(); } ; *** a/src/backend/catalog/Makefile --- b/src/backend/catalog/Makefile *************** *** 31,37 **** POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \ pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \ pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \ ! pg_statistic.h pg_rewrite.h pg_trigger.h pg_description.h \ pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \ pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ --- 31,37 ---- pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \ pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \ pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \ ! pg_statistic.h pg_rewrite.h pg_trigger.h pg_cmdtrigger.h pg_description.h \ pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \ pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ *** a/src/backend/catalog/dependency.c --- b/src/backend/catalog/dependency.c *************** *** 25,30 **** --- 25,31 ---- #include "catalog/pg_attrdef.h" #include "catalog/pg_authid.h" #include "catalog/pg_cast.h" + #include "catalog/pg_cmdtrigger.h" #include "catalog/pg_collation.h" #include "catalog/pg_collation_fn.h" #include "catalog/pg_constraint.h" *************** *** 52,57 **** --- 53,59 ---- #include "catalog/pg_ts_template.h" #include "catalog/pg_type.h" #include "catalog/pg_user_mapping.h" + #include "commands/cmdtrigger.h" #include "commands/comment.h" #include "commands/dbcommands.h" #include "commands/defrem.h" *************** *** 157,163 **** static const Oid object_classes[MAX_OCLASS] = { ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */ UserMappingRelationId, /* OCLASS_USER_MAPPING */ DefaultAclRelationId, /* OCLASS_DEFACL */ ! ExtensionRelationId /* OCLASS_EXTENSION */ }; --- 159,166 ---- ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */ UserMappingRelationId, /* OCLASS_USER_MAPPING */ DefaultAclRelationId, /* OCLASS_DEFACL */ ! ExtensionRelationId, /* OCLASS_EXTENSION */ ! CmdTriggerRelationId /* OCLASS_CMDTRIGGER */ }; *************** *** 1067,1072 **** doDeletion(const ObjectAddress *object) --- 1070,1079 ---- break; } + case OCLASS_CMDTRIGGER: + RemoveCmdTriggerById(object->objectId); + break; + case OCLASS_PROC: RemoveFunctionById(object->objectId); break; *************** *** 2188,2193 **** getObjectClass(const ObjectAddress *object) --- 2195,2203 ---- case ExtensionRelationId: return OCLASS_EXTENSION; + + case CmdTriggerRelationId: + return OCLASS_CMDTRIGGER; } /* shouldn't get here */ *************** *** 2822,2827 **** getObjectDescription(const ObjectAddress *object) --- 2832,2876 ---- break; } + case OCLASS_CMDTRIGGER: + { + Relation trigDesc; + ScanKeyData skey[1]; + SysScanDesc tgscan; + HeapTuple tup; + Form_pg_cmdtrigger trig; + + trigDesc = heap_open(CmdTriggerRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(object->objectId)); + + tgscan = systable_beginscan(trigDesc, CmdTriggerOidIndexId, true, + SnapshotNow, 1, skey); + + tup = systable_getnext(tgscan); + + if (!HeapTupleIsValid(tup)) + elog(ERROR, "could not find tuple for command trigger %u", + object->objectId); + + trig = (Form_pg_cmdtrigger) GETSTRUCT(tup); + + if (strcmp(NameStr(trig->ctgcommand), "ANY") == 0) + appendStringInfo(&buffer, _("trigger %s on any command"), + NameStr(trig->ctgname)); + else + appendStringInfo(&buffer, _("trigger %s on command %s"), + NameStr(trig->ctgname), + NameStr(trig->ctgcommand)); + + systable_endscan(tgscan); + heap_close(trigDesc, AccessShareLock); + break; + } + default: appendStringInfo(&buffer, "unrecognized object %u %u %d", object->classId, *** a/src/backend/catalog/index.c --- b/src/backend/catalog/index.c *************** *** 2789,2795 **** IndexGetRelation(Oid indexId, bool missing_ok) * reindex_index - This routine is used to recreate a single index */ void ! reindex_index(Oid indexId, bool skip_constraint_checks) { Relation iRel, heapRelation, --- 2789,2795 ---- * reindex_index - This routine is used to recreate a single index */ void ! reindex_index(Oid indexId, bool skip_constraint_checks, CommandContext cmd) { Relation iRel, heapRelation, *************** *** 2828,2833 **** reindex_index(Oid indexId, bool skip_constraint_checks) --- 2828,2843 ---- */ CheckTableNotInUse(iRel, "REINDEX INDEX"); + /* Call BEFORE REINDEX command triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = indexId; + cmd->objectname = RelationGetRelationName(heapRelation); + cmd->schemaname = get_namespace_name(RelationGetNamespace(heapRelation)); + + ExecBeforeCommandTriggers(cmd); + } + /* * All predicate locks on the index are about to be made invalid. Promote * them to relation locks on the heap. *************** *** 2923,2928 **** reindex_index(Oid indexId, bool skip_constraint_checks) --- 2933,2942 ---- /* Close rels, but keep locks */ index_close(iRel, NoLock); heap_close(heapRelation, NoLock); + + /* Call AFTER REINDEX command triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); } /* *************** *** 2955,2961 **** reindex_index(Oid indexId, bool skip_constraint_checks) * index rebuild. */ bool ! reindex_relation(Oid relid, int flags) { Relation rel; Oid toast_relid; --- 2969,2975 ---- * index rebuild. */ bool ! reindex_relation(Oid relid, int flags, CommandContext cmd) { Relation rel; Oid toast_relid; *************** *** 2972,2977 **** reindex_relation(Oid relid, int flags) --- 2986,3002 ---- toast_relid = rel->rd_rel->reltoastrelid; + /* Call BEFORE REINDEX command triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = relid; + cmd->objectname = RelationGetRelationName(rel); + cmd->objectname = get_namespace_name(RelationGetNamespace(rel)); + + ExecBeforeCommandTriggers(cmd); + } + + /* * Get the list of index OIDs for this relation. (We trust to the * relcache to get this with a sequential scan if ignoring system *************** *** 3033,3039 **** reindex_relation(Oid relid, int flags) if (is_pg_class) RelationSetIndexList(rel, doneIndexes, InvalidOid); ! reindex_index(indexOid, !(flags & REINDEX_REL_CHECK_CONSTRAINTS)); CommandCounterIncrement(); --- 3058,3064 ---- if (is_pg_class) RelationSetIndexList(rel, doneIndexes, InvalidOid); ! reindex_index(indexOid, !(flags & REINDEX_REL_CHECK_CONSTRAINTS), NULL); CommandCounterIncrement(); *************** *** 3068,3074 **** reindex_relation(Oid relid, int flags) * still hold the lock on the master table. */ if ((flags & REINDEX_REL_PROCESS_TOAST) && OidIsValid(toast_relid)) ! result |= reindex_relation(toast_relid, flags); return result; } --- 3093,3103 ---- * still hold the lock on the master table. */ if ((flags & REINDEX_REL_PROCESS_TOAST) && OidIsValid(toast_relid)) ! result |= reindex_relation(toast_relid, flags, NULL); ! ! /* Call AFTER REINDEX command triggers */ ! if (CommandFiresAfterTriggers(cmd)) ! ExecAfterCommandTriggers(cmd); return result; } *** a/src/backend/catalog/objectaddress.c --- b/src/backend/catalog/objectaddress.c *************** *** 21,26 **** --- 21,27 ---- #include "catalog/objectaddress.h" #include "catalog/pg_authid.h" #include "catalog/pg_cast.h" + #include "catalog/pg_cmdtrigger.h" #include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_conversion.h" *************** *** 44,49 **** --- 45,51 ---- #include "catalog/pg_ts_parser.h" #include "catalog/pg_ts_template.h" #include "catalog/pg_type.h" + #include "commands/cmdtrigger.h" #include "commands/dbcommands.h" #include "commands/defrem.h" #include "commands/extension.h" *************** *** 204,209 **** static ObjectPropertyType ObjectProperty[] = --- 206,217 ---- InvalidAttrNumber }, { + CmdTriggerRelationId, + CmdTriggerOidIndexId, + -1, + InvalidAttrNumber + }, + { TSConfigRelationId, TSConfigOidIndexId, TSCONFIGOID, *************** *** 249,254 **** static ObjectAddress get_object_address_type(ObjectType objtype, --- 257,264 ---- List *objname, bool missing_ok); static ObjectAddress get_object_address_opcf(ObjectType objtype, List *objname, List *objargs, bool missing_ok); + static ObjectAddress get_object_address_cmdtrigger(ObjectType objtype, + List *objname, List *objargs, bool missing_ok); static ObjectPropertyType *get_object_property_data(Oid class_id); /* *************** *** 292,298 **** get_object_address(ObjectType objtype, List *objname, List *objargs, */ inval_count = SharedInvalidMessageCounter; ! /* Look up object address. */ switch (objtype) { case OBJECT_INDEX: --- 302,308 ---- */ inval_count = SharedInvalidMessageCounter; ! /* Look up object address. */ switch (objtype) { case OBJECT_INDEX: *************** *** 317,322 **** get_object_address(ObjectType objtype, List *objname, List *objargs, --- 327,336 ---- address = get_object_address_relobject(objtype, objname, &relation, missing_ok); break; + case OBJECT_CMDTRIGGER: + address = get_object_address_cmdtrigger(objtype, objname, + objargs, missing_ok); + break; case OBJECT_DATABASE: case OBJECT_EXTENSION: case OBJECT_TABLESPACE: *************** *** 902,907 **** get_object_address_opcf(ObjectType objtype, --- 916,945 ---- } /* + * Find the ObjectAddress for a command trigger. + */ + static ObjectAddress + get_object_address_cmdtrigger(ObjectType objtype, + List *objname, List *objargs, bool missing_ok) + { + char *name; + char *command; + ObjectAddress address; + + Assert(list_length(objname) == 1); /* command triggers are not schema qualified */ + Assert(list_length(objargs) == 1); + + name = strVal(linitial(objname)); + command = strVal(linitial(objargs)); + + address.classId = CmdTriggerRelationId; + address.objectId = get_cmdtrigger_oid(name, command, missing_ok); + address.objectSubId = 0; + + return address; + } + + /* * Check ownership of an object previously identified by get_object_address. */ void *************** *** 1054,1059 **** check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, --- 1092,1098 ---- break; case OBJECT_TSPARSER: case OBJECT_TSTEMPLATE: + case OBJECT_CMDTRIGGER: /* We treat these object types as being owned by superusers */ if (!superuser_arg(roleid)) ereport(ERROR, *** a/src/backend/catalog/pg_aggregate.c --- b/src/backend/catalog/pg_aggregate.c *************** *** 23,28 **** --- 23,29 ---- #include "catalog/pg_proc.h" #include "catalog/pg_proc_fn.h" #include "catalog/pg_type.h" + #include "commands/cmdtrigger.h" #include "miscadmin.h" #include "parser/parse_coerce.h" #include "parser/parse_func.h" *************** *** 50,56 **** AggregateCreate(const char *aggName, List *aggfinalfnName, List *aggsortopName, Oid aggTransType, ! const char *agginitval) { Relation aggdesc; HeapTuple tup; --- 51,58 ---- List *aggfinalfnName, List *aggsortopName, Oid aggTransType, ! const char *agginitval, ! CommandContext cmd) { Relation aggdesc; HeapTuple tup; *************** *** 222,227 **** AggregateCreate(const char *aggName, --- 224,240 ---- aclcheck_error(aclresult, ACL_KIND_TYPE, format_type_be(finaltype)); + /* + * Call BEFORE CREATE AGGREGATE triggers + */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = InvalidOid; + cmd->objectname = (char *)aggName; + cmd->schemaname = get_namespace_name(aggNamespace); + + ExecBeforeCommandTriggers(cmd); + } /* * Everything looks okay. Try to create the pg_proc entry for the *************** *** 317,322 **** AggregateCreate(const char *aggName, --- 330,342 ---- referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } + + /* Call AFTER CREATE AGGREGATE triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectId = procOid; + ExecAfterCommandTriggers(cmd); + } } /* *** a/src/backend/catalog/pg_collation.c --- b/src/backend/catalog/pg_collation.c *************** *** 23,32 **** --- 23,34 ---- #include "catalog/pg_collation.h" #include "catalog/pg_collation_fn.h" #include "catalog/pg_namespace.h" + #include "commands/cmdtrigger.h" #include "mb/pg_wchar.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/rel.h" + #include "utils/lsyscache.h" #include "utils/syscache.h" #include "utils/tqual.h" *************** *** 40,46 **** Oid CollationCreate(const char *collname, Oid collnamespace, Oid collowner, int32 collencoding, ! const char *collcollate, const char *collctype) { Relation rel; TupleDesc tupDesc; --- 42,49 ---- CollationCreate(const char *collname, Oid collnamespace, Oid collowner, int32 collencoding, ! const char *collcollate, const char *collctype, ! CommandContext cmd) { Relation rel; TupleDesc tupDesc; *************** *** 90,95 **** CollationCreate(const char *collname, Oid collnamespace, --- 93,110 ---- errmsg("collation \"%s\" already exists", collname))); + /* + * Call BEFORE CREATE COLLATION triggers + */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = InvalidOid; + cmd->objectname = (char *)collname; + cmd->schemaname = get_namespace_name(collnamespace); + + ExecBeforeCommandTriggers(cmd); + } + /* open pg_collation */ rel = heap_open(CollationRelationId, RowExclusiveLock); tupDesc = RelationGetDescr(rel); *************** *** 141,146 **** CollationCreate(const char *collname, Oid collnamespace, --- 156,167 ---- heap_freetuple(tup); heap_close(rel, RowExclusiveLock); + /* Call AFTER CREATE COLLATION triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectId = oid; + ExecAfterCommandTriggers(cmd); + } return oid; } *** a/src/backend/catalog/pg_operator.c --- b/src/backend/catalog/pg_operator.c *************** *** 336,342 **** OperatorCreate(const char *operatorName, Oid restrictionId, Oid joinId, bool canMerge, ! bool canHash) { Relation pg_operator_desc; HeapTuple tup; --- 336,343 ---- Oid restrictionId, Oid joinId, bool canMerge, ! bool canHash, ! CommandContext cmd) { Relation pg_operator_desc; HeapTuple tup; *************** *** 433,438 **** OperatorCreate(const char *operatorName, --- 434,451 ---- operatorName); /* + * Call BEFORE CREATE AGGREGARE triggers + */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = InvalidOid; + cmd->objectname = (char *)operatorName; + cmd->schemaname = get_namespace_name(operatorNamespace); + + ExecBeforeCommandTriggers(cmd); + } + + /* * Set up the other operators. If they do not currently exist, create * shells in order to get ObjectId's. */ *************** *** 564,569 **** OperatorCreate(const char *operatorName, --- 577,589 ---- if (OidIsValid(commutatorId) || OidIsValid(negatorId)) OperatorUpd(operatorObjectId, commutatorId, negatorId); + + /* Call AFTER CREATE OPERATOR triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectId = operatorObjectId; + ExecAfterCommandTriggers(cmd); + } } /* *** a/src/backend/catalog/pg_shdepend.c --- b/src/backend/catalog/pg_shdepend.c *************** *** 1327,1337 **** shdepReassignOwned(List *roleids, Oid newrole) switch (sdepForm->classid) { case CollationRelationId: ! AlterCollationOwner_oid(sdepForm->objid, newrole); break; case ConversionRelationId: ! AlterConversionOwner_oid(sdepForm->objid, newrole); break; case TypeRelationId: --- 1327,1337 ---- switch (sdepForm->classid) { case CollationRelationId: ! AlterCollationOwner_oid(sdepForm->objid, newrole, NULL); break; case ConversionRelationId: ! AlterConversionOwner_oid(sdepForm->objid, newrole, NULL); break; case TypeRelationId: *************** *** 1357,1363 **** shdepReassignOwned(List *roleids, Oid newrole) break; case ProcedureRelationId: ! AlterFunctionOwner_oid(sdepForm->objid, newrole); break; case LanguageRelationId: --- 1357,1363 ---- break; case ProcedureRelationId: ! AlterFunctionOwner_oid(sdepForm->objid, newrole, NULL); break; case LanguageRelationId: *** a/src/backend/catalog/pg_type.c --- b/src/backend/catalog/pg_type.c *************** *** 676,682 **** GenerateTypeDependencies(Oid typeNamespace, * ALTER TYPE RENAME TO command. */ void ! RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace) { Relation pg_type_desc; HeapTuple tuple; --- 676,683 ---- * ALTER TYPE RENAME TO command. */ void ! RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace, ! CommandContext cmd) { Relation pg_type_desc; HeapTuple tuple; *************** *** 703,708 **** RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace) --- 704,719 ---- (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("type \"%s\" already exists", newTypeName))); + /* Call BEFORE ALTER TYPE triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = typeOid; + cmd->objectname = NameStr(typ->typname); + cmd->schemaname = get_namespace_name(typeNamespace); + + ExecBeforeCommandTriggers(cmd); + } + /* OK, do the rename --- tuple is a copy, so OK to scribble on it */ namestrcpy(&(typ->typname), newTypeName); *************** *** 719,727 **** RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace) { char *arrname = makeArrayTypeName(newTypeName, typeNamespace); ! RenameTypeInternal(arrayOid, arrname, typeNamespace); pfree(arrname); } } --- 730,745 ---- { char *arrname = makeArrayTypeName(newTypeName, typeNamespace); ! RenameTypeInternal(arrayOid, arrname, typeNamespace, NULL); pfree(arrname); } + + /* Call AFTER ALTER TYPE triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectname = (char *)newTypeName; + ExecAfterCommandTriggers(cmd); + } } *************** *** 822,828 **** moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace) newname = makeArrayTypeName(typeName, typeNamespace); /* Apply the rename */ ! RenameTypeInternal(typeOid, newname, typeNamespace); /* * We must bump the command counter so that any subsequent use of --- 840,846 ---- newname = makeArrayTypeName(typeName, typeNamespace); /* Apply the rename */ ! RenameTypeInternal(typeOid, newname, typeNamespace, NULL); /* * We must bump the command counter so that any subsequent use of *** a/src/backend/commands/Makefile --- b/src/backend/commands/Makefile *************** *** 12,19 **** subdir = src/backend/commands top_builddir = ../../.. include $(top_builddir)/src/Makefile.global ! OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \ ! collationcmds.o constraint.o conversioncmds.o copy.o \ dbcommands.o define.o discard.o dropcmds.o explain.o extension.o \ foreigncmds.o functioncmds.o \ indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \ --- 12,19 ---- top_builddir = ../../.. include $(top_builddir)/src/Makefile.global ! OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o cmdtrigger.o \ ! comment.o collationcmds.o constraint.o conversioncmds.o copy.o \ dbcommands.o define.o discard.o dropcmds.o explain.o extension.o \ foreigncmds.o functioncmds.o \ indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \ *** a/src/backend/commands/aggregatecmds.c --- b/src/backend/commands/aggregatecmds.c *************** *** 28,33 **** --- 28,34 ---- #include "catalog/pg_aggregate.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" + #include "commands/cmdtrigger.h" #include "commands/defrem.h" #include "miscadmin.h" #include "parser/parse_func.h" *************** *** 46,52 **** * "args" defines the input type(s). */ void ! DefineAggregate(List *name, List *args, bool oldstyle, List *parameters) { char *aggName; Oid aggNamespace; --- 47,54 ---- * "args" defines the input type(s). */ void ! DefineAggregate(List *name, List *args, bool oldstyle, List *parameters, ! CommandContext cmd) { char *aggName; Oid aggNamespace; *************** *** 203,209 **** DefineAggregate(List *name, List *args, bool oldstyle, List *parameters) finalfuncName, /* final function name */ sortoperatorName, /* sort operator name */ transTypeId, /* transition data type */ ! initval); /* initial condition */ } --- 205,212 ---- finalfuncName, /* final function name */ sortoperatorName, /* sort operator name */ transTypeId, /* transition data type */ ! initval, /* initial condition */ ! cmd); } *************** *** 212,218 **** DefineAggregate(List *name, List *args, bool oldstyle, List *parameters) * Rename an aggregate. */ void ! RenameAggregate(List *name, List *args, const char *newname) { Oid procOid; Oid namespaceOid; --- 215,221 ---- * Rename an aggregate. */ void ! RenameAggregate(List *name, List *args, const char *newname, CommandContext cmd) { Oid procOid; Oid namespaceOid; *************** *** 258,263 **** RenameAggregate(List *name, List *args, const char *newname) --- 261,276 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* Call BEFORE ALTER AGGREGATE triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = NameStr(procForm->proname); + cmd->schemaname = get_namespace_name(namespaceOid); + + ExecBeforeCommandTriggers(cmd); + } + /* rename */ namestrcpy(&(((Form_pg_proc) GETSTRUCT(tup))->proname), newname); simple_heap_update(rel, &tup->t_self, tup); *************** *** 265,277 **** RenameAggregate(List *name, List *args, const char *newname) heap_close(rel, NoLock); heap_freetuple(tup); } /* * Change aggregate owner */ void ! AlterAggregateOwner(List *name, List *args, Oid newOwnerId) { Oid procOid; --- 278,297 ---- heap_close(rel, NoLock); heap_freetuple(tup); + + /* Call AFTER ALTER AGGREGATE triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectname = (char *)newname; + ExecAfterCommandTriggers(cmd); + } } /* * Change aggregate owner */ void ! AlterAggregateOwner(List *name, List *args, Oid newOwnerId, CommandContext cmd) { Oid procOid; *************** *** 279,283 **** AlterAggregateOwner(List *name, List *args, Oid newOwnerId) procOid = LookupAggNameTypeNames(name, args, false); /* The rest is just like a function */ ! AlterFunctionOwner_oid(procOid, newOwnerId); } --- 299,303 ---- procOid = LookupAggNameTypeNames(name, args, false); /* The rest is just like a function */ ! AlterFunctionOwner_oid(procOid, newOwnerId, cmd); } *** a/src/backend/commands/alter.c --- b/src/backend/commands/alter.c *************** *** 20,25 **** --- 20,26 ---- #include "catalog/pg_largeobject.h" #include "catalog/pg_namespace.h" #include "commands/alter.h" + #include "commands/cmdtrigger.h" #include "commands/collationcmds.h" #include "commands/conversioncmds.h" #include "commands/dbcommands.h" *************** *** 47,64 **** void ExecRenameStmt(RenameStmt *stmt) { switch (stmt->renameType) { case OBJECT_AGGREGATE: ! RenameAggregate(stmt->object, stmt->objarg, stmt->newname); break; case OBJECT_COLLATION: ! RenameCollation(stmt->object, stmt->newname); break; case OBJECT_CONVERSION: ! RenameConversion(stmt->object, stmt->newname); break; case OBJECT_DATABASE: --- 48,72 ---- void ExecRenameStmt(RenameStmt *stmt) { + CommandContextData cmd; + InitCommandContext(&cmd, (Node *)stmt, false); + switch (stmt->renameType) { case OBJECT_AGGREGATE: ! RenameAggregate(stmt->object, stmt->objarg, stmt->newname, &cmd); break; case OBJECT_COLLATION: ! RenameCollation(stmt->object, stmt->newname, &cmd); break; case OBJECT_CONVERSION: ! RenameConversion(stmt->object, stmt->newname, &cmd); ! break; ! ! case OBJECT_CMDTRIGGER: ! RenameCmdTrigger(stmt->object, stmt->subname, stmt->newname); break; case OBJECT_DATABASE: *************** *** 74,80 **** ExecRenameStmt(RenameStmt *stmt) break; case OBJECT_FUNCTION: ! RenameFunction(stmt->object, stmt->objarg, stmt->newname); break; case OBJECT_LANGUAGE: --- 82,88 ---- break; case OBJECT_FUNCTION: ! RenameFunction(stmt->object, stmt->objarg, stmt->newname, &cmd); break; case OBJECT_LANGUAGE: *************** *** 94,100 **** ExecRenameStmt(RenameStmt *stmt) break; case OBJECT_SCHEMA: ! RenameSchema(stmt->subname, stmt->newname); break; case OBJECT_TABLESPACE: --- 102,108 ---- break; case OBJECT_SCHEMA: ! RenameSchema(stmt->subname, stmt->newname, &cmd); break; case OBJECT_TABLESPACE: *************** *** 106,112 **** ExecRenameStmt(RenameStmt *stmt) case OBJECT_VIEW: case OBJECT_INDEX: case OBJECT_FOREIGN_TABLE: ! RenameRelation(stmt); break; case OBJECT_COLUMN: --- 114,120 ---- case OBJECT_VIEW: case OBJECT_INDEX: case OBJECT_FOREIGN_TABLE: ! RenameRelation(stmt, &cmd); break; case OBJECT_COLUMN: *************** *** 115,142 **** ExecRenameStmt(RenameStmt *stmt) break; case OBJECT_TRIGGER: ! renametrig(stmt); break; case OBJECT_TSPARSER: ! RenameTSParser(stmt->object, stmt->newname); break; case OBJECT_TSDICTIONARY: ! RenameTSDictionary(stmt->object, stmt->newname); break; case OBJECT_TSTEMPLATE: ! RenameTSTemplate(stmt->object, stmt->newname); break; case OBJECT_TSCONFIGURATION: ! RenameTSConfiguration(stmt->object, stmt->newname); break; case OBJECT_DOMAIN: case OBJECT_TYPE: ! RenameType(stmt); break; default: --- 123,150 ---- break; case OBJECT_TRIGGER: ! renametrig(stmt, &cmd); break; case OBJECT_TSPARSER: ! RenameTSParser(stmt->object, stmt->newname, &cmd); break; case OBJECT_TSDICTIONARY: ! RenameTSDictionary(stmt->object, stmt->newname, &cmd); break; case OBJECT_TSTEMPLATE: ! RenameTSTemplate(stmt->object, stmt->newname, &cmd); break; case OBJECT_TSCONFIGURATION: ! RenameTSConfiguration(stmt->object, stmt->newname, &cmd); break; case OBJECT_DOMAIN: case OBJECT_TYPE: ! RenameType(stmt, &cmd); break; default: *************** *** 152,191 **** ExecRenameStmt(RenameStmt *stmt) void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt) { switch (stmt->objectType) { case OBJECT_AGGREGATE: AlterFunctionNamespace(stmt->object, stmt->objarg, true, ! stmt->newschema); break; case OBJECT_COLLATION: ! AlterCollationNamespace(stmt->object, stmt->newschema); break; case OBJECT_CONVERSION: ! AlterConversionNamespace(stmt->object, stmt->newschema); break; case OBJECT_EXTENSION: ! AlterExtensionNamespace(stmt->object, stmt->newschema); break; case OBJECT_FUNCTION: AlterFunctionNamespace(stmt->object, stmt->objarg, false, ! stmt->newschema); break; case OBJECT_OPERATOR: ! AlterOperatorNamespace(stmt->object, stmt->objarg, stmt->newschema); break; case OBJECT_OPCLASS: ! AlterOpClassNamespace(stmt->object, stmt->addname, stmt->newschema); break; case OBJECT_OPFAMILY: ! AlterOpFamilyNamespace(stmt->object, stmt->addname, stmt->newschema); break; case OBJECT_SEQUENCE: --- 160,202 ---- void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt) { + CommandContextData cmd; + InitCommandContext(&cmd, (Node *)stmt, false); + switch (stmt->objectType) { case OBJECT_AGGREGATE: AlterFunctionNamespace(stmt->object, stmt->objarg, true, ! stmt->newschema, &cmd); break; case OBJECT_COLLATION: ! AlterCollationNamespace(stmt->object, stmt->newschema, &cmd); break; case OBJECT_CONVERSION: ! AlterConversionNamespace(stmt->object, stmt->newschema, &cmd); break; case OBJECT_EXTENSION: ! AlterExtensionNamespace(stmt->object, stmt->newschema, &cmd); break; case OBJECT_FUNCTION: AlterFunctionNamespace(stmt->object, stmt->objarg, false, ! stmt->newschema, &cmd); break; case OBJECT_OPERATOR: ! AlterOperatorNamespace(stmt->object, stmt->objarg, stmt->newschema, &cmd); break; case OBJECT_OPCLASS: ! AlterOpClassNamespace(stmt->object, stmt->addname, stmt->newschema, &cmd); break; case OBJECT_OPFAMILY: ! AlterOpFamilyNamespace(stmt->object, stmt->addname, stmt->newschema, &cmd); break; case OBJECT_SEQUENCE: *************** *** 196,219 **** ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt) break; case OBJECT_TSPARSER: ! AlterTSParserNamespace(stmt->object, stmt->newschema); break; case OBJECT_TSDICTIONARY: ! AlterTSDictionaryNamespace(stmt->object, stmt->newschema); break; case OBJECT_TSTEMPLATE: ! AlterTSTemplateNamespace(stmt->object, stmt->newschema); break; case OBJECT_TSCONFIGURATION: ! AlterTSConfigurationNamespace(stmt->object, stmt->newschema); break; case OBJECT_TYPE: case OBJECT_DOMAIN: ! AlterTypeNamespace(stmt->object, stmt->newschema, stmt->objectType); break; default: --- 207,230 ---- break; case OBJECT_TSPARSER: ! AlterTSParserNamespace(stmt->object, stmt->newschema, &cmd); break; case OBJECT_TSDICTIONARY: ! AlterTSDictionaryNamespace(stmt->object, stmt->newschema, &cmd); break; case OBJECT_TSTEMPLATE: ! AlterTSTemplateNamespace(stmt->object, stmt->newschema, &cmd); break; case OBJECT_TSCONFIGURATION: ! AlterTSConfigurationNamespace(stmt->object, stmt->newschema, &cmd); break; case OBJECT_TYPE: case OBJECT_DOMAIN: ! AlterTypeNamespace(stmt->object, stmt->newschema, stmt->objectType, &cmd); break; default: *************** *** 235,240 **** ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt) --- 246,254 ---- * * Returns the OID of the object's previous namespace, or InvalidOid if * object doesn't have a schema. + * + * Doesn't run any command trigger for those sub-commands, so just pass a NULL + * CommandContext to functions implementing the ALTER. */ Oid AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid) *************** *** 271,285 **** AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid) } case OCLASS_PROC: ! oldNspOid = AlterFunctionNamespace_oid(objid, nspOid); break; case OCLASS_TYPE: ! oldNspOid = AlterTypeNamespace_oid(objid, nspOid); break; case OCLASS_COLLATION: ! oldNspOid = AlterCollationNamespace_oid(objid, nspOid); break; case OCLASS_CONVERSION: --- 285,299 ---- } case OCLASS_PROC: ! oldNspOid = AlterFunctionNamespace_oid(objid, nspOid, NULL); break; case OCLASS_TYPE: ! oldNspOid = AlterTypeNamespace_oid(objid, nspOid, NULL); break; case OCLASS_COLLATION: ! oldNspOid = AlterCollationNamespace_oid(objid, nspOid, NULL); break; case OCLASS_CONVERSION: *************** *** 349,355 **** Oid AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId, Oid objid, Oid nspOid, int Anum_name, int Anum_namespace, int Anum_owner, ! AclObjectKind acl_kind) { Oid classId = RelationGetRelid(rel); Oid oldNspOid; --- 363,369 ---- AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId, Oid objid, Oid nspOid, int Anum_name, int Anum_namespace, int Anum_owner, ! AclObjectKind acl_kind, CommandContext cmd) { Oid classId = RelationGetRelid(rel); Oid oldNspOid; *************** *** 419,424 **** AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId, --- 433,448 ---- getObjectDescriptionOids(classId, objid), get_namespace_name(nspOid)))); + /* Call BEFORE ALTER OBJECT triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = objid; + cmd->objectname = NameStr(*(DatumGetName(name))); + cmd->schemaname = get_namespace_name(oldNspOid); + + ExecBeforeCommandTriggers(cmd); + } + /* Build modified tuple */ values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum)); nulls = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool)); *************** *** 441,446 **** AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId, --- 465,476 ---- changeDependencyFor(classId, objid, NamespaceRelationId, oldNspOid, nspOid); + /* Call AFTER ALTER OBJECT triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->schemaname = get_namespace_name(nspOid); + ExecAfterCommandTriggers(cmd); + } return oldNspOid; } *************** *** 453,471 **** void ExecAlterOwnerStmt(AlterOwnerStmt *stmt) { Oid newowner = get_role_oid(stmt->newowner, false); switch (stmt->objectType) { case OBJECT_AGGREGATE: ! AlterAggregateOwner(stmt->object, stmt->objarg, newowner); break; case OBJECT_COLLATION: ! AlterCollationOwner(stmt->object, newowner); break; case OBJECT_CONVERSION: ! AlterConversionOwner(stmt->object, newowner); break; case OBJECT_DATABASE: --- 483,504 ---- ExecAlterOwnerStmt(AlterOwnerStmt *stmt) { Oid newowner = get_role_oid(stmt->newowner, false); + CommandContextData cmd; + + InitCommandContext(&cmd, (Node *)stmt, false); switch (stmt->objectType) { case OBJECT_AGGREGATE: ! AlterAggregateOwner(stmt->object, stmt->objarg, newowner, &cmd); break; case OBJECT_COLLATION: ! AlterCollationOwner(stmt->object, newowner, &cmd); break; case OBJECT_CONVERSION: ! AlterConversionOwner(stmt->object, newowner, &cmd); break; case OBJECT_DATABASE: *************** *** 473,479 **** ExecAlterOwnerStmt(AlterOwnerStmt *stmt) break; case OBJECT_FUNCTION: ! AlterFunctionOwner(stmt->object, stmt->objarg, newowner); break; case OBJECT_LANGUAGE: --- 506,512 ---- break; case OBJECT_FUNCTION: ! AlterFunctionOwner(stmt->object, stmt->objarg, newowner, &cmd); break; case OBJECT_LANGUAGE: *************** *** 489,495 **** ExecAlterOwnerStmt(AlterOwnerStmt *stmt) AlterOperatorOwner(stmt->object, (TypeName *) linitial(stmt->objarg), (TypeName *) lsecond(stmt->objarg), ! newowner); break; case OBJECT_OPCLASS: --- 522,528 ---- AlterOperatorOwner(stmt->object, (TypeName *) linitial(stmt->objarg), (TypeName *) lsecond(stmt->objarg), ! newowner, &cmd); break; case OBJECT_OPCLASS: *************** *** 501,507 **** ExecAlterOwnerStmt(AlterOwnerStmt *stmt) break; case OBJECT_SCHEMA: ! AlterSchemaOwner(strVal(linitial(stmt->object)), newowner); break; case OBJECT_TABLESPACE: --- 534,540 ---- break; case OBJECT_SCHEMA: ! AlterSchemaOwner(strVal(linitial(stmt->object)), newowner, &cmd); break; case OBJECT_TABLESPACE: *************** *** 510,524 **** ExecAlterOwnerStmt(AlterOwnerStmt *stmt) case OBJECT_TYPE: case OBJECT_DOMAIN: /* same as TYPE */ ! AlterTypeOwner(stmt->object, newowner, stmt->objectType); break; case OBJECT_TSDICTIONARY: ! AlterTSDictionaryOwner(stmt->object, newowner); break; case OBJECT_TSCONFIGURATION: ! AlterTSConfigurationOwner(stmt->object, newowner); break; case OBJECT_FDW: --- 543,557 ---- case OBJECT_TYPE: case OBJECT_DOMAIN: /* same as TYPE */ ! AlterTypeOwner(stmt->object, newowner, stmt->objectType, &cmd); break; case OBJECT_TSDICTIONARY: ! AlterTSDictionaryOwner(stmt->object, newowner, &cmd); break; case OBJECT_TSCONFIGURATION: ! AlterTSConfigurationOwner(stmt->object, newowner, &cmd); break; case OBJECT_FDW: *** a/src/backend/commands/cluster.c --- b/src/backend/commands/cluster.c *************** *** 100,105 **** static void reform_and_rewrite_tuple(HeapTuple tuple, --- 100,109 ---- void cluster(ClusterStmt *stmt, bool isTopLevel) { + CommandContextData cmd; + + InitCommandContext(&cmd, (Node *)stmt, false); + if (stmt->relation != NULL) { /* This is the single-relation case. */ *************** *** 173,179 **** cluster(ClusterStmt *stmt, bool isTopLevel) heap_close(rel, NoLock); /* Do the job */ ! cluster_rel(tableOid, indexOid, false, stmt->verbose, -1, -1); } else { --- 177,183 ---- heap_close(rel, NoLock); /* Do the job */ ! cluster_rel(tableOid, indexOid, false, stmt->verbose, -1, -1, &cmd); } else { *************** *** 185,190 **** cluster(ClusterStmt *stmt, bool isTopLevel) --- 189,198 ---- List *rvs; ListCell *rv; + /* Call BEFORE CLUSTER command triggers, all slots are NULL */ + if (CommandFiresTriggers(&cmd)) + ExecBeforeCommandTriggers(&cmd); + /* * We cannot run this form of CLUSTER inside a user transaction block; * we'd be holding locks way too long. *************** *** 223,229 **** cluster(ClusterStmt *stmt, bool isTopLevel) /* functions in indexes may want a snapshot set */ PushActiveSnapshot(GetTransactionSnapshot()); cluster_rel(rvtc->tableOid, rvtc->indexOid, true, stmt->verbose, ! -1, -1); PopActiveSnapshot(); CommitTransactionCommand(); } --- 231,237 ---- /* functions in indexes may want a snapshot set */ PushActiveSnapshot(GetTransactionSnapshot()); cluster_rel(rvtc->tableOid, rvtc->indexOid, true, stmt->verbose, ! -1, -1, NULL); PopActiveSnapshot(); CommitTransactionCommand(); } *************** *** 255,261 **** cluster(ClusterStmt *stmt, bool isTopLevel) */ void cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose, ! int freeze_min_age, int freeze_table_age) { Relation OldHeap; --- 263,269 ---- */ void cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose, ! int freeze_min_age, int freeze_table_age, CommandContext cmd) { Relation OldHeap; *************** *** 376,381 **** cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose, --- 384,399 ---- if (OidIsValid(indexOid)) check_index_is_clusterable(OldHeap, indexOid, recheck, AccessExclusiveLock); + /* Call BEFORE CLUSTER command trigger */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = RelationGetRelid(OldHeap); + cmd->objectname = RelationGetRelationName(OldHeap); + cmd->schemaname = get_namespace_name(RelationGetNamespace(OldHeap)); + + ExecBeforeCommandTriggers(cmd); + } + /* * All predicate locks on the tuples or pages are about to be made * invalid, because we move tuples around. Promote them to relation *************** *** 389,394 **** cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose, --- 407,417 ---- verbose); /* NB: rebuild_relation does heap_close() on OldHeap */ + + /* + * NB: we don't run AFTER CLUSTER command triggers because of transaction + * control issues + */ } /* *************** *** 1432,1438 **** finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, reindex_flags = REINDEX_REL_SUPPRESS_INDEX_USE; if (check_constraints) reindex_flags |= REINDEX_REL_CHECK_CONSTRAINTS; ! reindex_relation(OIDOldHeap, reindex_flags); /* Destroy new heap with old filenode */ object.classId = RelationRelationId; --- 1455,1461 ---- reindex_flags = REINDEX_REL_SUPPRESS_INDEX_USE; if (check_constraints) reindex_flags |= REINDEX_REL_CHECK_CONSTRAINTS; ! reindex_relation(OIDOldHeap, reindex_flags, NULL); /* Destroy new heap with old filenode */ object.classId = RelationRelationId; *************** *** 1486,1498 **** finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u", OIDOldHeap); RenameRelationInternal(newrel->rd_rel->reltoastrelid, ! NewToastName); /* ... and its index too */ snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index", OIDOldHeap); RenameRelationInternal(toastidx, ! NewToastName); } relation_close(newrel, NoLock); } --- 1509,1521 ---- snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u", OIDOldHeap); RenameRelationInternal(newrel->rd_rel->reltoastrelid, ! NewToastName, NULL); /* ... and its index too */ snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index", OIDOldHeap); RenameRelationInternal(toastidx, ! NewToastName, NULL); } relation_close(newrel, NoLock); } *** /dev/null --- b/src/backend/commands/cmdtrigger.c *************** *** 0 **** --- 1,707 ---- + /*------------------------------------------------------------------------- + * + * cmdtrigger.c + * PostgreSQL COMMAND TRIGGER support code. + * + * Portions Copyright (c) 2011, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/commands/cmdtrigger.c + * + *------------------------------------------------------------------------- + */ + #include "postgres.h" + + #include "access/heapam.h" + #include "access/sysattr.h" + #include "catalog/catalog.h" + #include "catalog/dependency.h" + #include "catalog/indexing.h" + #include "catalog/objectaccess.h" + #include "catalog/pg_cmdtrigger.h" + #include "catalog/pg_language.h" + #include "catalog/pg_proc.h" + #include "catalog/pg_trigger.h" + #include "catalog/pg_type.h" + #include "commands/cmdtrigger.h" + #include "commands/dbcommands.h" + #include "commands/trigger.h" + #include "parser/parse_func.h" + #include "pgstat.h" + #include "miscadmin.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" + #include "utils/lsyscache.h" + #include "utils/memutils.h" + #include "utils/rel.h" + #include "utils/tqual.h" + #include "utils/syscache.h" + #include "tcop/utility.h" + + static void check_cmdtrigger_name(const char *command, const char *trigname, Relation tgrel); + + /* + * 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 + CheckCmdTriggerPrivileges() + { + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use command triggers")))); + } + + /* + * Insert Command Trigger Tuple + * + * Insert the new pg_cmdtrigger row, and return the OID assigned to the new + * row. + */ + static Oid + InsertCmdTriggerTuple(Relation tgrel, + char *command, char *trigname, Oid funcoid, char ctgtype) + { + Oid trigoid; + HeapTuple tuple; + Datum values[Natts_pg_trigger]; + bool nulls[Natts_pg_trigger]; + ObjectAddress myself, referenced; + + /* + * Build the new pg_trigger tuple. + */ + memset(nulls, false, sizeof(nulls)); + + values[Anum_pg_cmdtrigger_ctgcommand - 1] = NameGetDatum(command); + values[Anum_pg_cmdtrigger_ctgname - 1] = NameGetDatum(trigname); + values[Anum_pg_cmdtrigger_ctgfoid - 1] = ObjectIdGetDatum(funcoid); + values[Anum_pg_cmdtrigger_ctgtype - 1] = CharGetDatum(ctgtype); + values[Anum_pg_cmdtrigger_ctgenabled - 1] = CharGetDatum(TRIGGER_FIRES_ON_ORIGIN); + + tuple = heap_form_tuple(tgrel->rd_att, values, nulls); + + simple_heap_insert(tgrel, tuple); + + CatalogUpdateIndexes(tgrel, tuple); + + /* remember oid for record dependencies */ + trigoid = HeapTupleGetOid(tuple); + + heap_freetuple(tuple); + + /* + * Record dependencies for trigger. Always place a normal dependency on + * the function. + */ + myself.classId = CmdTriggerRelationId; + myself.objectId = trigoid; + myself.objectSubId = 0; + + referenced.classId = ProcedureRelationId; + referenced.objectId = funcoid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + return trigoid; + } + + /* + * Create a trigger. Returns the OID of the created trigger. + */ + void + CreateCmdTrigger(CreateCmdTrigStmt *stmt, const char *queryString) + { + Relation tgrel; + ListCell *c; + /* cmd trigger args: when, cmd_tag, objectId, schemaname, objectname [,parsetree] */ + Oid fargtypes[5] = {TEXTOID, TEXTOID, OIDOID, TEXTOID, TEXTOID}; + Oid fargtypes_c[6] = {TEXTOID, TEXTOID, OIDOID, TEXTOID, TEXTOID, INTERNALOID}; + Oid funcoid; + Oid funcrettype; + + CheckCmdTriggerPrivileges(); + + /* + * Find and validate the trigger function. When the function is coded in C + * it receives an internal argument which is the parse tree as a Node *. + * + * Only C coded functions can accept an argument of type internal, so we + * don't have to explicitely check about the prolang here. + */ + funcoid = LookupFuncName(stmt->funcname, 6, fargtypes_c, true); + if (funcoid == InvalidOid) + funcoid = LookupFuncName(stmt->funcname, 5, fargtypes, false); + + /* we need the trigger type to validate the return type */ + funcrettype = get_func_rettype(funcoid); + + /* + * Generate the trigger's OID now, so that we can use it in the name if + * needed. + */ + tgrel = heap_open(CmdTriggerRelationId, RowExclusiveLock); + + foreach(c, stmt->command) + { + Oid trigoid; + A_Const *con = (A_Const *) lfirst(c); + char *command = strVal(&con->val); + + /* + * Add some restrictions. We don't allow for AFTER command triggers on + * commands that do their own transaction management, such as VACUUM and + * CREATE INDEX CONCURRENTLY, because RAISE EXCEPTION at this point is + * meaningless, the work as already been commited. + * + * CREATE INDEX CONCURRENTLY has no specific command tag and can not be + * captured here, so we just document that not AFTER command trigger + * will get run. + */ + if (stmt->timing == CMD_TRIGGER_FIRED_AFTER + && (strcmp(command, "VACUUM") == 0)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("AFTER VACUUM command triggers are not implemented"))); + + if (stmt->timing == CMD_TRIGGER_FIRED_AFTER + && (strcmp(command, "CLUSTER") == 0)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("AFTER CLUSTER command triggers are not implemented"))); + + if (stmt->timing == CMD_TRIGGER_FIRED_AFTER + && (strcmp(command, "CREATE INDEX") == 0)) + ereport(WARNING, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("CREATE INDEX CONCURRENTLY is not supported"), + errdetail("The command trigger will not get fired."))); + + if (strcmp(command, "REINDEX") == 0) + ereport(WARNING, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("REINDEX DATABASE is not supported"), + errdetail("The command trigger will not get fired."))); + + /* + * Scan pg_cmdtrigger for existing triggers on command. We do this only + * to give a nice error message if there's already a trigger of the + * same name. (The unique index on ctgcommand/ctgname would complain + * anyway.) + * + * NOTE that this is cool only because we have AccessExclusiveLock on + * the relation, so the trigger set won't be changing underneath us. + */ + check_cmdtrigger_name(command, stmt->trigname, tgrel); + + if (funcrettype != VOIDOID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("function \"%s\" must return type \"void\"", + NameListToString(stmt->funcname)))); + + trigoid = InsertCmdTriggerTuple(tgrel, command, stmt->trigname, funcoid, stmt->timing); + } + heap_close(tgrel, RowExclusiveLock); + } + + /* + * Guts of command trigger deletion. + */ + void + RemoveCmdTriggerById(Oid trigOid) + { + Relation tgrel; + SysScanDesc tgscan; + ScanKeyData skey[1]; + HeapTuple tup; + + tgrel = heap_open(CmdTriggerRelationId, RowExclusiveLock); + + /* + * Find the trigger to delete. + */ + ScanKeyInit(&skey[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(trigOid)); + + tgscan = systable_beginscan(tgrel, CmdTriggerOidIndexId, true, + SnapshotNow, 1, skey); + + tup = systable_getnext(tgscan); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "could not find tuple for command trigger %u", trigOid); + + /* + * Delete the pg_cmdtrigger tuple. + */ + simple_heap_delete(tgrel, &tup->t_self); + + systable_endscan(tgscan); + heap_close(tgrel, RowExclusiveLock); + } + + /* + * ALTER TRIGGER foo ON COMMAND ... ENABLE|DISABLE|ENABLE ALWAYS|REPLICA + */ + void + AlterCmdTrigger(AlterCmdTrigStmt *stmt) + { + Relation tgrel; + SysScanDesc tgscan; + ScanKeyData skey[2]; + HeapTuple tup; + Form_pg_cmdtrigger cmdForm; + char tgenabled = pstrdup(stmt->tgenabled)[0]; /* works with gram.y */ + + CheckCmdTriggerPrivileges(); + + tgrel = heap_open(CmdTriggerRelationId, RowExclusiveLock); + ScanKeyInit(&skey[0], + Anum_pg_cmdtrigger_ctgcommand, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(stmt->command)); + ScanKeyInit(&skey[1], + Anum_pg_cmdtrigger_ctgname, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(stmt->trigname)); + + tgscan = systable_beginscan(tgrel, CmdTriggerCommandNameIndexId, true, + SnapshotNow, 2, skey); + + tup = systable_getnext(tgscan); + + if (!HeapTupleIsValid(tup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("trigger \"%s\" for command \"%s\" does not exist, skipping", + stmt->trigname, stmt->command))); + + /* Copy tuple so we can modify it below */ + tup = heap_copytuple(tup); + cmdForm = (Form_pg_cmdtrigger) GETSTRUCT(tup); + + systable_endscan(tgscan); + + cmdForm->ctgenabled = tgenabled; + + simple_heap_update(tgrel, &tup->t_self, tup); + CatalogUpdateIndexes(tgrel, tup); + + heap_close(tgrel, RowExclusiveLock); + heap_freetuple(tup); + } + + + /* + * Rename command trigger + */ + void + RenameCmdTrigger(List *name, const char *trigname, const char *newname) + { + SysScanDesc tgscan; + ScanKeyData skey[2]; + HeapTuple tup; + Relation rel; + Form_pg_cmdtrigger cmdForm; + char *command; + + CheckCmdTriggerPrivileges(); + + Assert(list_length(name) == 1); + command = strVal((Value *)linitial(name)); + + rel = heap_open(CmdTriggerRelationId, RowExclusiveLock); + + //FIXME: need a row level lock here + /* newname must be available */ + check_cmdtrigger_name(command, newname, rel); + + /* get existing tuple */ + ScanKeyInit(&skey[0], + Anum_pg_cmdtrigger_ctgcommand, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(command)); + ScanKeyInit(&skey[1], + Anum_pg_cmdtrigger_ctgname, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(trigname)); + + tgscan = systable_beginscan(rel, CmdTriggerCommandNameIndexId, true, + SnapshotNow, 2, skey); + + tup = systable_getnext(tgscan); + + if (!HeapTupleIsValid(tup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("trigger \"%s\" for command \"%s\" does not exist, skipping", + trigname, command))); + + /* Copy tuple so we can modify it below */ + tup = heap_copytuple(tup); + cmdForm = (Form_pg_cmdtrigger) GETSTRUCT(tup); + + systable_endscan(tgscan); + + /* rename */ + namestrcpy(&(cmdForm->ctgname), newname); + simple_heap_update(rel, &tup->t_self, tup); + CatalogUpdateIndexes(rel, tup); + + heap_freetuple(tup); + heap_close(rel, NoLock); + } + + /* + * get_cmdtrigger_oid - Look up a trigger by name to find its OID. + * + * If missing_ok is false, throw an error if trigger not found. If + * true, just return InvalidOid. + */ + Oid + get_cmdtrigger_oid(const char *trigname, const char *command, bool missing_ok) + { + Relation tgrel; + ScanKeyData skey[2]; + SysScanDesc tgscan; + HeapTuple tup; + Oid oid; + + /* + * Find the trigger, verify permissions, set up object address + */ + tgrel = heap_open(CmdTriggerRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], + Anum_pg_cmdtrigger_ctgcommand, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(command)); + ScanKeyInit(&skey[1], + Anum_pg_cmdtrigger_ctgname, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(trigname)); + + tgscan = systable_beginscan(tgrel, CmdTriggerCommandNameIndexId, true, + SnapshotNow, 1, skey); + + tup = systable_getnext(tgscan); + + if (!HeapTupleIsValid(tup)) + { + if (!missing_ok) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("trigger \"%s\" for command \"%s\" does not exist, skipping", + trigname, command))); + oid = InvalidOid; + } + else + { + oid = HeapTupleGetOid(tup); + } + + systable_endscan(tgscan); + heap_close(tgrel, AccessShareLock); + return oid; + } + + /* + * Scan pg_cmdtrigger for existing triggers on command. We do this only to + * give a nice error message if there's already a trigger of the same name. + */ + void + check_cmdtrigger_name(const char *command, const char *trigname, Relation tgrel) + { + SysScanDesc tgscan; + ScanKeyData skey[2]; + HeapTuple tuple; + + ScanKeyInit(&skey[0], + Anum_pg_cmdtrigger_ctgcommand, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(command)); + ScanKeyInit(&skey[1], + Anum_pg_cmdtrigger_ctgname, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(trigname)); + + tgscan = systable_beginscan(tgrel, CmdTriggerCommandNameIndexId, true, + SnapshotNow, 2, skey); + + tuple = systable_getnext(tgscan); + + elog(DEBUG1, "check_cmdtrigger_name(%s, %s)", command, trigname); + + if (HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("trigger \"%s\" for command \"%s\" already exists", + trigname, command))); + systable_endscan(tgscan); + } + + /* + * Functions to execute the command triggers. + * + * We call the functions that matches the command triggers definitions in + * alphabetical order, and give them those arguments: + * + * command tag, text + * objectId, oid + * schemaname, text + * objectname, text + * + */ + + /* + * Scan the catalogs and fill in the CommandContext procedures that we will + * have to call before and after the command. + */ + bool + ListCommandTriggers(CommandContext cmd) + { + int count = 0; + Relation rel, irel; + SysScanDesc scandesc; + HeapTuple tuple; + ScanKeyData entry[1]; + + cmd->before = cmd->after = NIL; + + rel = heap_open(CmdTriggerRelationId, AccessShareLock); + irel = index_open(CmdTriggerCommandNameIndexId, AccessShareLock); + + ScanKeyInit(&entry[0], + Anum_pg_cmdtrigger_ctgcommand, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(cmd->tag)); + + scandesc = systable_beginscan_ordered(rel, irel, SnapshotNow, 1, entry); + + while (HeapTupleIsValid(tuple = systable_getnext_ordered(scandesc, ForwardScanDirection))) + { + Form_pg_cmdtrigger form = (Form_pg_cmdtrigger) GETSTRUCT(tuple); + + if (form->ctgenabled == TRIGGER_DISABLED) + { + continue; + } + else if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA) + { + if (form->ctgenabled == TRIGGER_FIRES_ON_ORIGIN) + continue; + } + else /* ORIGIN or LOCAL role */ + { + if (form->ctgenabled == TRIGGER_FIRES_ON_REPLICA) + continue; + } + + switch (form->ctgtype) + { + case CMD_TRIGGER_FIRED_BEFORE: + cmd->before = lappend_oid(cmd->before, form->ctgfoid); + break; + + case CMD_TRIGGER_FIRED_AFTER: + cmd->after = lappend_oid(cmd->after, form->ctgfoid); + break; + } + count++; + } + systable_endscan_ordered(scandesc); + + index_close(irel, AccessShareLock); + heap_close(rel, AccessShareLock); + + return count > 0; + } + + static bool + call_cmdtrigger_procedure(CommandContext cmd, + RegProcedure proc, + const char *when, + MemoryContext per_command_context) + { + FmgrInfo flinfo; + FunctionCallInfoData fcinfo; + PgStat_FunctionCallUsage fcusage; + Datum result; + HeapTuple procedureTuple; + Form_pg_proc procedureStruct; + int nargs = 5; + + fmgr_info_cxt(proc, &flinfo, per_command_context); + + /* we need the procedure's language here to know how many args to call it + * with + */ + procedureTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(proc)); + if (!HeapTupleIsValid(procedureTuple)) + elog(ERROR, "cache lookup failed for function %u", proc); + procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple); + + if (procedureStruct->prolang == ClanguageId) + nargs = 6; + + ReleaseSysCache(procedureTuple); + + /* Can't use OidFunctionCallN because we might get a NULL result */ + InitFunctionCallInfoData(fcinfo, &flinfo, nargs, InvalidOid, NULL, NULL); + + fcinfo.arg[0] = PointerGetDatum(cstring_to_text(pstrdup(when))); + + /* We support triggers ON ANY COMMAND so all fields here are nullable. */ + if (cmd->tag != NULL) + fcinfo.arg[1] = PointerGetDatum(cstring_to_text(pstrdup(cmd->tag))); + + fcinfo.arg[2] = ObjectIdGetDatum(cmd->objectId); + + if (cmd->schemaname != NULL) + fcinfo.arg[3] = PointerGetDatum(cstring_to_text(pstrdup(cmd->schemaname))); + + if (cmd->objectname != NULL) + fcinfo.arg[4] = PointerGetDatum(cstring_to_text(pstrdup(cmd->objectname))); + + fcinfo.argnull[0] = false; + fcinfo.argnull[1] = cmd->tag == NULL; + fcinfo.argnull[2] = cmd->objectId == InvalidOid; + fcinfo.argnull[3] = cmd->schemaname == NULL; + fcinfo.argnull[4] = cmd->objectname == NULL; + + if (nargs == 6) + { + fcinfo.arg[5] = PointerGetDatum(cmd->parsetree); + fcinfo.argnull[5] = false; + } + + pgstat_init_function_usage(&fcinfo, &fcusage); + + result = FunctionCallInvoke(&fcinfo); + + pgstat_end_function_usage(&fcusage, true); + + + if (!fcinfo.isnull && DatumGetBool(result) == false) + return false; + return true; + } + + /* + * A BEFORE command trigger can choose to "abort" the command by returning + * false. This function is called by ExecBeforeOrInsteadOfCommandTriggers() so + * is not exposed to other modules. + */ + static void + exec_command_triggers_internal(CommandContext cmd, List *procs, const char *when) + { + MemoryContext per_command_context, oldContext; + ListCell *cell; + + /* + * Do the functions evaluation in a per-command memory context, so that + * leaked memory will be reclaimed once per command. + */ + per_command_context = + AllocSetContextCreate(CurrentMemoryContext, + "CommandTriggerContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + + oldContext = MemoryContextSwitchTo(per_command_context); + MemoryContextReset(per_command_context); + + foreach(cell, procs) + { + Oid proc = lfirst_oid(cell); + call_cmdtrigger_procedure(cmd, (RegProcedure)proc, when, per_command_context); + } + MemoryContextSwitchTo(oldContext); + } + + /* + * Routine to call to setup a CommandContextData structure. + * + * This ensures that cmd->before and cmd->after are set to meaningful values, + * always NIL when list_triggers is false. + * + * In case of ANY trigger init we don't want to list triggers associated with + * the real command tag, we have another API to do that, see + * ExecBeforeAnyCommandTriggers() and ExecAfterAnyCommandTriggers(). + */ + void + InitCommandContext(CommandContext cmd, const Node *stmt, bool list_any_triggers) + { + cmd->tag = (char *) CreateCommandTag((Node *)stmt); + cmd->parsetree = (Node *)stmt; + cmd->objectId = InvalidOid; + cmd->objectname = NULL; + cmd->schemaname = NULL; + cmd->before = NIL; + cmd->after = NIL; + + if (list_any_triggers) + { + /* list procedures for "ANY" command */ + char *tag = cmd->tag; + + cmd->tag = "ANY"; + ListCommandTriggers(cmd); + cmd->tag = tag; + } + else + ListCommandTriggers(cmd); + } + + /* + * InitCommandContext() must have been called when CommandFiresTriggers() is + * called. When CommandFiresTriggers() returns false, cmd structure needs not + * be initialized further. + * + * There's no place where we can skip BEFORE command trigger initialization + * when we have an AFTER command triggers to run, because objectname and + * schemaname are needed in both places, so we check both here. + */ + bool + CommandFiresTriggers(CommandContext cmd) + { + return cmd != NULL && (cmd->before != NIL || cmd->after != NIL); + } + + /* + * It's still interresting to avoid preparing the Command Context for AFTER + * command triggers when we have none to Execute, so we provide this API too. + */ + bool + CommandFiresAfterTriggers(CommandContext cmd) + { + return cmd != NULL && cmd->after != NIL; + } + + /* + * In the various Exec...CommandTriggers functions, we still protect against + * and empty procedure list so as not to create a MemoryContext then switch to + * it unnecessarily. + */ + void + ExecBeforeCommandTriggers(CommandContext cmd) + { + if (cmd != NULL && cmd->before != NIL) + exec_command_triggers_internal(cmd, cmd->before, "BEFORE"); + } + + void + ExecAfterCommandTriggers(CommandContext cmd) + { + if (cmd != NULL && cmd->after != NIL) + exec_command_triggers_internal(cmd, cmd->after, "AFTER"); + } *** a/src/backend/commands/collationcmds.c --- b/src/backend/commands/collationcmds.c *************** *** 22,27 **** --- 22,28 ---- #include "catalog/pg_collation.h" #include "catalog/pg_collation_fn.h" #include "commands/alter.h" + #include "commands/cmdtrigger.h" #include "commands/collationcmds.h" #include "commands/dbcommands.h" #include "commands/defrem.h" *************** *** 34,46 **** #include "utils/syscache.h" static void AlterCollationOwner_internal(Relation rel, Oid collationOid, ! Oid newOwnerId); /* * CREATE COLLATION */ void ! DefineCollation(List *names, List *parameters) { char *collName; Oid collNamespace; --- 35,47 ---- #include "utils/syscache.h" static void AlterCollationOwner_internal(Relation rel, Oid collationOid, ! Oid newOwnerId, CommandContext cmd); /* * CREATE COLLATION */ void ! DefineCollation(List *names, List *parameters, CommandContext cmd) { char *collName; Oid collNamespace; *************** *** 137,154 **** DefineCollation(List *names, List *parameters) GetUserId(), GetDatabaseEncoding(), collcollate, ! collctype); ! /* check that the locales can be loaded */ ! CommandCounterIncrement(); ! (void) pg_newlocale_from_collation(newoid); } /* * Rename collation */ void ! RenameCollation(List *name, const char *newname) { Oid collationOid; Oid namespaceOid; --- 138,160 ---- GetUserId(), GetDatabaseEncoding(), collcollate, ! collctype, ! cmd); ! /* before or instead of command trigger might have cancelled the command */ ! if (OidIsValid(newoid)) ! { ! /* check that the locales can be loaded */ ! CommandCounterIncrement(); ! (void) pg_newlocale_from_collation(newoid); ! } } /* * Rename collation */ void ! RenameCollation(List *name, const char *newname, CommandContext cmd) { Oid collationOid; Oid namespaceOid; *************** *** 200,220 **** RenameCollation(List *name, const char *newname) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); /* rename */ namestrcpy(&(((Form_pg_collation) GETSTRUCT(tup))->collname), newname); simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); heap_freetuple(tup); - heap_close(rel, RowExclusiveLock); } /* * Change collation owner, by name */ void ! AlterCollationOwner(List *name, Oid newOwnerId) { Oid collationOid; Relation rel; --- 206,242 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* Call BEFORE ALTER COLLATION triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = NameStr((((Form_pg_collation) GETSTRUCT(tup))->collname)); + cmd->schemaname = get_namespace_name(namespaceOid); + + ExecBeforeCommandTriggers(cmd); + } + /* rename */ namestrcpy(&(((Form_pg_collation) GETSTRUCT(tup))->collname), newname); simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); heap_freetuple(tup); heap_close(rel, RowExclusiveLock); + + /* Call AFTER ALTER COLLATION triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectname = (char *)newname; + ExecAfterCommandTriggers(cmd); + } } /* * Change collation owner, by name */ void ! AlterCollationOwner(List *name, Oid newOwnerId, CommandContext cmd) { Oid collationOid; Relation rel; *************** *** 223,229 **** AlterCollationOwner(List *name, Oid newOwnerId) collationOid = get_collation_oid(name, false); ! AlterCollationOwner_internal(rel, collationOid, newOwnerId); heap_close(rel, RowExclusiveLock); } --- 245,251 ---- collationOid = get_collation_oid(name, false); ! AlterCollationOwner_internal(rel, collationOid, newOwnerId, cmd); heap_close(rel, RowExclusiveLock); } *************** *** 232,244 **** AlterCollationOwner(List *name, Oid newOwnerId) * Change collation owner, by oid */ void ! AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId) { Relation rel; rel = heap_open(CollationRelationId, RowExclusiveLock); ! AlterCollationOwner_internal(rel, collationOid, newOwnerId); heap_close(rel, RowExclusiveLock); } --- 254,266 ---- * Change collation owner, by oid */ void ! AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId, CommandContext cmd) { Relation rel; rel = heap_open(CollationRelationId, RowExclusiveLock); ! AlterCollationOwner_internal(rel, collationOid, newOwnerId, cmd); heap_close(rel, RowExclusiveLock); } *************** *** 250,256 **** AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId) * open and suitably locked; it will not be closed. */ static void ! AlterCollationOwner_internal(Relation rel, Oid collationOid, Oid newOwnerId) { Form_pg_collation collForm; HeapTuple tup; --- 272,279 ---- * open and suitably locked; it will not be closed. */ static void ! AlterCollationOwner_internal(Relation rel, Oid collationOid, Oid newOwnerId, ! CommandContext cmd) { Form_pg_collation collForm; HeapTuple tup; *************** *** 291,296 **** AlterCollationOwner_internal(Relation rel, Oid collationOid, Oid newOwnerId) --- 314,329 ---- get_namespace_name(collForm->collnamespace)); } + /* Call BEFORE ALTER COLLATION triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = NameStr(collForm->collname); + cmd->schemaname = get_namespace_name(collForm->collnamespace); + + ExecBeforeCommandTriggers(cmd); + } + /* * Modify the owner --- okay to scribble on tup because it's a copy */ *************** *** 303,310 **** AlterCollationOwner_internal(Relation rel, Oid collationOid, Oid newOwnerId) /* Update owner dependency reference */ changeDependencyOnOwner(CollationRelationId, collationOid, newOwnerId); - } heap_freetuple(tup); } --- 336,346 ---- /* Update owner dependency reference */ changeDependencyOnOwner(CollationRelationId, collationOid, newOwnerId); + /* Call AFTER ALTER COLLATION triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); + } heap_freetuple(tup); } *************** *** 312,318 **** AlterCollationOwner_internal(Relation rel, Oid collationOid, Oid newOwnerId) * Execute ALTER COLLATION SET SCHEMA */ void ! AlterCollationNamespace(List *name, const char *newschema) { Oid collOid, nspOid; --- 348,354 ---- * Execute ALTER COLLATION SET SCHEMA */ void ! AlterCollationNamespace(List *name, const char *newschema, CommandContext cmd) { Oid collOid, nspOid; *************** *** 321,334 **** AlterCollationNamespace(List *name, const char *newschema) nspOid = LookupCreationNamespace(newschema); ! AlterCollationNamespace_oid(collOid, nspOid); } /* * Change collation schema, by oid */ Oid ! AlterCollationNamespace_oid(Oid collOid, Oid newNspOid) { Oid oldNspOid; Relation rel; --- 357,370 ---- nspOid = LookupCreationNamespace(newschema); ! AlterCollationNamespace_oid(collOid, nspOid, cmd); } /* * Change collation schema, by oid */ Oid ! AlterCollationNamespace_oid(Oid collOid, Oid newNspOid, CommandContext cmd) { Oid oldNspOid; Relation rel; *************** *** 374,380 **** AlterCollationNamespace_oid(Oid collOid, Oid newNspOid) Anum_pg_collation_collname, Anum_pg_collation_collnamespace, Anum_pg_collation_collowner, ! ACL_KIND_COLLATION); heap_close(rel, RowExclusiveLock); --- 410,416 ---- Anum_pg_collation_collname, Anum_pg_collation_collnamespace, Anum_pg_collation_collowner, ! ACL_KIND_COLLATION, cmd); heap_close(rel, RowExclusiveLock); *** a/src/backend/commands/conversioncmds.c --- b/src/backend/commands/conversioncmds.c *************** *** 31,37 **** #include "utils/syscache.h" static void AlterConversionOwner_internal(Relation rel, Oid conversionOid, ! Oid newOwnerId); /* * CREATE CONVERSION --- 31,37 ---- #include "utils/syscache.h" static void AlterConversionOwner_internal(Relation rel, Oid conversionOid, ! Oid newOwnerId, CommandContext cmd); /* * CREATE CONVERSION *************** *** 39,45 **** static void AlterConversionOwner_internal(Relation rel, Oid conversionOid, void CreateConversionCommand(CreateConversionStmt *stmt) { ! Oid namespaceId; char *conversion_name; AclResult aclresult; int from_encoding; --- 39,45 ---- void CreateConversionCommand(CreateConversionStmt *stmt) { ! Oid namespaceId, convOid; char *conversion_name; AclResult aclresult; int from_encoding; *************** *** 50,55 **** CreateConversionCommand(CreateConversionStmt *stmt) --- 50,56 ---- List *func_name = stmt->func_name; static Oid funcargs[] = {INT4OID, INT4OID, CSTRINGOID, INTERNALOID, INT4OID}; char result[1]; + CommandContextData cmd; /* Convert list of names to a name and namespace */ namespaceId = QualifiedNameGetCreationNamespace(stmt->conversion_name, *************** *** 109,127 **** CreateConversionCommand(CreateConversionStmt *stmt) CStringGetDatum(result), Int32GetDatum(0)); /* * All seem ok, go ahead (possible failure would be a duplicate conversion * name) */ ! ConversionCreate(conversion_name, namespaceId, GetUserId(), ! from_encoding, to_encoding, funcoid, stmt->def); } /* * Rename conversion */ void ! RenameConversion(List *name, const char *newname) { Oid conversionOid; Oid namespaceOid; --- 110,147 ---- CStringGetDatum(result), Int32GetDatum(0)); + /* Call BEFORE CREATE CONVERSION command triggers */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = InvalidOid; + cmd.objectname = conversion_name; + cmd.schemaname = get_namespace_name(namespaceId); + + ExecBeforeCommandTriggers(&cmd); + } + /* * All seem ok, go ahead (possible failure would be a duplicate conversion * name) */ ! convOid = ConversionCreate(conversion_name, namespaceId, GetUserId(), ! from_encoding, to_encoding, funcoid, stmt->def); ! ! /* Call AFTER CREATE CONVERSION command triggers */ ! if (CommandFiresAfterTriggers(&cmd)) ! { ! cmd.objectId = convOid; ! ExecAfterCommandTriggers(&cmd); ! } } /* * Rename conversion */ void ! RenameConversion(List *name, const char *newname, CommandContext cmd) { Oid conversionOid; Oid namespaceOid; *************** *** 159,164 **** RenameConversion(List *name, const char *newname) --- 179,194 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* Call BEFORE ALTER CONVERSION triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = NameStr((((Form_pg_conversion) GETSTRUCT(tup))->conname)); + cmd->schemaname = get_namespace_name(namespaceOid); + + ExecBeforeCommandTriggers(cmd); + } + /* rename */ namestrcpy(&(((Form_pg_conversion) GETSTRUCT(tup))->conname), newname); simple_heap_update(rel, &tup->t_self, tup); *************** *** 166,178 **** RenameConversion(List *name, const char *newname) heap_close(rel, NoLock); heap_freetuple(tup); } /* * Change conversion owner, by name */ void ! AlterConversionOwner(List *name, Oid newOwnerId) { Oid conversionOid; Relation rel; --- 196,215 ---- heap_close(rel, NoLock); heap_freetuple(tup); + + /* Call AFTER ALTER CONVERSION triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectname = (char *)newname; + ExecAfterCommandTriggers(cmd); + } } /* * Change conversion owner, by name */ void ! AlterConversionOwner(List *name, Oid newOwnerId, CommandContext cmd) { Oid conversionOid; Relation rel; *************** *** 181,187 **** AlterConversionOwner(List *name, Oid newOwnerId) conversionOid = get_conversion_oid(name, false); ! AlterConversionOwner_internal(rel, conversionOid, newOwnerId); heap_close(rel, NoLock); } --- 218,224 ---- conversionOid = get_conversion_oid(name, false); ! AlterConversionOwner_internal(rel, conversionOid, newOwnerId, cmd); heap_close(rel, NoLock); } *************** *** 190,202 **** AlterConversionOwner(List *name, Oid newOwnerId) * Change conversion owner, by oid */ void ! AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId) { Relation rel; rel = heap_open(ConversionRelationId, RowExclusiveLock); ! AlterConversionOwner_internal(rel, conversionOid, newOwnerId); heap_close(rel, NoLock); } --- 227,239 ---- * Change conversion owner, by oid */ void ! AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId, CommandContext cmd) { Relation rel; rel = heap_open(ConversionRelationId, RowExclusiveLock); ! AlterConversionOwner_internal(rel, conversionOid, newOwnerId, cmd); heap_close(rel, NoLock); } *************** *** 208,214 **** AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId) * open and suitably locked; it will not be closed. */ static void ! AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId) { Form_pg_conversion convForm; HeapTuple tup; --- 245,252 ---- * open and suitably locked; it will not be closed. */ static void ! AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId, ! CommandContext cmd) { Form_pg_conversion convForm; HeapTuple tup; *************** *** 249,254 **** AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId) --- 287,302 ---- get_namespace_name(convForm->connamespace)); } + /* Call BEFORE ALTER CONVERSION triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = NameStr(convForm->conname); + cmd->schemaname = get_namespace_name(convForm->connamespace); + + ExecBeforeCommandTriggers(cmd); + } + /* * Modify the owner --- okay to scribble on tup because it's a copy */ *************** *** 261,266 **** AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId) --- 309,318 ---- /* Update owner dependency reference */ changeDependencyOnOwner(ConversionRelationId, conversionOid, newOwnerId); + + /* Call AFTER ALTER CONVERSION triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); } heap_freetuple(tup); *************** *** 270,276 **** AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId) * Execute ALTER CONVERSION SET SCHEMA */ void ! AlterConversionNamespace(List *name, const char *newschema) { Oid convOid, nspOid; --- 322,328 ---- * Execute ALTER CONVERSION SET SCHEMA */ void ! AlterConversionNamespace(List *name, const char *newschema, CommandContext cmd) { Oid convOid, nspOid; *************** *** 288,294 **** AlterConversionNamespace(List *name, const char *newschema) Anum_pg_conversion_conname, Anum_pg_conversion_connamespace, Anum_pg_conversion_conowner, ! ACL_KIND_CONVERSION); heap_close(rel, RowExclusiveLock); } --- 340,346 ---- Anum_pg_conversion_conname, Anum_pg_conversion_connamespace, Anum_pg_conversion_conowner, ! ACL_KIND_CONVERSION, cmd); heap_close(rel, RowExclusiveLock); } *************** *** 309,315 **** AlterConversionNamespace_oid(Oid convOid, Oid newNspOid) Anum_pg_conversion_conname, Anum_pg_conversion_connamespace, Anum_pg_conversion_conowner, ! ACL_KIND_CONVERSION); heap_close(rel, RowExclusiveLock); --- 361,367 ---- Anum_pg_conversion_conname, Anum_pg_conversion_connamespace, Anum_pg_conversion_conowner, ! ACL_KIND_CONVERSION, NULL); heap_close(rel, RowExclusiveLock); *** a/src/backend/commands/dbcommands.c --- b/src/backend/commands/dbcommands.c *************** *** 49,54 **** --- 49,55 ---- #include "storage/ipc.h" #include "storage/procarray.h" #include "storage/smgr.h" + #include "tcop/utility.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" *************** *** 732,739 **** createdb_failure_callback(int code, Datum arg) * DROP DATABASE */ void ! dropdb(const char *dbname, bool missing_ok) { Oid db_id; bool db_istemplate; Relation pgdbrel; --- 733,742 ---- * DROP DATABASE */ void ! dropdb(const DropdbStmt *stmt) { + const char *dbname = stmt->dbname; + bool missing_ok = stmt->missing_ok; Oid db_id; bool db_istemplate; Relation pgdbrel; *** a/src/backend/commands/dropcmds.c --- b/src/backend/commands/dropcmds.c *************** *** 21,32 **** --- 21,35 ---- #include "catalog/objectaddress.h" #include "catalog/pg_class.h" #include "catalog/pg_proc.h" + #include "commands/cmdtrigger.h" #include "commands/defrem.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "parser/parse_type.h" + #include "tcop/utility.h" #include "utils/acl.h" #include "utils/builtins.h" + #include "utils/lsyscache.h" #include "utils/syscache.h" static void does_not_exist_skipping(ObjectType objtype, *************** *** 49,54 **** RemoveObjects(DropStmt *stmt) --- 52,59 ---- ObjectAddresses *objects; ListCell *cell1; ListCell *cell2 = NULL; + int i = 0, n = list_length(stmt->objects); + CommandContext *cmds = (CommandContext *) palloc(n * sizeof(CommandContext)); objects = new_object_addresses(); *************** *** 59,64 **** RemoveObjects(DropStmt *stmt) --- 64,70 ---- List *objargs = NIL; Relation relation = NULL; Oid namespaceId; + CommandContextData cmd; if (stmt->arguments) { *************** *** 77,82 **** RemoveObjects(DropStmt *stmt) --- 83,89 ---- if (!OidIsValid(address.objectId)) { does_not_exist_skipping(stmt->removeType, objname, objargs); + cmds[i++] = NULL; continue; } *************** *** 115,126 **** RemoveObjects(DropStmt *stmt) --- 122,157 ---- if (relation) heap_close(relation, NoLock); + /* + * Call BEFORE DROP command triggers + */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = address.objectId; + cmd.objectname = strVal(list_nth(objname, list_length(objname)-1)); + cmd.schemaname = get_namespace_name(namespaceId); + + ExecBeforeCommandTriggers(&cmd); + } + cmds[i++] = &cmd; + add_exact_object_address(&address, objects); } /* Here we really delete them. */ performMultipleDeletions(objects, stmt->behavior, 0); + /* Call AFTER DROP command triggers */ + for(i = 0; iobjectId = InvalidOid; + ExecAfterCommandTriggers(cmds[i]); + } + } free_object_addresses(objects); } *************** *** 206,211 **** does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs) --- 237,247 ---- args = NameListToString(list_truncate(objname, list_length(objname) - 1)); break; + case OBJECT_CMDTRIGGER: + msg = gettext_noop("trigger \"%s\" for command \"%s\" does not exist, skipping"); + name = NameListToString(objname); + args = strVal(linitial(objargs)); + break; case OBJECT_RULE: msg = gettext_noop("rule \"%s\" for relation \"%s\" does not exist, skipping"); name = strVal(llast(objname)); *** a/src/backend/commands/extension.c --- b/src/backend/commands/extension.c *************** *** 39,44 **** --- 39,45 ---- #include "catalog/pg_namespace.h" #include "catalog/pg_type.h" #include "commands/alter.h" + #include "commands/cmdtrigger.h" #include "commands/comment.h" #include "commands/extension.h" #include "commands/schemacmds.h" *************** *** 1190,1200 **** CreateExtension(CreateExtensionStmt *stmt) --- 1191,1215 ---- List *requiredSchemas; Oid extensionOid; ListCell *lc; + CommandContextData cmd; /* Check extension name validity before any filesystem access */ check_valid_extension_name(stmt->extname); /* + * Call BEFORE CREATE EXTENSION triggers + */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = InvalidOid; + cmd.objectname = stmt->extname; + cmd.schemaname = NULL; + + ExecBeforeCommandTriggers(&cmd); + } + /* * Check for duplicate extension name. The unique index on * pg_extension.extname would catch this anyway, and serves as a backstop * in case of race conditions; but this is a friendlier error message, and *************** *** 1467,1472 **** CreateExtension(CreateExtensionStmt *stmt) --- 1482,1494 ---- */ ApplyExtensionUpdates(extensionOid, pcontrol, versionName, updateVersions); + + /* Call AFTER CREATE EXTENSION triggers */ + if (CommandFiresAfterTriggers(&cmd)) + { + cmd.objectId = extensionOid; + ExecAfterCommandTriggers(&cmd); + } } /* *************** *** 2186,2192 **** pg_extension_config_dump(PG_FUNCTION_ARGS) * Execute ALTER EXTENSION SET SCHEMA */ void ! AlterExtensionNamespace(List *names, const char *newschema) { char *extensionName; Oid extensionOid; --- 2208,2214 ---- * Execute ALTER EXTENSION SET SCHEMA */ void ! AlterExtensionNamespace(List *names, const char *newschema, CommandContext cmd) { char *extensionName; Oid extensionOid; *************** *** 2247,2252 **** AlterExtensionNamespace(List *names, const char *newschema) --- 2269,2284 ---- systable_endscan(extScan); + /* Call BEFORE ALTER EXTENSION triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = extensionOid; + cmd->objectname = extensionName; + cmd->schemaname = NULL; + + ExecBeforeCommandTriggers(cmd); + } + /* * If the extension is already in the target schema, just silently do * nothing. *************** *** 2342,2347 **** AlterExtensionNamespace(List *names, const char *newschema) --- 2374,2383 ---- /* update dependencies to point to the new schema */ changeDependencyFor(ExtensionRelationId, extensionOid, NamespaceRelationId, oldNspOid, nspOid); + + /* Call AFTER ALTER EXTENSION triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); } /* *************** *** 2363,2368 **** ExecAlterExtensionStmt(AlterExtensionStmt *stmt) --- 2399,2405 ---- Datum datum; bool isnull; ListCell *lc; + CommandContextData cmd; /* * We use global variables to track the extension being created, so we can *************** *** 2415,2420 **** ExecAlterExtensionStmt(AlterExtensionStmt *stmt) --- 2452,2471 ---- stmt->extname); /* + * Call BEFORE ALTER EXTENSION triggers + */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = extensionOid; + cmd.objectname = stmt->extname; + cmd.schemaname = NULL; + + ExecBeforeCommandTriggers(&cmd); + } + + /* * Read the primary control file. Note we assume that it does not contain * any non-ASCII data, so there is no need to worry about encoding at this * point. *************** *** 2480,2485 **** ExecAlterExtensionStmt(AlterExtensionStmt *stmt) --- 2531,2540 ---- */ ApplyExtensionUpdates(extensionOid, control, oldVersionName, updateVersions); + + /* Call AFTER ALTER EXTENSION triggers */ + if (CommandFiresAfterTriggers(&cmd)) + ExecAfterCommandTriggers(&cmd); } /* *************** *** 2649,2654 **** ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt) --- 2704,2710 ---- ObjectAddress object; Relation relation; Oid oldExtension; + CommandContextData cmd; extension.classId = ExtensionRelationId; extension.objectId = get_extension_oid(stmt->extname, false); *************** *** 2677,2682 **** ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt) --- 2733,2745 ---- */ oldExtension = getExtensionOfObject(object.classId, object.objectId); + InitCommandContext(&cmd, (Node *)stmt, false); + + /* Init the command context no matter what, that's cheap here */ + cmd.objectId = extension.objectId; + cmd.objectname = stmt->extname; + cmd.schemaname = NULL; + if (stmt->action > 0) { /* *************** *** 2689,2694 **** ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt) --- 2752,2761 ---- getObjectDescription(&object), get_extension_name(oldExtension)))); + /* Call BEFORE ALTER EXTENSION command triggers */ + if (CommandFiresTriggers(&cmd)) + ExecBeforeCommandTriggers(&cmd); + /* * OK, add the dependency. */ *************** *** 2706,2711 **** ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt) --- 2773,2782 ---- getObjectDescription(&object), stmt->extname))); + /* Call BEFORE ALTER EXTENSION command triggers */ + if (CommandFiresTriggers(&cmd)) + ExecBeforeCommandTriggers(&cmd); + /* * OK, drop the dependency. */ *************** *** 2723,2726 **** ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt) --- 2794,2801 ---- */ if (relation != NULL) relation_close(relation, NoLock); + + /* Call AFTER ALTER EXTENSION command triggers */ + if (CommandFiresAfterTriggers(&cmd)) + ExecAfterCommandTriggers(&cmd); } *** a/src/backend/commands/foreigncmds.c --- b/src/backend/commands/foreigncmds.c *************** *** 578,583 **** CreateForeignDataWrapper(CreateFdwStmt *stmt) --- 578,584 ---- Oid ownerId; ObjectAddress myself; ObjectAddress referenced; + CommandContextData cmd; rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock); *************** *** 601,606 **** CreateForeignDataWrapper(CreateFdwStmt *stmt) --- 602,619 ---- errmsg("foreign-data wrapper \"%s\" already exists", stmt->fdwname))); + /* Call BEFORE CREATE FOREIGN DATA WRAPPER triggers */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = InvalidOid; + cmd.objectname = stmt->fdwname; + cmd.schemaname = NULL; + + ExecBeforeCommandTriggers(&cmd); + } + /* * Insert tuple into pg_foreign_data_wrapper. */ *************** *** 669,674 **** CreateForeignDataWrapper(CreateFdwStmt *stmt) --- 682,694 ---- ForeignDataWrapperRelationId, fdwId, 0); heap_close(rel, RowExclusiveLock); + + /* Call AFTER CREATE FOREIGN DATA WRAPPER triggers */ + if (CommandFiresAfterTriggers(&cmd)) + { + cmd.objectId = fdwId; + ExecAfterCommandTriggers(&cmd); + } } *************** *** 691,696 **** AlterForeignDataWrapper(AlterFdwStmt *stmt) --- 711,717 ---- bool validator_given; Oid fdwhandler; Oid fdwvalidator; + CommandContextData cmd; rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock); *************** *** 713,718 **** AlterForeignDataWrapper(AlterFdwStmt *stmt) --- 734,751 ---- fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp); fdwId = HeapTupleGetOid(tp); + /* Call BEFORE ALTER FOREIGN DATA WRAPPER triggers */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = fdwId; + cmd.objectname = stmt->fdwname; + cmd.schemaname = NULL; + + ExecBeforeCommandTriggers(&cmd); + } + memset(repl_val, 0, sizeof(repl_val)); memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); *************** *** 830,835 **** AlterForeignDataWrapper(AlterFdwStmt *stmt) --- 863,872 ---- } heap_close(rel, RowExclusiveLock); + + /* Call AFTER ALTER FOREIGN DATA WRAPPER triggers */ + if (CommandFiresAfterTriggers(&cmd)) + ExecAfterCommandTriggers(&cmd); } *************** *** 874,879 **** CreateForeignServer(CreateForeignServerStmt *stmt) --- 911,917 ---- ObjectAddress myself; ObjectAddress referenced; ForeignDataWrapper *fdw; + CommandContextData cmd; rel = heap_open(ForeignServerRelationId, RowExclusiveLock); *************** *** 899,904 **** CreateForeignServer(CreateForeignServerStmt *stmt) --- 937,954 ---- if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); + /* Call BEFORE CREATE SERVER triggers */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = InvalidOid; + cmd.objectname = stmt->servername; + cmd.schemaname = NULL; + + ExecBeforeCommandTriggers(&cmd); + } + /* * Insert tuple into pg_foreign_server. */ *************** *** 965,970 **** CreateForeignServer(CreateForeignServerStmt *stmt) --- 1015,1027 ---- InvokeObjectAccessHook(OAT_POST_CREATE, ForeignServerRelationId, srvId, 0); heap_close(rel, RowExclusiveLock); + + /* Call AFTER CREATE SERVER triggers */ + if (CommandFiresAfterTriggers(&cmd)) + { + cmd.objectId = srvId; + ExecAfterCommandTriggers(&cmd); + } } *************** *** 981,986 **** AlterForeignServer(AlterForeignServerStmt *stmt) --- 1038,1044 ---- bool repl_repl[Natts_pg_foreign_server]; Oid srvId; Form_pg_foreign_server srvForm; + CommandContextData cmd; rel = heap_open(ForeignServerRelationId, RowExclusiveLock); *************** *** 1002,1007 **** AlterForeignServer(AlterForeignServerStmt *stmt) --- 1060,1077 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, stmt->servername); + /* Call BEFORE ALTER SERVER triggers */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = srvId; + cmd.objectname = stmt->servername; + cmd.schemaname = NULL; + + ExecBeforeCommandTriggers(&cmd); + } + memset(repl_val, 0, sizeof(repl_val)); memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); *************** *** 1058,1063 **** AlterForeignServer(AlterForeignServerStmt *stmt) --- 1128,1137 ---- heap_freetuple(tp); heap_close(rel, RowExclusiveLock); + + /* Call AFTER ALTER SERVER triggers */ + if (CommandFiresAfterTriggers(&cmd)) + ExecAfterCommandTriggers(&cmd); } *************** *** 1129,1134 **** CreateUserMapping(CreateUserMappingStmt *stmt) --- 1203,1209 ---- ObjectAddress referenced; ForeignServer *srv; ForeignDataWrapper *fdw; + CommandContextData cmd; rel = heap_open(UserMappingRelationId, RowExclusiveLock); *************** *** 1154,1159 **** CreateUserMapping(CreateUserMappingStmt *stmt) --- 1229,1246 ---- fdw = GetForeignDataWrapper(srv->fdwid); + /* Call BEFORE CREATE USER MAPPING triggers */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = InvalidOid; + cmd.objectname = NULL; /* composite object name */ + cmd.schemaname = NULL; + + ExecBeforeCommandTriggers(&cmd); + } + /* * Insert tuple into pg_user_mapping. */ *************** *** 1205,1210 **** CreateUserMapping(CreateUserMappingStmt *stmt) --- 1292,1304 ---- InvokeObjectAccessHook(OAT_POST_CREATE, UserMappingRelationId, umId, 0); heap_close(rel, RowExclusiveLock); + + /* Call AFTER CREATE USER MAPPING triggers */ + if (CommandFiresAfterTriggers(&cmd)) + { + cmd.objectId = umId; + ExecAfterCommandTriggers(&cmd); + } } *************** *** 1222,1227 **** AlterUserMapping(AlterUserMappingStmt *stmt) --- 1316,1322 ---- Oid useId; Oid umId; ForeignServer *srv; + CommandContextData cmd; rel = heap_open(UserMappingRelationId, RowExclusiveLock); *************** *** 1244,1249 **** AlterUserMapping(AlterUserMappingStmt *stmt) --- 1339,1356 ---- if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for user mapping %u", umId); + /* Call BEFORE ALTER USER MAPPING triggers */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = umId; + cmd.objectname = NULL; /* composite object name */ + cmd.schemaname = NULL; + + ExecBeforeCommandTriggers(&cmd); + } + memset(repl_val, 0, sizeof(repl_val)); memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); *************** *** 1291,1296 **** AlterUserMapping(AlterUserMappingStmt *stmt) --- 1398,1407 ---- heap_freetuple(tp); heap_close(rel, RowExclusiveLock); + + /* Call AFTER ALTER SERVER triggers */ + if (CommandFiresAfterTriggers(&cmd)) + ExecAfterCommandTriggers(&cmd); } *************** *** 1304,1309 **** RemoveUserMapping(DropUserMappingStmt *stmt) --- 1415,1421 ---- Oid useId; Oid umId; ForeignServer *srv; + CommandContextData cmd; useId = GetUserOidFromMapping(stmt->username, stmt->missing_ok); srv = GetForeignServerByName(stmt->servername, true); *************** *** 1351,1356 **** RemoveUserMapping(DropUserMappingStmt *stmt) --- 1463,1480 ---- user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername); + /* Call BEFORE DROP USER MAPPING triggers */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = umId; + cmd.objectname = NULL; /* composite object name */ + cmd.schemaname = NULL; + + ExecBeforeCommandTriggers(&cmd); + } + /* * Do the deletion */ *************** *** 1359,1364 **** RemoveUserMapping(DropUserMappingStmt *stmt) --- 1483,1494 ---- object.objectSubId = 0; performDeletion(&object, DROP_CASCADE, 0); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = InvalidOid; + ExecAfterCommandTriggers(&cmd); + } } *** a/src/backend/commands/functioncmds.c --- b/src/backend/commands/functioncmds.c *************** *** 55,60 **** --- 55,61 ---- #include "parser/parse_expr.h" #include "parser/parse_func.h" #include "parser/parse_type.h" + #include "tcop/utility.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" *************** *** 66,72 **** static void AlterFunctionOwner_internal(Relation rel, HeapTuple tup, ! Oid newOwnerId); /* --- 67,73 ---- static void AlterFunctionOwner_internal(Relation rel, HeapTuple tup, ! Oid newOwnerId, CommandContext cmd); /* *************** *** 802,807 **** CreateFunction(CreateFunctionStmt *stmt, const char *queryString) --- 803,809 ---- { char *probin_str; char *prosrc_str; + Oid procOid; Oid prorettype; bool returnsSet; char *language; *************** *** 827,832 **** CreateFunction(CreateFunctionStmt *stmt, const char *queryString) --- 829,835 ---- HeapTuple languageTuple; Form_pg_language languageStruct; List *as_clause; + CommandContextData cmd; /* Convert list of names to a name and namespace */ namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname, *************** *** 970,1001 **** CreateFunction(CreateFunctionStmt *stmt, const char *queryString) errmsg("ROWS is not applicable when function does not return a set"))); /* * And now that we have all the parameters, and know we're permitted to do * so, go ahead and create the function. */ ! ProcedureCreate(funcname, ! namespaceId, ! stmt->replace, ! returnsSet, ! prorettype, ! languageOid, ! languageValidator, ! prosrc_str, /* converted to text later */ ! probin_str, /* converted to text later */ ! false, /* not an aggregate */ ! isWindowFunc, ! security, ! isLeakProof, ! isStrict, ! volatility, ! parameterTypes, ! PointerGetDatum(allParameterTypes), ! PointerGetDatum(parameterModes), ! PointerGetDatum(parameterNames), ! parameterDefaults, ! PointerGetDatum(proconfig), ! procost, ! prorows); } --- 973,1025 ---- errmsg("ROWS is not applicable when function does not return a set"))); /* + * Call BEFORE CREATE FUNCTION triggers + */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = InvalidOid; + cmd.objectname = (char *)funcname; + cmd.schemaname = get_namespace_name(namespaceId); + + ExecBeforeCommandTriggers(&cmd); + } + /* * And now that we have all the parameters, and know we're permitted to do * so, go ahead and create the function. */ ! procOid = ! ProcedureCreate(funcname, ! namespaceId, ! stmt->replace, ! returnsSet, ! prorettype, ! languageOid, ! languageValidator, ! prosrc_str, /* converted to text later */ ! probin_str, /* converted to text later */ ! false, /* not an aggregate */ ! isWindowFunc, ! security, ! isLeakProof, ! isStrict, ! volatility, ! parameterTypes, ! PointerGetDatum(allParameterTypes), ! PointerGetDatum(parameterModes), ! PointerGetDatum(parameterNames), ! parameterDefaults, ! PointerGetDatum(proconfig), ! procost, ! prorows); ! ! /* Call AFTER CREATE FUNCTION triggers */ ! if (CommandFiresAfterTriggers(&cmd)) ! { ! cmd.objectId = procOid; ! ExecAfterCommandTriggers(&cmd); ! } } *************** *** 1053,1059 **** RemoveFunctionById(Oid funcOid) * Rename function */ void ! RenameFunction(List *name, List *argtypes, const char *newname) { Oid procOid; Oid namespaceOid; --- 1077,1083 ---- * Rename function */ void ! RenameFunction(List *name, List *argtypes, const char *newname, CommandContext cmd) { Oid procOid; Oid namespaceOid; *************** *** 1107,1112 **** RenameFunction(List *name, List *argtypes, const char *newname) --- 1131,1146 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* Call BEFORE ALTER FUNCTION triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = procOid; + cmd->objectname = NameListToString(name); + cmd->schemaname = get_namespace_name(namespaceOid); + + ExecBeforeCommandTriggers(cmd); + } + /* rename */ namestrcpy(&(procForm->proname), newname); simple_heap_update(rel, &tup->t_self, tup); *************** *** 1114,1126 **** RenameFunction(List *name, List *argtypes, const char *newname) heap_close(rel, NoLock); heap_freetuple(tup); } /* * Change function owner by name and args */ void ! AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId) { Relation rel; Oid procOid; --- 1148,1168 ---- heap_close(rel, NoLock); heap_freetuple(tup); + + /* Call AFTER ALTER FUNCTION triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectname = (char *)newname; + ExecAfterCommandTriggers(cmd); + } } /* * Change function owner by name and args */ void ! AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId, ! CommandContext cmd) { Relation rel; Oid procOid; *************** *** 1141,1147 **** AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId) NameListToString(name)), errhint("Use ALTER AGGREGATE to change owner of aggregate functions."))); ! AlterFunctionOwner_internal(rel, tup, newOwnerId); heap_close(rel, NoLock); } --- 1183,1189 ---- NameListToString(name)), errhint("Use ALTER AGGREGATE to change owner of aggregate functions."))); ! AlterFunctionOwner_internal(rel, tup, newOwnerId, cmd); heap_close(rel, NoLock); } *************** *** 1150,1156 **** AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId) * Change function owner by Oid */ void ! AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId) { Relation rel; HeapTuple tup; --- 1192,1198 ---- * Change function owner by Oid */ void ! AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId, CommandContext cmd) { Relation rel; HeapTuple tup; *************** *** 1160,1172 **** AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId) tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procOid)); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for function %u", procOid); ! AlterFunctionOwner_internal(rel, tup, newOwnerId); heap_close(rel, NoLock); } static void ! AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) { Form_pg_proc procForm; AclResult aclresult; --- 1202,1215 ---- tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procOid)); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for function %u", procOid); ! AlterFunctionOwner_internal(rel, tup, newOwnerId, cmd); heap_close(rel, NoLock); } static void ! AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId, ! CommandContext cmd) { Form_pg_proc procForm; AclResult aclresult; *************** *** 1212,1217 **** AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) --- 1255,1269 ---- get_namespace_name(procForm->pronamespace)); } + /* Call BEFORE ALTER FUNCTION triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = procOid; + cmd->objectname = NameStr(procForm->proname); + cmd->schemaname = get_namespace_name(procForm->pronamespace); + + ExecBeforeCommandTriggers(cmd); + } memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); *************** *** 1243,1248 **** AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) --- 1295,1304 ---- /* Update owner dependency reference */ changeDependencyOnOwner(ProcedureRelationId, procOid, newOwnerId); + + /* Call AFTER ALTER FUNCTION triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); } ReleaseSysCache(tup); *************** *** 1479,1484 **** CreateCast(CreateCastStmt *stmt) --- 1535,1541 ---- ObjectAddress myself, referenced; AclResult aclresult; + CommandContextData cmd; sourcetypeid = typenameTypeId(NULL, stmt->sourcetype); targettypeid = typenameTypeId(NULL, stmt->targettype); *************** *** 1697,1702 **** CreateCast(CreateCastStmt *stmt) --- 1754,1771 ---- break; } + /* Call BEFORE CREATE CAST command triggers */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = InvalidOid; + cmd.objectname = NULL; /* composite name, not supported */ + cmd.schemaname = NULL; /* casts don't live in a namespace */ + + ExecBeforeCommandTriggers(&cmd); + } + relation = heap_open(CastRelationId, RowExclusiveLock); /* *************** *** 1764,1769 **** CreateCast(CreateCastStmt *stmt) --- 1833,1844 ---- heap_freetuple(tuple); heap_close(relation, RowExclusiveLock); + + if (CommandFiresAfterTriggers(&cmd)) + { + cmd.objectId = castid; + ExecAfterCommandTriggers(&cmd); + } } /* *************** *** 1822,1828 **** DropCastById(Oid castOid) */ void AlterFunctionNamespace(List *name, List *argtypes, bool isagg, ! const char *newschema) { Oid procOid; Oid nspOid; --- 1897,1903 ---- */ void AlterFunctionNamespace(List *name, List *argtypes, bool isagg, ! const char *newschema, CommandContext cmd) { Oid procOid; Oid nspOid; *************** *** 1836,1846 **** AlterFunctionNamespace(List *name, List *argtypes, bool isagg, /* get schema OID and check its permissions */ nspOid = LookupCreationNamespace(newschema); ! AlterFunctionNamespace_oid(procOid, nspOid); } Oid ! AlterFunctionNamespace_oid(Oid procOid, Oid nspOid) { Oid oldNspOid; HeapTuple tup; --- 1911,1921 ---- /* get schema OID and check its permissions */ nspOid = LookupCreationNamespace(newschema); ! AlterFunctionNamespace_oid(procOid, nspOid, cmd); } Oid ! AlterFunctionNamespace_oid(Oid procOid, Oid nspOid, CommandContext cmd) { Oid oldNspOid; HeapTuple tup; *************** *** 1875,1880 **** AlterFunctionNamespace_oid(Oid procOid, Oid nspOid) --- 1950,1965 ---- NameStr(proc->proname), get_namespace_name(nspOid)))); + /* Call BEFORE ALTER FUNCTION triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = procOid; + cmd->objectname = NameStr(proc->proname); + cmd->schemaname = get_namespace_name(oldNspOid); + + ExecBeforeCommandTriggers(cmd); + } + /* OK, modify the pg_proc row */ /* tup is a copy, so we can scribble directly on it */ *************** *** 1893,1898 **** AlterFunctionNamespace_oid(Oid procOid, Oid nspOid) --- 1978,1988 ---- heap_close(procRel, RowExclusiveLock); + if (CommandFiresAfterTriggers(cmd)) + { + cmd->schemaname = get_namespace_name(nspOid); + ExecAfterCommandTriggers(cmd); + } return oldNspOid; } *** a/src/backend/commands/indexcmds.c --- b/src/backend/commands/indexcmds.c *************** *** 318,324 **** DefineIndex(RangeVar *heapRelation, bool check_rights, bool skip_build, bool quiet, ! bool concurrent) { Oid *typeObjectId; Oid *collationObjectId; --- 318,325 ---- bool check_rights, bool skip_build, bool quiet, ! bool concurrent, ! CommandContext cmd) { Oid *typeObjectId; Oid *collationObjectId; *************** *** 574,579 **** DefineIndex(RangeVar *heapRelation, --- 575,597 ---- index_check_primary_key(rel, indexInfo, is_alter_table); /* + * Call BEFORE CREATE INDEX triggers + * + * cmd.tag and cmd.parseetree must have been prepared for us by the caller. + * + * Bootstrap code must be able to skip command triggers, it's passing NULL + * as the CommandContext pointer. + */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = InvalidOid; + cmd->objectname = indexRelationName; + cmd->schemaname = get_namespace_name(namespaceId); + + ExecBeforeCommandTriggers(cmd); + } + + /* * Report index creation if appropriate (delay this till after most of the * error checks) */ *************** *** 625,630 **** DefineIndex(RangeVar *heapRelation, --- 643,655 ---- { /* Close the heap and we're done, in the non-concurrent case */ heap_close(rel, NoLock); + + /* Call AFTER CREATE INDEX triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectId = indexRelationId; + ExecAfterCommandTriggers(cmd); + } return indexRelationId; } *************** *** 914,919 **** DefineIndex(RangeVar *heapRelation, --- 939,949 ---- */ UnlockRelationIdForSession(&heaprelid, ShareUpdateExclusiveLock); + /* + * Don't Call AFTER CREATE INDEX triggers here, because the transaction + * that did the work is already commited, RAISE EXCEPTION in the trigger + * can no longer undo what we did. + */ return indexRelationId; } *************** *** 1735,1741 **** ChooseIndexColumnNames(List *indexElems) * Recreate a specific index. */ void ! ReindexIndex(RangeVar *indexRelation) { Oid indOid; Oid heapOid = InvalidOid; --- 1765,1771 ---- * Recreate a specific index. */ void ! ReindexIndex(RangeVar *indexRelation, CommandContext cmd) { Oid indOid; Oid heapOid = InvalidOid; *************** *** 1746,1752 **** ReindexIndex(RangeVar *indexRelation) RangeVarCallbackForReindexIndex, (void *) &heapOid); ! reindex_index(indOid, false); } /* --- 1776,1782 ---- RangeVarCallbackForReindexIndex, (void *) &heapOid); ! reindex_index(indOid, false, cmd); } /* *************** *** 1813,1819 **** RangeVarCallbackForReindexIndex(const RangeVar *relation, * Recreate all indexes of a table (and of its toast table, if any) */ void ! ReindexTable(RangeVar *relation) { Oid heapOid; --- 1843,1849 ---- * Recreate all indexes of a table (and of its toast table, if any) */ void ! ReindexTable(RangeVar *relation, CommandContext cmd) { Oid heapOid; *************** *** 1821,1827 **** ReindexTable(RangeVar *relation) heapOid = RangeVarGetRelidExtended(relation, ShareLock, false, false, RangeVarCallbackOwnsTable, NULL); ! if (!reindex_relation(heapOid, REINDEX_REL_PROCESS_TOAST)) ereport(NOTICE, (errmsg("table \"%s\" has no indexes", relation->relname))); --- 1851,1857 ---- heapOid = RangeVarGetRelidExtended(relation, ShareLock, false, false, RangeVarCallbackOwnsTable, NULL); ! if (!reindex_relation(heapOid, REINDEX_REL_PROCESS_TOAST, cmd)) ereport(NOTICE, (errmsg("table \"%s\" has no indexes", relation->relname))); *************** *** 1934,1940 **** ReindexDatabase(const char *databaseName, bool do_system, bool do_user) StartTransactionCommand(); /* functions in indexes may want a snapshot set */ PushActiveSnapshot(GetTransactionSnapshot()); ! if (reindex_relation(relid, REINDEX_REL_PROCESS_TOAST)) ereport(NOTICE, (errmsg("table \"%s.%s\" was reindexed", get_namespace_name(get_rel_namespace(relid)), --- 1964,1970 ---- StartTransactionCommand(); /* functions in indexes may want a snapshot set */ PushActiveSnapshot(GetTransactionSnapshot()); ! if (reindex_relation(relid, REINDEX_REL_PROCESS_TOAST, NULL)) ereport(NOTICE, (errmsg("table \"%s.%s\" was reindexed", get_namespace_name(get_rel_namespace(relid)), *** a/src/backend/commands/opclasscmds.c --- b/src/backend/commands/opclasscmds.c *************** *** 350,355 **** DefineOpClass(CreateOpClassStmt *stmt) --- 350,356 ---- NameData opcName; ObjectAddress myself, referenced; + CommandContextData cmd; /* Convert list of names to a name and namespace */ namespaceoid = QualifiedNameGetCreationNamespace(stmt->opclassname, *************** *** 590,595 **** DefineOpClass(CreateOpClassStmt *stmt) --- 591,608 ---- stmt->amname))); } + /* Call BEFORE CREATE OPERATOR CLASS command triggers */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = InvalidOid; + cmd.objectname = stmt->amname; + cmd.schemaname = get_namespace_name(namespaceoid); + + ExecBeforeCommandTriggers(&cmd); + } + rel = heap_open(OperatorClassRelationId, RowExclusiveLock); /* *************** *** 720,725 **** DefineOpClass(CreateOpClassStmt *stmt) --- 733,745 ---- OperatorClassRelationId, opclassoid, 0); heap_close(rel, RowExclusiveLock); + + /* Call AFTER CREATE OPERATOR CLASS command triggers */ + if (CommandFiresAfterTriggers(&cmd)) + { + cmd.objectId = opclassoid; + ExecAfterCommandTriggers(&cmd); + } } *************** *** 731,739 **** void DefineOpFamily(CreateOpFamilyStmt *stmt) { char *opfname; /* name of opfamily we're creating */ ! Oid amoid, /* our AM's oid */ namespaceoid; /* namespace to create opfamily in */ AclResult aclresult; /* Convert list of names to a name and namespace */ namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname, --- 751,761 ---- DefineOpFamily(CreateOpFamilyStmt *stmt) { char *opfname; /* name of opfamily we're creating */ ! Oid opfOid, ! amoid, /* our AM's oid */ namespaceoid; /* namespace to create opfamily in */ AclResult aclresult; + CommandContextData cmd; /* Convert list of names to a name and namespace */ namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname, *************** *** 759,766 **** DefineOpFamily(CreateOpFamilyStmt *stmt) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to create an operator family"))); /* Insert pg_opfamily catalog entry */ ! (void) CreateOpFamily(stmt->amname, opfname, namespaceoid, amoid); } --- 781,807 ---- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to create an operator family"))); + /* Call BEFORE CREATE OPERATOR FAMILY command triggers */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = InvalidOid; + cmd.objectname = opfname; + cmd.schemaname = get_namespace_name(namespaceoid); + + ExecBeforeCommandTriggers(&cmd); + } + /* Insert pg_opfamily catalog entry */ ! opfOid = CreateOpFamily(stmt->amname, opfname, namespaceoid, amoid); ! ! /* Call AFTER CREATE OPERATOR FAMILY command triggers */ ! if (CommandFiresAfterTriggers(&cmd)) ! { ! cmd.objectId = opfOid; ! ExecAfterCommandTriggers(&cmd); ! } } *************** *** 781,786 **** AlterOpFamily(AlterOpFamilyStmt *stmt) --- 822,828 ---- maxProcNumber; /* amsupport value */ HeapTuple tup; Form_pg_am pg_am; + CommandContextData cmd; /* Get necessary info about access method */ tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname)); *************** *** 815,820 **** AlterOpFamily(AlterOpFamilyStmt *stmt) --- 857,882 ---- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to alter an operator family"))); + /* Call BEFORE ALTER OPERATOR FAMILY command triggers */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + HeapTuple htup; + Form_pg_opfamily opfForm; + + htup = OpFamilyCacheLookup(amoid, stmt->opfamilyname, false); + opfForm = (Form_pg_opfamily) GETSTRUCT(htup); + + cmd.objectId = opfamilyoid; + cmd.objectname = NameStr(opfForm->opfname); + cmd.schemaname = get_namespace_name(opfForm->opfnamespace); + + ReleaseSysCache(htup); + + ExecBeforeCommandTriggers(&cmd); + } + /* * ADD and DROP cases need separate code from here on down. */ *************** *** 826,831 **** AlterOpFamily(AlterOpFamilyStmt *stmt) --- 888,897 ---- AlterOpFamilyAdd(stmt->opfamilyname, amoid, opfamilyoid, maxOpNumber, maxProcNumber, stmt->items); + + /* Call AFTER ALTER OPERATOR FAMILY command triggers */ + if (CommandFiresAfterTriggers(&cmd)) + ExecAfterCommandTriggers(&cmd); } /* *************** *** 1919,1925 **** AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) * ALTER OPERATOR CLASS any_name USING access_method SET SCHEMA name */ void ! AlterOpClassNamespace(List *name, char *access_method, const char *newschema) { Oid amOid; Relation rel; --- 1985,1992 ---- * ALTER OPERATOR CLASS any_name USING access_method SET SCHEMA name */ void ! AlterOpClassNamespace(List *name, char *access_method, const char *newschema, ! CommandContext cmd) { Oid amOid; Relation rel; *************** *** 1941,1947 **** AlterOpClassNamespace(List *name, char *access_method, const char *newschema) Anum_pg_opclass_opcname, Anum_pg_opclass_opcnamespace, Anum_pg_opclass_opcowner, ! ACL_KIND_OPCLASS); heap_close(rel, RowExclusiveLock); } --- 2008,2014 ---- Anum_pg_opclass_opcname, Anum_pg_opclass_opcnamespace, Anum_pg_opclass_opcowner, ! ACL_KIND_OPCLASS, cmd); heap_close(rel, RowExclusiveLock); } *************** *** 1960,1966 **** AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid) Anum_pg_opclass_opcname, Anum_pg_opclass_opcnamespace, Anum_pg_opclass_opcowner, ! ACL_KIND_OPCLASS); heap_close(rel, RowExclusiveLock); --- 2027,2033 ---- Anum_pg_opclass_opcname, Anum_pg_opclass_opcnamespace, Anum_pg_opclass_opcowner, ! ACL_KIND_OPCLASS, NULL); heap_close(rel, RowExclusiveLock); *************** *** 2128,2134 **** get_am_oid(const char *amname, bool missing_ok) * ALTER OPERATOR FAMILY any_name USING access_method SET SCHEMA name */ void ! AlterOpFamilyNamespace(List *name, char *access_method, const char *newschema) { Oid amOid; Relation rel; --- 2195,2202 ---- * ALTER OPERATOR FAMILY any_name USING access_method SET SCHEMA name */ void ! AlterOpFamilyNamespace(List *name, char *access_method, const char *newschema, ! CommandContext cmd) { Oid amOid; Relation rel; *************** *** 2150,2156 **** AlterOpFamilyNamespace(List *name, char *access_method, const char *newschema) Anum_pg_opfamily_opfname, Anum_pg_opfamily_opfnamespace, Anum_pg_opfamily_opfowner, ! ACL_KIND_OPFAMILY); heap_close(rel, RowExclusiveLock); } --- 2218,2224 ---- Anum_pg_opfamily_opfname, Anum_pg_opfamily_opfnamespace, Anum_pg_opfamily_opfowner, ! ACL_KIND_OPFAMILY, cmd); heap_close(rel, RowExclusiveLock); } *************** *** 2169,2175 **** AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid) Anum_pg_opfamily_opfname, Anum_pg_opfamily_opfnamespace, Anum_pg_opfamily_opfowner, ! ACL_KIND_OPFAMILY); heap_close(rel, RowExclusiveLock); --- 2237,2243 ---- Anum_pg_opfamily_opfname, Anum_pg_opfamily_opfnamespace, Anum_pg_opfamily_opfowner, ! ACL_KIND_OPFAMILY, NULL); heap_close(rel, RowExclusiveLock); *** a/src/backend/commands/operatorcmds.c --- b/src/backend/commands/operatorcmds.c *************** *** 51,57 **** #include "utils/syscache.h" ! static void AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId); /* * DefineOperator --- 51,58 ---- #include "utils/syscache.h" ! static void AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId, ! CommandContext cmd); /* * DefineOperator *************** *** 62,68 **** static void AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerI * 'parameters' is a list of DefElem */ void ! DefineOperator(List *names, List *parameters) { char *oprName; Oid oprNamespace; --- 63,69 ---- * 'parameters' is a list of DefElem */ void ! DefineOperator(List *names, List *parameters, CommandContext cmd) { char *oprName; Oid oprNamespace; *************** *** 310,316 **** DefineOperator(List *names, List *parameters) restrictionOid, /* optional restrict. sel. procedure */ joinOid, /* optional join sel. procedure name */ canMerge, /* operator merges */ ! canHash); /* operator hashes */ } --- 311,318 ---- restrictionOid, /* optional restrict. sel. procedure */ joinOid, /* optional join sel. procedure name */ canMerge, /* operator merges */ ! canHash, /* operator hashes */ ! cmd); } *************** *** 343,349 **** AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId) rel = heap_open(OperatorRelationId, RowExclusiveLock); ! AlterOperatorOwner_internal(rel, operOid, newOwnerId); heap_close(rel, NoLock); } --- 345,351 ---- rel = heap_open(OperatorRelationId, RowExclusiveLock); ! AlterOperatorOwner_internal(rel, operOid, newOwnerId, NULL); heap_close(rel, NoLock); } *************** *** 353,359 **** AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId) */ void AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2, ! Oid newOwnerId) { Oid operOid; Relation rel; --- 355,361 ---- */ void AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2, ! Oid newOwnerId, CommandContext cmd) { Oid operOid; Relation rel; *************** *** 364,376 **** AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2, typeName1, typeName2, false, -1); ! AlterOperatorOwner_internal(rel, operOid, newOwnerId); heap_close(rel, NoLock); } static void ! AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId) { HeapTuple tup; AclResult aclresult; --- 366,379 ---- typeName1, typeName2, false, -1); ! AlterOperatorOwner_internal(rel, operOid, newOwnerId, cmd); heap_close(rel, NoLock); } static void ! AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId, ! CommandContext cmd) { HeapTuple tup; AclResult aclresult; *************** *** 410,415 **** AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId) --- 413,428 ---- get_namespace_name(oprForm->oprnamespace)); } + /* Call BEFORE ALTER OPERATOR triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = operOid; + cmd->objectname = NameStr(oprForm->oprname); + cmd->schemaname = get_namespace_name(oprForm->oprnamespace); + + ExecBeforeCommandTriggers(cmd); + } + /* * Modify the owner --- okay to scribble on tup because it's a copy */ *************** *** 424,436 **** AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId) } heap_freetuple(tup); } /* * Execute ALTER OPERATOR SET SCHEMA */ void ! AlterOperatorNamespace(List *names, List *argtypes, const char *newschema) { List *operatorName = names; TypeName *typeName1 = (TypeName *) linitial(argtypes); --- 437,454 ---- } heap_freetuple(tup); + + /* Call AFTER ALTER OPERATOR triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); } /* * Execute ALTER OPERATOR SET SCHEMA */ void ! AlterOperatorNamespace(List *names, List *argtypes, const char *newschema, ! CommandContext cmd) { List *operatorName = names; TypeName *typeName1 = (TypeName *) linitial(argtypes); *************** *** 454,460 **** AlterOperatorNamespace(List *names, List *argtypes, const char *newschema) Anum_pg_operator_oprname, Anum_pg_operator_oprnamespace, Anum_pg_operator_oprowner, ! ACL_KIND_OPER); heap_close(rel, RowExclusiveLock); } --- 472,478 ---- Anum_pg_operator_oprname, Anum_pg_operator_oprnamespace, Anum_pg_operator_oprowner, ! ACL_KIND_OPER, cmd); heap_close(rel, RowExclusiveLock); } *************** *** 472,478 **** AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid) Anum_pg_operator_oprname, Anum_pg_operator_oprnamespace, Anum_pg_operator_oprowner, ! ACL_KIND_OPER); heap_close(rel, RowExclusiveLock); --- 490,496 ---- Anum_pg_operator_oprname, Anum_pg_operator_oprnamespace, Anum_pg_operator_oprowner, ! ACL_KIND_OPER, NULL); heap_close(rel, RowExclusiveLock); *** a/src/backend/commands/proclang.c --- b/src/backend/commands/proclang.c *************** *** 49,55 **** typedef struct char *tmpllibrary; /* path of shared library */ } PLTemplate; ! static void create_proc_lang(const char *languageName, bool replace, Oid languageOwner, Oid handlerOid, Oid inlineOid, Oid valOid, bool trusted); static PLTemplate *find_language_template(const char *languageName); --- 49,55 ---- char *tmpllibrary; /* path of shared library */ } PLTemplate; ! static Oid create_proc_lang(const char *languageName, bool replace, Oid languageOwner, Oid handlerOid, Oid inlineOid, Oid valOid, bool trusted); static PLTemplate *find_language_template(const char *languageName); *************** *** 67,75 **** CreateProceduralLanguage(CreatePLangStmt *stmt) PLTemplate *pltemplate; Oid handlerOid, inlineOid, ! valOid; Oid funcrettype; Oid funcargtypes[1]; /* * If we have template information for the language, ignore the supplied --- 67,79 ---- PLTemplate *pltemplate; Oid handlerOid, inlineOid, ! valOid, ! loid; Oid funcrettype; Oid funcargtypes[1]; + CommandContextData cmd; + + InitCommandContext(&cmd, (Node *)stmt, false); /* * If we have template information for the language, ignore the supplied *************** *** 222,231 **** CreateProceduralLanguage(CreatePLangStmt *stmt) else valOid = InvalidOid; /* ok, create it */ ! create_proc_lang(stmt->plname, stmt->replace, GetUserId(), ! handlerOid, inlineOid, ! valOid, pltemplate->tmpltrusted); } else { --- 226,252 ---- else valOid = InvalidOid; + /* Call BEFORE CREATE LANGUAGE command triggers */ + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = InvalidOid; + cmd.objectname = stmt->plname; + cmd.schemaname = NULL; + + ExecBeforeCommandTriggers(&cmd); + } + /* ok, create it */ ! loid = create_proc_lang(stmt->plname, stmt->replace, GetUserId(), ! handlerOid, inlineOid, ! valOid, pltemplate->tmpltrusted); ! ! /* Call AFTER CREATE LANGUAGE command triggers */ ! if (CommandFiresAfterTriggers(&cmd)) ! { ! cmd.objectId = loid; ! ExecAfterCommandTriggers(&cmd); ! } } else { *************** *** 297,313 **** CreateProceduralLanguage(CreatePLangStmt *stmt) else valOid = InvalidOid; /* ok, create it */ ! create_proc_lang(stmt->plname, stmt->replace, GetUserId(), ! handlerOid, inlineOid, ! valOid, stmt->pltrusted); } } /* * Guts of language creation. */ ! static void create_proc_lang(const char *languageName, bool replace, Oid languageOwner, Oid handlerOid, Oid inlineOid, Oid valOid, bool trusted) --- 318,351 ---- else valOid = InvalidOid; + /* Call BEFORE CREATE LANGUAGE command triggers */ + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = InvalidOid; + cmd.objectname = stmt->plname; + cmd.schemaname = NULL; + + ExecBeforeCommandTriggers(&cmd); + } + /* ok, create it */ ! loid = create_proc_lang(stmt->plname, stmt->replace, GetUserId(), ! handlerOid, inlineOid, ! valOid, stmt->pltrusted); ! ! /* Call AFTER CREATE LANGUAGE command triggers */ ! if (CommandFiresAfterTriggers(&cmd)) ! { ! cmd.objectId = loid; ! ExecAfterCommandTriggers(&cmd); ! } } } /* * Guts of language creation. */ ! static Oid create_proc_lang(const char *languageName, bool replace, Oid languageOwner, Oid handlerOid, Oid inlineOid, Oid valOid, bool trusted) *************** *** 431,436 **** create_proc_lang(const char *languageName, bool replace, --- 469,476 ---- LanguageRelationId, myself.objectId, 0); heap_close(rel, RowExclusiveLock); + + return myself.objectId; } /* *** a/src/backend/commands/schemacmds.c --- b/src/backend/commands/schemacmds.c *************** *** 20,25 **** --- 20,26 ---- #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_namespace.h" + #include "commands/cmdtrigger.h" #include "commands/dbcommands.h" #include "commands/schemacmds.h" #include "miscadmin.h" *************** *** 31,37 **** #include "utils/syscache.h" ! static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId); /* * CREATE SCHEMA --- 32,39 ---- #include "utils/syscache.h" ! static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, ! Oid newOwnerId, CommandContext cmd); /* * CREATE SCHEMA *************** *** 49,54 **** CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) --- 51,57 ---- Oid saved_uid; int save_sec_context; AclResult aclresult; + CommandContextData cmd; GetUserIdAndSecContext(&saved_uid, &save_sec_context); *************** *** 82,87 **** CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) --- 85,103 ---- errdetail("The prefix \"pg_\" is reserved for system schemas."))); /* + * Call BEFORE CREATE SCHEMA triggers (before changing authorization) + */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = InvalidOid; + cmd.objectname = (char *)schemaName; + cmd.schemaname = NULL; /* a schema does not live in another schema */ + + ExecBeforeCommandTriggers(&cmd); + } + /* * If the requested authorization is different from the current user, * temporarily set the current user so that the object(s) will be created * with the correct ownership. *************** *** 144,149 **** CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) --- 160,172 ---- /* Reset current user and security context */ SetUserIdAndSecContext(saved_uid, save_sec_context); + + /* Call AFTER CREATE SCHEMA triggers */ + if (CommandFiresAfterTriggers(&cmd)) + { + cmd.objectId = namespaceId; + ExecAfterCommandTriggers(&cmd); + } } /* *************** *** 174,180 **** RemoveSchemaById(Oid schemaOid) * Rename schema */ void ! RenameSchema(const char *oldname, const char *newname) { HeapTuple tup; Relation rel; --- 197,203 ---- * Rename schema */ void ! RenameSchema(const char *oldname, const char *newname, CommandContext cmd) { HeapTuple tup; Relation rel; *************** *** 211,216 **** RenameSchema(const char *oldname, const char *newname) --- 234,249 ---- errmsg("unacceptable schema name \"%s\"", newname), errdetail("The prefix \"pg_\" is reserved for system schemas."))); + /* Call BEFORE ALTER SCHEMA triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = (char *)oldname; + cmd->schemaname = NULL; + + ExecBeforeCommandTriggers(cmd); + } + /* rename */ namestrcpy(&(((Form_pg_namespace) GETSTRUCT(tup))->nspname), newname); simple_heap_update(rel, &tup->t_self, tup); *************** *** 218,223 **** RenameSchema(const char *oldname, const char *newname) --- 251,263 ---- heap_close(rel, NoLock); heap_freetuple(tup); + + /* Call AFTER ALTER SCHEMA triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectname = (char *)newname; + ExecAfterCommandTriggers(cmd); + } } void *************** *** 232,238 **** AlterSchemaOwner_oid(Oid oid, Oid newOwnerId) if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for schema %u", oid); ! AlterSchemaOwner_internal(tup, rel, newOwnerId); ReleaseSysCache(tup); --- 272,278 ---- if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for schema %u", oid); ! AlterSchemaOwner_internal(tup, rel, newOwnerId, NULL); ReleaseSysCache(tup); *************** *** 244,250 **** AlterSchemaOwner_oid(Oid oid, Oid newOwnerId) * Change schema owner */ void ! AlterSchemaOwner(const char *name, Oid newOwnerId) { HeapTuple tup; Relation rel; --- 284,290 ---- * Change schema owner */ void ! AlterSchemaOwner(const char *name, Oid newOwnerId, CommandContext cmd) { HeapTuple tup; Relation rel; *************** *** 257,263 **** AlterSchemaOwner(const char *name, Oid newOwnerId) (errcode(ERRCODE_UNDEFINED_SCHEMA), errmsg("schema \"%s\" does not exist", name))); ! AlterSchemaOwner_internal(tup, rel, newOwnerId); ReleaseSysCache(tup); --- 297,303 ---- (errcode(ERRCODE_UNDEFINED_SCHEMA), errmsg("schema \"%s\" does not exist", name))); ! AlterSchemaOwner_internal(tup, rel, newOwnerId, cmd); ReleaseSysCache(tup); *************** *** 265,271 **** AlterSchemaOwner(const char *name, Oid newOwnerId) } static void ! AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) { Form_pg_namespace nspForm; --- 305,312 ---- } static void ! AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId, ! CommandContext cmd) { Form_pg_namespace nspForm; *************** *** 312,317 **** AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) --- 353,368 ---- aclcheck_error(aclresult, ACL_KIND_DATABASE, get_database_name(MyDatabaseId)); + /* Call BEFORE ALTER SCHEMA triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = NameStr(nspForm->nspname); + cmd->schemaname = NULL; + + ExecBeforeCommandTriggers(cmd); + } + memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); *************** *** 343,348 **** AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) /* Update owner dependency reference */ changeDependencyOnOwner(NamespaceRelationId, HeapTupleGetOid(tup), newOwnerId); - } } --- 394,402 ---- /* Update owner dependency reference */ changeDependencyOnOwner(NamespaceRelationId, HeapTupleGetOid(tup), newOwnerId); + /* Call AFTER ALTER SCHEMA triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); + } } *** a/src/backend/commands/sequence.c --- b/src/backend/commands/sequence.c *************** *** 20,25 **** --- 20,26 ---- #include "catalog/namespace.h" #include "catalog/pg_type.h" #include "commands/defrem.h" + #include "commands/cmdtrigger.h" #include "commands/sequence.h" #include "commands/tablecmds.h" #include "funcapi.h" *************** *** 28,33 **** --- 29,35 ---- #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" + #include "tcop/utility.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" *************** *** 115,127 **** DefineSequence(CreateSeqStmt *seq) bool null[SEQ_COL_LASTCOL]; int i; NameData name; /* Unlogged sequences are not implemented -- not clear if useful. */ if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("unlogged sequences are not supported"))); - /* Check and set all option values */ init_params(seq->options, true, &new, &owned_by); --- 117,129 ---- bool null[SEQ_COL_LASTCOL]; int i; NameData name; + CommandContextData cmd; /* Unlogged sequences are not implemented -- not clear if useful. */ if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("unlogged sequences are not supported"))); /* Check and set all option values */ init_params(seq->options, true, &new, &owned_by); *************** *** 211,216 **** DefineSequence(CreateSeqStmt *seq) --- 213,232 ---- stmt->tablespacename = NULL; stmt->if_not_exists = false; + /* + * Call BEFORE CREATE SEQUENCE triggers + */ + InitCommandContext(&cmd, (Node *)seq, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = InvalidOid; + cmd.objectname = NameStr(name); + cmd.schemaname = NULL; /* can't publish it easily enough here */ + + ExecBeforeCommandTriggers(&cmd); + } + seqoid = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId); Assert(seqoid != InvalidOid); *************** *** 226,231 **** DefineSequence(CreateSeqStmt *seq) --- 242,254 ---- process_owned_by(rel, owned_by); heap_close(rel, NoLock); + + /* Call AFTER CREATE SEQUENCE triggers */ + if (CommandFiresAfterTriggers(&cmd)) + { + cmd.objectId = seqoid; + ExecAfterCommandTriggers(&cmd); + } } /* *************** *** 423,428 **** AlterSequence(AlterSeqStmt *stmt) --- 446,452 ---- Form_pg_sequence seq; FormData_pg_sequence new; List *owned_by; + CommandContextData cmd; /* Open and lock sequence. */ relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok); *************** *** 458,463 **** AlterSequence(AlterSeqStmt *stmt) --- 482,501 ---- /* Now okay to update the on-disk tuple */ memcpy(seq, &new, sizeof(FormData_pg_sequence)); + /* + * Call BEFORE ALTER SEQUENCE triggers + */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (ListCommandTriggers(&cmd)) + { + cmd.objectId = relid; + cmd.objectname = stmt->sequence->relname; + cmd.schemaname = stmt->sequence->schemaname; + + ExecBeforeCommandTriggers(&cmd); + } + START_CRIT_SECTION(); MarkBufferDirty(buf); *************** *** 496,501 **** AlterSequence(AlterSeqStmt *stmt) --- 534,543 ---- process_owned_by(seqrel, owned_by); relation_close(seqrel, NoLock); + + /* Call AFTER ALTER SEQUENCE triggers */ + if (CommandFiresAfterTriggers(&cmd)) + ExecAfterCommandTriggers(&cmd); } *** a/src/backend/commands/tablecmds.c --- b/src/backend/commands/tablecmds.c *************** *** 41,46 **** --- 41,47 ---- #include "catalog/pg_type_fn.h" #include "catalog/storage.h" #include "catalog/toasting.h" + #include "commands/cmdtrigger.h" #include "commands/cluster.h" #include "commands/comment.h" #include "commands/defrem.h" *************** *** 73,78 **** --- 74,80 ---- #include "storage/lock.h" #include "storage/predicate.h" #include "storage/smgr.h" + #include "tcop/utility.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" *************** *** 735,740 **** RemoveRelations(DropStmt *drop) --- 737,744 ---- ObjectAddresses *objects; char relkind; ListCell *cell; + int i = 0, n = list_length(drop->objects); + CommandContext *cmds = (CommandContext *) palloc(n * sizeof(CommandContext)); /* * First we identify all the relations, then we delete them in a single *************** *** 781,786 **** RemoveRelations(DropStmt *drop) --- 785,791 ---- Oid relOid; ObjectAddress obj; struct DropRelationCallbackState state; + CommandContextData cmd; /* * These next few steps are a great deal like relation_openrv, but we *************** *** 806,814 **** RemoveRelations(DropStmt *drop) --- 811,835 ---- if (!OidIsValid(relOid)) { DropErrorMsgNonExistent(rel->relname, relkind, drop->missing_ok); + cmds[i++] = NULL; continue; } + /* + * Call BEFORE DROP command triggers + */ + InitCommandContext(&cmd, (Node *)drop, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = relOid; + cmd.objectname = get_rel_name(relOid); + cmd.schemaname = get_namespace_name(get_rel_namespace(relOid)); + + ExecBeforeCommandTriggers(&cmd); + } + cmds[i++] = &cmd; + /* OK, we're ready to delete this one */ obj.classId = RelationRelationId; obj.objectId = relOid; *************** *** 819,824 **** RemoveRelations(DropStmt *drop) --- 840,855 ---- performMultipleDeletions(objects, drop->behavior, 0); + /* Call AFTER DROP command triggers */ + for(i = 0; iobjectId = InvalidOid; + ExecAfterCommandTriggers(cmds[i]); + } + } + free_object_addresses(objects); } *************** *** 1136,1142 **** ExecuteTruncate(TruncateStmt *stmt) /* * Reconstruct the indexes to match, and we're done. */ ! reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST); } } --- 1167,1173 ---- /* * Reconstruct the indexes to match, and we're done. */ ! reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST, NULL); } } *************** *** 2328,2334 **** renameatt(RenameStmt *stmt) * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE RENAME */ void ! RenameRelation(RenameStmt *stmt) { Oid relid; --- 2359,2365 ---- * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE RENAME */ void ! RenameRelation(RenameStmt *stmt, CommandContext cmd) { Oid relid; *************** *** 2353,2359 **** RenameRelation(RenameStmt *stmt) } /* Do the work */ ! RenameRelationInternal(relid, stmt->newname); } /* --- 2384,2390 ---- } /* Do the work */ ! RenameRelationInternal(relid, stmt->newname, cmd); } /* *************** *** 2366,2372 **** RenameRelation(RenameStmt *stmt) * sequence, AFAIK there's no need for it to be there. */ void ! RenameRelationInternal(Oid myrelid, const char *newrelname) { Relation targetrelation; Relation relrelation; /* for RELATION relation */ --- 2397,2403 ---- * sequence, AFAIK there's no need for it to be there. */ void ! RenameRelationInternal(Oid myrelid, const char *newrelname, CommandContext cmd) { Relation targetrelation; Relation relrelation; /* for RELATION relation */ *************** *** 2397,2402 **** RenameRelationInternal(Oid myrelid, const char *newrelname) --- 2428,2443 ---- errmsg("relation \"%s\" already exists", newrelname))); + /* Call BEFORE ALTER relation triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(reltup); + cmd->objectname = NameStr(relform->relname); + cmd->schemaname = get_namespace_name(namespaceId); + + ExecBeforeCommandTriggers(cmd); + } + /* * Update pg_class tuple with new relname. (Scribbling on reltup is OK * because it's a copy...) *************** *** 2416,2422 **** RenameRelationInternal(Oid myrelid, const char *newrelname) */ if (OidIsValid(targetrelation->rd_rel->reltype)) RenameTypeInternal(targetrelation->rd_rel->reltype, ! newrelname, namespaceId); /* * Also rename the associated constraint, if any. --- 2457,2463 ---- */ if (OidIsValid(targetrelation->rd_rel->reltype)) RenameTypeInternal(targetrelation->rd_rel->reltype, ! newrelname, namespaceId, NULL); /* * Also rename the associated constraint, if any. *************** *** 2433,2438 **** RenameRelationInternal(Oid myrelid, const char *newrelname) --- 2474,2486 ---- * Close rel, but keep exclusive lock! */ relation_close(targetrelation, NoLock); + + /* Call AFTER ALTER relation triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectname = (char *)newrelname; + ExecAfterCommandTriggers(cmd); + } } /* *************** *** 5331,5337 **** ATExecAddIndex(AlteredTableInfo *tab, Relation rel, check_rights, skip_build, quiet, ! false); /* * If TryReuseIndex() stashed a relfilenode for us, we used it for the new --- 5379,5385 ---- check_rights, skip_build, quiet, ! false, NULL); /* * If TryReuseIndex() stashed a relfilenode for us, we used it for the new *************** *** 5390,5396 **** ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, ereport(NOTICE, (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"", indexName, constraintName))); ! RenameRelationInternal(index_oid, constraintName); } /* Extra checks needed if making primary key */ --- 5438,5444 ---- ereport(NOTICE, (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"", indexName, constraintName))); ! RenameRelationInternal(index_oid, constraintName, NULL); } /* Extra checks needed if making primary key */ *************** *** 9479,9484 **** AlterTableNamespace(AlterObjectSchemaStmt *stmt) --- 9527,9533 ---- Oid nspOid; Relation classRel; RangeVar *newrv; + CommandContextData cmd; relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock, stmt->missing_ok, false, *************** *** 9519,9531 **** AlterTableNamespace(AlterObjectSchemaStmt *stmt) /* common checks on switching namespaces */ CheckSetNamespace(oldNspOid, nspOid, RelationRelationId, relid); /* OK, modify the pg_class row and pg_depend entry */ classRel = heap_open(RelationRelationId, RowExclusiveLock); AlterRelationNamespaceInternal(classRel, relid, oldNspOid, nspOid, true); /* Fix the table's row type too */ ! AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid, false, false); /* Fix other dependent stuff */ if (rel->rd_rel->relkind == RELKIND_RELATION) --- 9568,9592 ---- /* common checks on switching namespaces */ CheckSetNamespace(oldNspOid, nspOid, RelationRelationId, relid); + /* Call BEFORE ALTER TABLE triggers */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = relid; + cmd.objectname = stmt->relation->relname; + cmd.schemaname = get_namespace_name(oldNspOid); + + ExecBeforeCommandTriggers(&cmd); + } + /* OK, modify the pg_class row and pg_depend entry */ classRel = heap_open(RelationRelationId, RowExclusiveLock); AlterRelationNamespaceInternal(classRel, relid, oldNspOid, nspOid, true); /* Fix the table's row type too */ ! AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid, false, false, NULL); /* Fix other dependent stuff */ if (rel->rd_rel->relkind == RELKIND_RELATION) *************** *** 9540,9545 **** AlterTableNamespace(AlterObjectSchemaStmt *stmt) --- 9601,9613 ---- /* close rel, but keep lock until commit */ relation_close(rel, NoLock); + + /* Call AFTER ALTER TABLE triggers */ + if (CommandFiresAfterTriggers(&cmd)) + { + cmd.schemaname = get_namespace_name(nspOid); + ExecAfterCommandTriggers(&cmd); + } } /* *************** *** 9687,9693 **** AlterSeqNamespaces(Relation classRel, Relation rel, * them to the new namespace, too. */ AlterTypeNamespaceInternal(RelationGetForm(seqRel)->reltype, ! newNspOid, false, false); /* Now we can close it. Keep the lock till end of transaction. */ relation_close(seqRel, NoLock); --- 9755,9761 ---- * them to the new namespace, too. */ AlterTypeNamespaceInternal(RelationGetForm(seqRel)->reltype, ! newNspOid, false, false, NULL); /* Now we can close it. Keep the lock till end of transaction. */ relation_close(seqRel, NoLock); *** a/src/backend/commands/tablespace.c --- b/src/backend/commands/tablespace.c *************** *** 67,72 **** --- 67,73 ---- #include "postmaster/bgwriter.h" #include "storage/fd.h" #include "storage/standby.h" + #include "tcop/utility.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" *************** *** 365,370 **** CreateTableSpace(CreateTableSpaceStmt *stmt) --- 366,372 ---- /* We keep the lock on pg_tablespace until commit */ heap_close(rel, NoLock); + #else /* !HAVE_SYMLINK */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), *************** *** 518,523 **** DropTableSpace(DropTableSpaceStmt *stmt) --- 520,526 ---- /* We keep the lock on pg_tablespace until commit */ heap_close(rel, NoLock); + #else /* !HAVE_SYMLINK */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), *************** *** 812,817 **** RenameTableSpace(const char *oldname, const char *newname) --- 815,821 ---- HeapScanDesc scan; HeapTuple tup; HeapTuple newtuple; + Oid oid; Form_pg_tablespace newform; /* Search pg_tablespace */ *************** *** 829,834 **** RenameTableSpace(const char *oldname, const char *newname) --- 833,839 ---- errmsg("tablespace \"%s\" does not exist", oldname))); + oid = HeapTupleGetOid(tup); newtuple = heap_copytuple(tup); newform = (Form_pg_tablespace) GETSTRUCT(newtuple); *** a/src/backend/commands/trigger.c --- b/src/backend/commands/trigger.c *************** *** 144,149 **** CreateTrigger(CreateTrigStmt *stmt, const char *queryString, --- 144,150 ---- Oid constrrelid = InvalidOid; ObjectAddress myself, referenced; + CommandContextData cmd; rel = heap_openrv(stmt->relation, AccessExclusiveLock); *************** *** 426,431 **** CreateTrigger(CreateTrigStmt *stmt, const char *queryString, --- 427,449 ---- } /* + * Call BEFORE CREATE TRIGGER triggers + */ + if (!isInternal) + { + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = InvalidOid; + cmd.objectname = stmt->trigname; + cmd.schemaname = get_namespace_name(RelationGetNamespace(rel)); + + ExecBeforeCommandTriggers(&cmd); + } + } + + /* * If it's a user-entered CREATE CONSTRAINT TRIGGER command, make a * corresponding pg_constraint entry. */ *************** *** 761,766 **** CreateTrigger(CreateTrigStmt *stmt, const char *queryString, --- 779,790 ---- /* Keep lock on target rel until end of xact */ heap_close(rel, NoLock); + /* Call AFTER CREATE TRIGGER triggers */ + if (!isInternal && CommandFiresAfterTriggers(&cmd)) + { + cmd.objectId = trigoid; + ExecAfterCommandTriggers(&cmd); + } return trigoid; } *************** *** 1208,1214 **** RangeVarCallbackForRenameTrigger(const RangeVar *rv, Oid relid, Oid oldrelid, * update row in catalog */ void ! renametrig(RenameStmt *stmt) { Relation targetrel; Relation tgrel; --- 1232,1238 ---- * update row in catalog */ void ! renametrig(RenameStmt *stmt, CommandContext cmd) { Relation targetrel; Relation tgrel; *************** *** 1275,1280 **** renametrig(RenameStmt *stmt) --- 1299,1314 ---- SnapshotNow, 2, key); if (HeapTupleIsValid(tuple = systable_getnext(tgscan))) { + /* Call BEFORE ALTER TRIGGER triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tuple); + cmd->objectname = stmt->subname; + cmd->schemaname = get_namespace_name(RelationGetNamespace(targetrel)); + + ExecBeforeCommandTriggers(cmd); + } + /* * Update pg_trigger tuple with new tgname. */ *************** *** 1311,1316 **** renametrig(RenameStmt *stmt) --- 1345,1357 ---- * Close rel, but keep exclusive lock! */ relation_close(targetrel, NoLock); + + /* Call AFTER ALTER TRIGGER triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectname = stmt->newname; + ExecAfterCommandTriggers(cmd); + } } *** a/src/backend/commands/tsearchcmds.c --- b/src/backend/commands/tsearchcmds.c *************** *** 32,37 **** --- 32,38 ---- #include "catalog/pg_ts_template.h" #include "catalog/pg_type.h" #include "commands/alter.h" + #include "commands/cmdtrigger.h" #include "commands/defrem.h" #include "miscadmin.h" #include "nodes/makefuncs.h" *************** *** 167,173 **** makeParserDependencies(HeapTuple tuple) * CREATE TEXT SEARCH PARSER */ void ! DefineTSParser(List *names, List *parameters) { char *prsname; ListCell *pl; --- 168,174 ---- * CREATE TEXT SEARCH PARSER */ void ! DefineTSParser(List *names, List *parameters, CommandContext cmd) { char *prsname; ListCell *pl; *************** *** 258,263 **** DefineTSParser(List *names, List *parameters) --- 259,276 ---- errmsg("text search parser lextypes method is required"))); /* + * Call BEFORE CREATE TEXT SEARCH PARSER triggers + */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = InvalidOid; + cmd->objectname = NameStr(pname); + cmd->schemaname = get_namespace_name(namespaceoid); + + ExecBeforeCommandTriggers(cmd); + } + + /* * Looks good, insert */ prsRel = heap_open(TSParserRelationId, RowExclusiveLock); *************** *** 276,281 **** DefineTSParser(List *names, List *parameters) --- 289,301 ---- heap_freetuple(tup); heap_close(prsRel, RowExclusiveLock); + + /* Call AFTER CREATE TEXT SEARCH PARSER triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectId = prsOid; + ExecAfterCommandTriggers(cmd); + } } /* *************** *** 305,311 **** RemoveTSParserById(Oid prsId) * ALTER TEXT SEARCH PARSER RENAME */ void ! RenameTSParser(List *oldname, const char *newname) { HeapTuple tup; Relation rel; --- 325,331 ---- * ALTER TEXT SEARCH PARSER RENAME */ void ! RenameTSParser(List *oldname, const char *newname, CommandContext cmd) { HeapTuple tup; Relation rel; *************** *** 336,354 **** RenameTSParser(List *oldname, const char *newname) errmsg("text search parser \"%s\" already exists", newname))); namestrcpy(&(((Form_pg_ts_parser) GETSTRUCT(tup))->prsname), newname); simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); heap_close(rel, NoLock); heap_freetuple(tup); } /* * ALTER TEXT SEARCH PARSER any_name SET SCHEMA name */ void ! AlterTSParserNamespace(List *name, const char *newschema) { Oid prsId, nspOid; --- 356,391 ---- errmsg("text search parser \"%s\" already exists", newname))); + /* Call BEFORE ALTER TEXT SEARCH PARSER triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = prsId; + cmd->objectname = NameStr(((Form_pg_ts_parser) GETSTRUCT(tup))->prsname); + cmd->schemaname = get_namespace_name(namespaceOid); + + ExecBeforeCommandTriggers(cmd); + } + namestrcpy(&(((Form_pg_ts_parser) GETSTRUCT(tup))->prsname), newname); simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); heap_close(rel, NoLock); heap_freetuple(tup); + + /* Call AFTER ALTER TEXT SEARCH PARSER triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectname = (char *)newname; + ExecAfterCommandTriggers(cmd); + } } /* * ALTER TEXT SEARCH PARSER any_name SET SCHEMA name */ void ! AlterTSParserNamespace(List *name, const char *newschema, CommandContext cmd) { Oid prsId, nspOid; *************** *** 365,371 **** AlterTSParserNamespace(List *name, const char *newschema) prsId, nspOid, Anum_pg_ts_parser_prsname, Anum_pg_ts_parser_prsnamespace, ! -1, -1); heap_close(rel, RowExclusiveLock); } --- 402,408 ---- prsId, nspOid, Anum_pg_ts_parser_prsname, Anum_pg_ts_parser_prsnamespace, ! -1, -1, cmd); heap_close(rel, RowExclusiveLock); } *************** *** 383,389 **** AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid) prsId, newNspOid, Anum_pg_ts_parser_prsname, Anum_pg_ts_parser_prsnamespace, ! -1, -1); heap_close(rel, RowExclusiveLock); --- 420,426 ---- prsId, newNspOid, Anum_pg_ts_parser_prsname, Anum_pg_ts_parser_prsnamespace, ! -1, -1, NULL); heap_close(rel, RowExclusiveLock); *************** *** 484,490 **** verify_dictoptions(Oid tmplId, List *dictoptions) * CREATE TEXT SEARCH DICTIONARY */ void ! DefineTSDictionary(List *names, List *parameters) { ListCell *pl; Relation dictRel; --- 521,527 ---- * CREATE TEXT SEARCH DICTIONARY */ void ! DefineTSDictionary(List *names, List *parameters, CommandContext cmd) { ListCell *pl; Relation dictRel; *************** *** 537,542 **** DefineTSDictionary(List *names, List *parameters) --- 574,591 ---- verify_dictoptions(templId, dictoptions); /* + * Call BEFORE CREATE TEXT SEARCH DICTIONARY triggers + */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = InvalidOid; + cmd->objectname = NameStr(dname); + cmd->schemaname = get_namespace_name(namespaceoid); + + ExecBeforeCommandTriggers(cmd); + } + + /* * Looks good, insert */ memset(values, 0, sizeof(values)); *************** *** 570,582 **** DefineTSDictionary(List *names, List *parameters) heap_freetuple(tup); heap_close(dictRel, RowExclusiveLock); } /* * ALTER TEXT SEARCH DICTIONARY RENAME */ void ! RenameTSDictionary(List *oldname, const char *newname) { HeapTuple tup; Relation rel; --- 619,638 ---- heap_freetuple(tup); heap_close(dictRel, RowExclusiveLock); + + /* Call AFTER CREATE TEXT SEARCH DICTIONARY triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectId = dictOid; + ExecAfterCommandTriggers(cmd); + } } /* * ALTER TEXT SEARCH DICTIONARY RENAME */ void ! RenameTSDictionary(List *oldname, const char *newname, CommandContext cmd) { HeapTuple tup; Relation rel; *************** *** 615,633 **** RenameTSDictionary(List *oldname, const char *newname) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); namestrcpy(&(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname), newname); simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); heap_close(rel, NoLock); heap_freetuple(tup); } /* * ALTER TEXT SEARCH DICTIONARY any_name SET SCHEMA name */ void ! AlterTSDictionaryNamespace(List *name, const char *newschema) { Oid dictId, nspOid; --- 671,706 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* Call BEFORE ALTER TEXT SEARCH DICTIONARY triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = dictId; + cmd->objectname = NameStr(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname); + cmd->schemaname = get_namespace_name(namespaceOid); + + ExecBeforeCommandTriggers(cmd); + } + namestrcpy(&(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname), newname); simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); heap_close(rel, NoLock); heap_freetuple(tup); + + /* Call AFTER ALTER TEXT SEARCH DICTIONARY triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectname = (char *)newname; + ExecAfterCommandTriggers(cmd); + } } /* * ALTER TEXT SEARCH DICTIONARY any_name SET SCHEMA name */ void ! AlterTSDictionaryNamespace(List *name, const char *newschema, CommandContext cmd) { Oid dictId, nspOid; *************** *** 645,651 **** AlterTSDictionaryNamespace(List *name, const char *newschema) Anum_pg_ts_dict_dictname, Anum_pg_ts_dict_dictnamespace, Anum_pg_ts_dict_dictowner, ! ACL_KIND_TSDICTIONARY); heap_close(rel, RowExclusiveLock); } --- 718,724 ---- Anum_pg_ts_dict_dictname, Anum_pg_ts_dict_dictnamespace, Anum_pg_ts_dict_dictowner, ! ACL_KIND_TSDICTIONARY, cmd); heap_close(rel, RowExclusiveLock); } *************** *** 664,670 **** AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid) Anum_pg_ts_dict_dictname, Anum_pg_ts_dict_dictnamespace, Anum_pg_ts_dict_dictowner, ! ACL_KIND_TSDICTIONARY); heap_close(rel, RowExclusiveLock); --- 737,743 ---- Anum_pg_ts_dict_dictname, Anum_pg_ts_dict_dictnamespace, Anum_pg_ts_dict_dictowner, ! ACL_KIND_TSDICTIONARY, NULL); heap_close(rel, RowExclusiveLock); *************** *** 712,717 **** AlterTSDictionary(AlterTSDictionaryStmt *stmt) --- 785,791 ---- Datum repl_val[Natts_pg_ts_dict]; bool repl_null[Natts_pg_ts_dict]; bool repl_repl[Natts_pg_ts_dict]; + CommandContextData cmd; dictId = get_ts_dict_oid(stmt->dictname, false); *************** *** 775,780 **** AlterTSDictionary(AlterTSDictionaryStmt *stmt) --- 849,867 ---- verify_dictoptions(((Form_pg_ts_dict) GETSTRUCT(tup))->dicttemplate, dictoptions); + /* Call BEFORE ALTER TEXT SEARCH DICTIONARY command triggers */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + Form_pg_ts_dict form = (Form_pg_ts_dict) GETSTRUCT(tup); + cmd.objectId = dictId; + cmd.objectname = NameStr(form->dictname); + cmd.schemaname = get_namespace_name(form->dictnamespace); + + ExecBeforeCommandTriggers(&cmd); + } + /* * Looks good, update */ *************** *** 806,818 **** AlterTSDictionary(AlterTSDictionaryStmt *stmt) ReleaseSysCache(tup); heap_close(rel, RowExclusiveLock); } /* * ALTER TEXT SEARCH DICTIONARY OWNER */ void ! AlterTSDictionaryOwner(List *name, Oid newOwnerId) { HeapTuple tup; Relation rel; --- 893,909 ---- ReleaseSysCache(tup); heap_close(rel, RowExclusiveLock); + + /* Call AFTER ALTER TEXT SEARCH DICTIONARY command triggers */ + if (CommandFiresAfterTriggers(&cmd)) + ExecAfterCommandTriggers(&cmd); } /* * ALTER TEXT SEARCH DICTIONARY OWNER */ void ! AlterTSDictionaryOwner(List *name, Oid newOwnerId, CommandContext cmd) { HeapTuple tup; Relation rel; *************** *** 854,859 **** AlterTSDictionaryOwner(List *name, Oid newOwnerId) --- 945,960 ---- get_namespace_name(namespaceOid)); } + /* Call BEFORE ALTER TEXT SEARCH DICTIONARY triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = dictId; + cmd->objectname = NameStr(form->dictname); + cmd->schemaname = get_namespace_name(namespaceOid); + + ExecBeforeCommandTriggers(cmd); + } + form->dictowner = newOwnerId; simple_heap_update(rel, &tup->t_self, tup); *************** *** 862,867 **** AlterTSDictionaryOwner(List *name, Oid newOwnerId) --- 963,972 ---- /* Update owner dependency reference */ changeDependencyOnOwner(TSDictionaryRelationId, HeapTupleGetOid(tup), newOwnerId); + + /* Call AFTER ALTER TEXT SEARCH DICTIONARY triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); } heap_close(rel, NoLock); *************** *** 956,962 **** makeTSTemplateDependencies(HeapTuple tuple) * CREATE TEXT SEARCH TEMPLATE */ void ! DefineTSTemplate(List *names, List *parameters) { ListCell *pl; Relation tmplRel; --- 1061,1067 ---- * CREATE TEXT SEARCH TEMPLATE */ void ! DefineTSTemplate(List *names, List *parameters, CommandContext cmd) { ListCell *pl; Relation tmplRel; *************** *** 1022,1027 **** DefineTSTemplate(List *names, List *parameters) --- 1127,1144 ---- errmsg("text search template lexize method is required"))); /* + * Call BEFORE CREATE TEXT SEARCH TEMPLATE triggers + */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = InvalidOid; + cmd->objectname = NameStr(dname); + cmd->schemaname = get_namespace_name(namespaceoid); + + ExecBeforeCommandTriggers(cmd); + } + + /* * Looks good, insert */ *************** *** 1041,1053 **** DefineTSTemplate(List *names, List *parameters) heap_freetuple(tup); heap_close(tmplRel, RowExclusiveLock); } /* * ALTER TEXT SEARCH TEMPLATE RENAME */ void ! RenameTSTemplate(List *oldname, const char *newname) { HeapTuple tup; Relation rel; --- 1158,1177 ---- heap_freetuple(tup); heap_close(tmplRel, RowExclusiveLock); + + /* Call AFTER CREATE TEXT SEARCH TEMPLATE triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectId = dictOid; + ExecAfterCommandTriggers(cmd); + } } /* * ALTER TEXT SEARCH TEMPLATE RENAME */ void ! RenameTSTemplate(List *oldname, const char *newname, CommandContext cmd) { HeapTuple tup; Relation rel; *************** *** 1079,1097 **** RenameTSTemplate(List *oldname, const char *newname) errmsg("text search template \"%s\" already exists", newname))); namestrcpy(&(((Form_pg_ts_template) GETSTRUCT(tup))->tmplname), newname); simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); heap_close(rel, NoLock); heap_freetuple(tup); } /* * ALTER TEXT SEARCH TEMPLATE any_name SET SCHEMA name */ void ! AlterTSTemplateNamespace(List *name, const char *newschema) { Oid tmplId, nspOid; --- 1203,1238 ---- errmsg("text search template \"%s\" already exists", newname))); + /* Call BEFORE ALTER TEXT SEARCH TEMPLATE triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = tmplId; + cmd->objectname = NameStr(((Form_pg_ts_template) GETSTRUCT(tup))->tmplname); + cmd->schemaname = get_namespace_name(namespaceOid); + + ExecBeforeCommandTriggers(cmd); + } + namestrcpy(&(((Form_pg_ts_template) GETSTRUCT(tup))->tmplname), newname); simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); heap_close(rel, NoLock); heap_freetuple(tup); + + /* Call AFTER ALTER TEXT SEARCH TEMPLATE triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectname = (char *)newname; + ExecAfterCommandTriggers(cmd); + } } /* * ALTER TEXT SEARCH TEMPLATE any_name SET SCHEMA name */ void ! AlterTSTemplateNamespace(List *name, const char *newschema, CommandContext cmd) { Oid tmplId, nspOid; *************** *** 1108,1114 **** AlterTSTemplateNamespace(List *name, const char *newschema) tmplId, nspOid, Anum_pg_ts_template_tmplname, Anum_pg_ts_template_tmplnamespace, ! -1, -1); heap_close(rel, RowExclusiveLock); } --- 1249,1255 ---- tmplId, nspOid, Anum_pg_ts_template_tmplname, Anum_pg_ts_template_tmplnamespace, ! -1, -1, cmd); heap_close(rel, RowExclusiveLock); } *************** *** 1126,1132 **** AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid) tmplId, newNspOid, Anum_pg_ts_template_tmplname, Anum_pg_ts_template_tmplnamespace, ! -1, -1); heap_close(rel, RowExclusiveLock); --- 1267,1273 ---- tmplId, newNspOid, Anum_pg_ts_template_tmplname, Anum_pg_ts_template_tmplnamespace, ! -1, -1, NULL); heap_close(rel, RowExclusiveLock); *************** *** 1274,1280 **** makeConfigurationDependencies(HeapTuple tuple, bool removeOld, * CREATE TEXT SEARCH CONFIGURATION */ void ! DefineTSConfiguration(List *names, List *parameters) { Relation cfgRel; Relation mapRel = NULL; --- 1415,1421 ---- * CREATE TEXT SEARCH CONFIGURATION */ void ! DefineTSConfiguration(List *names, List *parameters, CommandContext cmd) { Relation cfgRel; Relation mapRel = NULL; *************** *** 1351,1356 **** DefineTSConfiguration(List *names, List *parameters) --- 1492,1509 ---- errmsg("text search parser is required"))); /* + * Call BEFORE CREATE TEXT SEARCH CONFIGURATION triggers + */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = InvalidOid; + cmd->objectname = NameStr(cname); + cmd->schemaname = get_namespace_name(namespaceoid); + + ExecBeforeCommandTriggers(cmd); + } + + /* * Looks good, build tuple and insert */ memset(values, 0, sizeof(values)); *************** *** 1426,1438 **** DefineTSConfiguration(List *names, List *parameters) if (mapRel) heap_close(mapRel, RowExclusiveLock); heap_close(cfgRel, RowExclusiveLock); } /* * ALTER TEXT SEARCH CONFIGURATION RENAME */ void ! RenameTSConfiguration(List *oldname, const char *newname) { HeapTuple tup; Relation rel; --- 1579,1598 ---- if (mapRel) heap_close(mapRel, RowExclusiveLock); heap_close(cfgRel, RowExclusiveLock); + + /* Call AFTER CREATE TEXT SEARCH PARSER triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectId = cfgOid; + ExecAfterCommandTriggers(cmd); + } } /* * ALTER TEXT SEARCH CONFIGURATION RENAME */ void ! RenameTSConfiguration(List *oldname, const char *newname, CommandContext cmd) { HeapTuple tup; Relation rel; *************** *** 1470,1488 **** RenameTSConfiguration(List *oldname, const char *newname) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); namestrcpy(&(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname), newname); simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); heap_close(rel, NoLock); heap_freetuple(tup); } /* * ALTER TEXT SEARCH CONFIGURATION any_name SET SCHEMA name */ void ! AlterTSConfigurationNamespace(List *name, const char *newschema) { Oid cfgId, nspOid; --- 1630,1665 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* Call BEFORE ALTER TEXT SEARCH CONFIGURATION triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = cfgId; + cmd->objectname = NameStr(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname); + cmd->schemaname = get_namespace_name(namespaceOid); + + ExecBeforeCommandTriggers(cmd); + } + namestrcpy(&(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname), newname); simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); heap_close(rel, NoLock); heap_freetuple(tup); + + /* Call AFTER ALTER TEXT SEARCH CONFIGURATION triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectname = (char *)newname; + ExecAfterCommandTriggers(cmd); + } } /* * ALTER TEXT SEARCH CONFIGURATION any_name SET SCHEMA name */ void ! AlterTSConfigurationNamespace(List *name, const char *newschema, CommandContext cmd) { Oid cfgId, nspOid; *************** *** 1500,1506 **** AlterTSConfigurationNamespace(List *name, const char *newschema) Anum_pg_ts_config_cfgname, Anum_pg_ts_config_cfgnamespace, Anum_pg_ts_config_cfgowner, ! ACL_KIND_TSCONFIGURATION); heap_close(rel, RowExclusiveLock); } --- 1677,1683 ---- Anum_pg_ts_config_cfgname, Anum_pg_ts_config_cfgnamespace, Anum_pg_ts_config_cfgowner, ! ACL_KIND_TSCONFIGURATION, cmd); heap_close(rel, RowExclusiveLock); } *************** *** 1519,1525 **** AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid) Anum_pg_ts_config_cfgname, Anum_pg_ts_config_cfgnamespace, Anum_pg_ts_config_cfgowner, ! ACL_KIND_TSCONFIGURATION); heap_close(rel, RowExclusiveLock); --- 1696,1702 ---- Anum_pg_ts_config_cfgname, Anum_pg_ts_config_cfgnamespace, Anum_pg_ts_config_cfgowner, ! ACL_KIND_TSCONFIGURATION, NULL); heap_close(rel, RowExclusiveLock); *************** *** 1578,1584 **** RemoveTSConfigurationById(Oid cfgId) * ALTER TEXT SEARCH CONFIGURATION OWNER */ void ! AlterTSConfigurationOwner(List *name, Oid newOwnerId) { HeapTuple tup; Relation rel; --- 1755,1761 ---- * ALTER TEXT SEARCH CONFIGURATION OWNER */ void ! AlterTSConfigurationOwner(List *name, Oid newOwnerId, CommandContext cmd) { HeapTuple tup; Relation rel; *************** *** 1620,1625 **** AlterTSConfigurationOwner(List *name, Oid newOwnerId) --- 1797,1812 ---- get_namespace_name(namespaceOid)); } + /* Call BEFORE ALTER TEXT SEARCH CONFIGURATION triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = cfgId; + cmd->objectname = NameStr(form->cfgname); + cmd->schemaname = get_namespace_name(namespaceOid); + + ExecBeforeCommandTriggers(cmd); + } + form->cfgowner = newOwnerId; simple_heap_update(rel, &tup->t_self, tup); *************** *** 1628,1635 **** AlterTSConfigurationOwner(List *name, Oid newOwnerId) /* Update owner dependency reference */ changeDependencyOnOwner(TSConfigRelationId, HeapTupleGetOid(tup), newOwnerId); - } heap_close(rel, NoLock); heap_freetuple(tup); } --- 1815,1825 ---- /* Update owner dependency reference */ changeDependencyOnOwner(TSConfigRelationId, HeapTupleGetOid(tup), newOwnerId); + /* Call AFTER ALTER TEXT SEARCH CONFIGURATION triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); + } heap_close(rel, NoLock); heap_freetuple(tup); } *************** *** 1642,1647 **** AlterTSConfiguration(AlterTSConfigurationStmt *stmt) --- 1832,1838 ---- { HeapTuple tup; Relation relMap; + CommandContextData cmd; /* Find the configuration */ tup = GetTSConfigTuple(stmt->cfgname); *************** *** 1656,1661 **** AlterTSConfiguration(AlterTSConfigurationStmt *stmt) --- 1847,1866 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, NameListToString(stmt->cfgname)); + /* Call BEFORE ALTER TEXT SEARCH CONFIGURATION command triggers */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + Form_pg_ts_config form = (Form_pg_ts_config) GETSTRUCT(tup); + + cmd.objectId = HeapTupleGetOid(tup); + cmd.objectname = NameStr(form->cfgname); + cmd.schemaname = get_namespace_name(form->cfgnamespace); + + ExecBeforeCommandTriggers(&cmd); + } + relMap = heap_open(TSConfigMapRelationId, RowExclusiveLock); /* Add or drop mappings */ *************** *** 1670,1675 **** AlterTSConfiguration(AlterTSConfigurationStmt *stmt) --- 1875,1884 ---- heap_close(relMap, RowExclusiveLock); ReleaseSysCache(tup); + + /* Call AFTER ALTER TEXT SEARCH CONFIGURATION command triggers */ + if (CommandFiresAfterTriggers(&cmd)) + ExecAfterCommandTriggers(&cmd); } /* *** a/src/backend/commands/typecmds.c --- b/src/backend/commands/typecmds.c *************** *** 49,54 **** --- 49,55 ---- #include "catalog/pg_range.h" #include "catalog/pg_type.h" #include "catalog/pg_type_fn.h" + #include "commands/cmdtrigger.h" #include "commands/defrem.h" #include "commands/tablecmds.h" #include "commands/typecmds.h" *************** *** 62,67 **** --- 63,69 ---- #include "parser/parse_expr.h" #include "parser/parse_func.h" #include "parser/parse_type.h" + #include "tcop/utility.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" *************** *** 111,117 **** static char *domainAddConstraint(Oid domainOid, Oid domainNamespace, * Registers a new base type. */ void ! DefineType(List *names, List *parameters) { char *typeName; Oid typeNamespace; --- 113,119 ---- * Registers a new base type. */ void ! DefineType(List *names, List *parameters, CommandContext cmd) { char *typeName; Oid typeNamespace; *************** *** 542,547 **** DefineType(List *names, List *parameters) --- 544,560 ---- NameListToString(analyzeName)); #endif + /* + * Call BEFORE CREATE TYPE triggers + */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = InvalidOid; + cmd->objectname = (char *)typeName; + cmd->schemaname = get_namespace_name(typeNamespace); + + ExecBeforeCommandTriggers(cmd); + } array_oid = AssignTypeArrayOid(); /* *************** *** 625,630 **** DefineType(List *names, List *parameters) --- 638,650 ---- collation); /* type's collation */ pfree(array_type); + + /* Call AFTER CREATE AGGREGATE triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectId = typoid; + ExecAfterCommandTriggers(cmd); + } } /* *************** *** 706,711 **** DefineDomain(CreateDomainStmt *stmt) --- 726,732 ---- Form_pg_type baseType; int32 basetypeMod; Oid baseColl; + CommandContextData cmd; /* Convert list of names to a name and namespace */ domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname, *************** *** 969,974 **** DefineDomain(CreateDomainStmt *stmt) --- 990,1010 ---- } } + + /* + * Call BEFORE CREATE DOMAIN triggers + */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = InvalidOid; + cmd.objectname = (char *)domainName; + cmd.schemaname = get_namespace_name(domainNamespace); + + ExecBeforeCommandTriggers(&cmd); + } + /* * Have TypeCreate do all the real work. */ *************** *** 1036,1041 **** DefineDomain(CreateDomainStmt *stmt) --- 1072,1084 ---- * Now we can clean up. */ ReleaseSysCache(typeTup); + + /* Call AFTER CREATE DOMAIN triggers */ + if (CommandFiresAfterTriggers(&cmd)) + { + cmd.objectId = domainoid; + ExecAfterCommandTriggers(&cmd); + } } *************** *** 1053,1058 **** DefineEnum(CreateEnumStmt *stmt) --- 1096,1102 ---- AclResult aclresult; Oid old_type_oid; Oid enumArrayOid; + CommandContextData cmd; /* Convert list of names to a name and namespace */ enumNamespace = QualifiedNameGetCreationNamespace(stmt->typeName, *************** *** 1079,1084 **** DefineEnum(CreateEnumStmt *stmt) --- 1123,1142 ---- errmsg("type \"%s\" already exists", enumName))); } + /* + * Call BEFORE CREATE (enum) TYPE triggers + */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = InvalidOid; + cmd.objectname = enumName; + cmd.schemaname = get_namespace_name(enumNamespace); + + ExecBeforeCommandTriggers(&cmd); + } + enumArrayOid = AssignTypeArrayOid(); /* Create the pg_type entry */ *************** *** 1156,1161 **** DefineEnum(CreateEnumStmt *stmt) --- 1214,1226 ---- InvalidOid); /* type's collation */ pfree(enumArrayName); + + /* Call AFTER CREATE (enum) TYPE triggers */ + if (CommandFiresAfterTriggers(&cmd)) + { + cmd.objectId = enumTypeOid; + ExecAfterCommandTriggers(&cmd); + } } /* *************** *** 1240,1245 **** DefineRange(CreateRangeStmt *stmt) --- 1305,1311 ---- char alignment; AclResult aclresult; ListCell *lc; + CommandContextData cmd; /* Convert list of names to a name and namespace */ typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName, *************** *** 1387,1392 **** DefineRange(CreateRangeStmt *stmt) --- 1453,1472 ---- /* alignment must be 'i' or 'd' for ranges */ alignment = (subtypalign == 'd') ? 'd' : 'i'; + /* + * Call BEFORE CREATE EXTENSION triggers + */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = InvalidOid; + cmd.objectname = typeName; + cmd.schemaname = get_namespace_name(typeNamespace); + + ExecBeforeCommandTriggers(&cmd); + } + /* Allocate OID for array type */ rangeArrayOid = AssignTypeArrayOid(); *************** *** 1469,1474 **** DefineRange(CreateRangeStmt *stmt) --- 1549,1561 ---- /* And create the constructor functions for this range type */ makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype); + + /* Call AFTER CREATE (range) TYPE triggers */ + if (CommandFiresAfterTriggers(&cmd)) + { + cmd.objectId = typoid; + ExecAfterCommandTriggers(&cmd); + } } /* *************** *** 1980,1986 **** AssignTypeArrayOid(void) *------------------------------------------------------------------- */ Oid ! DefineCompositeType(const RangeVar *typevar, List *coldeflist) { CreateStmt *createStmt = makeNode(CreateStmt); Oid old_type_oid; --- 2067,2074 ---- *------------------------------------------------------------------- */ Oid ! DefineCompositeType(const RangeVar *typevar, List *coldeflist, ! CommandContext cmd) { CreateStmt *createStmt = makeNode(CreateStmt); Oid old_type_oid; *************** *** 2022,2031 **** DefineCompositeType(const RangeVar *typevar, List *coldeflist) --- 2110,2138 ---- } /* + * Call BEFORE CREATE (composite) TYPE triggers + */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = InvalidOid; + cmd->objectname = createStmt->relation->relname; + cmd->schemaname = get_namespace_name(typeNamespace); + + ExecBeforeCommandTriggers(cmd); + } + + /* * Finally create the relation. This also creates the type. */ relid = DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid); Assert(relid != InvalidOid); + + /* Call AFTER CREATE (composite) TYPE triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectId = relid; + ExecAfterCommandTriggers(cmd); + } return relid; } *************** *** 2035,2041 **** DefineCompositeType(const RangeVar *typevar, List *coldeflist) * Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements. */ void ! AlterDomainDefault(List *names, Node *defaultRaw) { TypeName *typename; Oid domainoid; --- 2142,2148 ---- * Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements. */ void ! AlterDomainDefault(List *names, Node *defaultRaw, CommandContext cmd) { TypeName *typename; Oid domainoid; *************** *** 2065,2070 **** AlterDomainDefault(List *names, Node *defaultRaw) --- 2172,2187 ---- /* Check it's a domain and check user has permission for ALTER DOMAIN */ checkDomainOwner(tup); + /* Call BEFORE ALTER DOMAIN triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = NameStr(typTup->typname); + cmd->schemaname = get_namespace_name(typTup->typnamespace); + + ExecBeforeCommandTriggers(cmd); + } + /* Setup new tuple */ MemSet(new_record, (Datum) 0, sizeof(new_record)); MemSet(new_record_nulls, false, sizeof(new_record_nulls)); *************** *** 2161,2166 **** AlterDomainDefault(List *names, Node *defaultRaw) --- 2278,2287 ---- /* Clean up */ heap_close(rel, NoLock); heap_freetuple(newtuple); + + /* Call AFTER ALTER DOMAIN triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); } /* *************** *** 2169,2175 **** AlterDomainDefault(List *names, Node *defaultRaw) * Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements. */ void ! AlterDomainNotNull(List *names, bool notNull) { TypeName *typename; Oid domainoid; --- 2290,2296 ---- * Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements. */ void ! AlterDomainNotNull(List *names, bool notNull, CommandContext cmd) { TypeName *typename; Oid domainoid; *************** *** 2192,2197 **** AlterDomainNotNull(List *names, bool notNull) --- 2313,2328 ---- /* Check it's a domain and check user has permission for ALTER DOMAIN */ checkDomainOwner(tup); + /* Call BEFORE ALTER DOMAIN triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = NameStr(typTup->typname); + cmd->schemaname = get_namespace_name(typTup->typnamespace); + + ExecBeforeCommandTriggers(cmd); + } + /* Is the domain already set to the desired constraint? */ if (typTup->typnotnull == notNull) { *************** *** 2257,2262 **** AlterDomainNotNull(List *names, bool notNull) --- 2388,2397 ---- /* Clean up */ heap_freetuple(tup); heap_close(typrel, RowExclusiveLock); + + /* Call AFTER ALTER DOMAIN triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); } /* *************** *** 2266,2272 **** AlterDomainNotNull(List *names, bool notNull) */ void AlterDomainDropConstraint(List *names, const char *constrName, ! DropBehavior behavior, bool missing_ok) { TypeName *typename; Oid domainoid; --- 2401,2407 ---- */ void AlterDomainDropConstraint(List *names, const char *constrName, ! DropBehavior behavior, bool missing_ok, CommandContext cmd) { TypeName *typename; Oid domainoid; *************** *** 2292,2297 **** AlterDomainDropConstraint(List *names, const char *constrName, --- 2427,2442 ---- /* Check it's a domain and check user has permission for ALTER DOMAIN */ checkDomainOwner(tup); + /* Call BEFORE ALTER DOMAIN triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = NameStr(((Form_pg_type)tup)->typname); + cmd->schemaname = get_namespace_name(((Form_pg_type)tup)->typnamespace); + + ExecBeforeCommandTriggers(cmd); + } + /* Grab an appropriate lock on the pg_constraint relation */ conrel = heap_open(ConstraintRelationId, RowExclusiveLock); *************** *** 2341,2346 **** AlterDomainDropConstraint(List *names, const char *constrName, --- 2486,2495 ---- (errmsg("constraint \"%s\" of domain \"%s\" does not exist, skipping", constrName, TypeNameToString(typename)))); } + + /* Call AFTER ALTER DOMAIN triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); } /* *************** *** 2349,2355 **** AlterDomainDropConstraint(List *names, const char *constrName, * Implements the ALTER DOMAIN .. ADD CONSTRAINT statement. */ void ! AlterDomainAddConstraint(List *names, Node *newConstraint) { TypeName *typename; Oid domainoid; --- 2498,2504 ---- * Implements the ALTER DOMAIN .. ADD CONSTRAINT statement. */ void ! AlterDomainAddConstraint(List *names, Node *newConstraint, CommandContext cmd) { TypeName *typename; Oid domainoid; *************** *** 2378,2383 **** AlterDomainAddConstraint(List *names, Node *newConstraint) --- 2527,2542 ---- elog(ERROR, "unrecognized node type: %d", (int) nodeTag(newConstraint)); + /* Call BEFORE ALTER DOMAIN triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = NameStr(typTup->typname); + cmd->schemaname = get_namespace_name(typTup->typnamespace); + + ExecBeforeCommandTriggers(cmd); + } + constr = (Constraint *) newConstraint; switch (constr->contype) *************** *** 2444,2449 **** AlterDomainAddConstraint(List *names, Node *newConstraint) --- 2603,2612 ---- /* Clean up */ heap_close(typrel, RowExclusiveLock); + + /* Call AFTER ALTER DOMAIN triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); } /* *************** *** 2452,2458 **** AlterDomainAddConstraint(List *names, Node *newConstraint) * Implements the ALTER DOMAIN .. VALIDATE CONSTRAINT statement. */ void ! AlterDomainValidateConstraint(List *names, char *constrName) { TypeName *typename; Oid domainoid; --- 2615,2621 ---- * Implements the ALTER DOMAIN .. VALIDATE CONSTRAINT statement. */ void ! AlterDomainValidateConstraint(List *names, char *constrName, CommandContext cmd) { TypeName *typename; Oid domainoid; *************** *** 2525,2530 **** AlterDomainValidateConstraint(List *names, char *constrName) --- 2688,2703 ---- HeapTupleGetOid(tuple)); conbin = TextDatumGetCString(val); + /* Call BEFORE ALTER DOMAIN triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = NameStr(((Form_pg_type)tup)->typname); + cmd->schemaname = get_namespace_name(((Form_pg_type)tup)->typnamespace); + + ExecBeforeCommandTriggers(cmd); + } + validateDomainConstraint(domainoid, conbin); /* *************** *** 2543,2548 **** AlterDomainValidateConstraint(List *names, char *constrName) --- 2716,2725 ---- heap_close(conrel, RowExclusiveLock); ReleaseSysCache(tup); + + /* Call AFTER ALTER DOMAIN triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); } static void *************** *** 3091,3097 **** GetDomainConstraints(Oid typeOid) * Execute ALTER TYPE RENAME */ void ! RenameType(RenameStmt *stmt) { List *names = stmt->object; const char *newTypeName = stmt->newname; --- 3268,3274 ---- * Execute ALTER TYPE RENAME */ void ! RenameType(RenameStmt *stmt, CommandContext cmd) { List *names = stmt->object; const char *newTypeName = stmt->newname; *************** *** 3153,3162 **** RenameType(RenameStmt *stmt) * RenameRelationInternal will call RenameTypeInternal automatically. */ if (typTup->typtype == TYPTYPE_COMPOSITE) ! RenameRelationInternal(typTup->typrelid, newTypeName); else RenameTypeInternal(typeOid, newTypeName, ! typTup->typnamespace); /* Clean up */ heap_close(rel, RowExclusiveLock); --- 3330,3339 ---- * RenameRelationInternal will call RenameTypeInternal automatically. */ if (typTup->typtype == TYPTYPE_COMPOSITE) ! RenameRelationInternal(typTup->typrelid, newTypeName, cmd); else RenameTypeInternal(typeOid, newTypeName, ! typTup->typnamespace, cmd); /* Clean up */ heap_close(rel, RowExclusiveLock); *************** *** 3166,3172 **** RenameType(RenameStmt *stmt) * Change the owner of a type. */ void ! AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype) { TypeName *typename; Oid typeOid; --- 3343,3350 ---- * Change the owner of a type. */ void ! AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype, ! CommandContext cmd) { TypeName *typename; Oid typeOid; *************** *** 3252,3257 **** AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype) --- 3430,3445 ---- get_namespace_name(typTup->typnamespace)); } + /* Call BEFORE ALTER TYPE triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = typeOid; + cmd->objectname = (char *)typename; + cmd->schemaname = get_namespace_name(typTup->typnamespace); + + ExecBeforeCommandTriggers(cmd); + } + /* * If it's a composite type, invoke ATExecChangeOwner so that we fix * up the pg_class entry properly. That will call back to *************** *** 3279,3284 **** AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype) --- 3467,3476 ---- if (OidIsValid(typTup->typarray)) AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false); } + + /* Call AFTER ALTER TYPE triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); } /* Clean up */ *************** *** 3336,3342 **** AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId, * Execute ALTER TYPE SET SCHEMA */ void ! AlterTypeNamespace(List *names, const char *newschema, ObjectType objecttype) { TypeName *typename; Oid typeOid; --- 3528,3535 ---- * Execute ALTER TYPE SET SCHEMA */ void ! AlterTypeNamespace(List *names, const char *newschema, ObjectType objecttype, ! CommandContext cmd) { TypeName *typename; Oid typeOid; *************** *** 3356,3366 **** AlterTypeNamespace(List *names, const char *newschema, ObjectType objecttype) /* get schema OID and check its permissions */ nspOid = LookupCreationNamespace(newschema); ! AlterTypeNamespace_oid(typeOid, nspOid); } Oid ! AlterTypeNamespace_oid(Oid typeOid, Oid nspOid) { Oid elemOid; --- 3549,3559 ---- /* get schema OID and check its permissions */ nspOid = LookupCreationNamespace(newschema); ! AlterTypeNamespace_oid(typeOid, nspOid, cmd); } Oid ! AlterTypeNamespace_oid(Oid typeOid, Oid nspOid, CommandContext cmd) { Oid elemOid; *************** *** 3380,3386 **** AlterTypeNamespace_oid(Oid typeOid, Oid nspOid) format_type_be(elemOid)))); /* and do the work */ ! return AlterTypeNamespaceInternal(typeOid, nspOid, false, true); } /* --- 3573,3579 ---- format_type_be(elemOid)))); /* and do the work */ ! return AlterTypeNamespaceInternal(typeOid, nspOid, false, true, cmd); } /* *************** *** 3401,3407 **** AlterTypeNamespace_oid(Oid typeOid, Oid nspOid) Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, ! bool errorOnTableType) { Relation rel; HeapTuple tup; --- 3594,3601 ---- Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, ! bool errorOnTableType, ! CommandContext cmd) { Relation rel; HeapTuple tup; *************** *** 3447,3452 **** AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, --- 3641,3656 ---- format_type_be(typeOid)), errhint("Use ALTER TABLE instead."))); + /* Call BEFORE ALTER TYPE triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = typeOid; + cmd->objectname = NameStr(typform->typname); + cmd->schemaname = get_namespace_name(oldNspOid); + + ExecBeforeCommandTriggers(cmd); + } + /* OK, modify the pg_type row */ /* tup is a copy, so we can scribble directly on it */ *************** *** 3504,3510 **** AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, /* Recursively alter the associated array type, if any */ if (OidIsValid(arrayOid)) ! AlterTypeNamespaceInternal(arrayOid, nspOid, true, true); return oldNspOid; } --- 3708,3719 ---- /* Recursively alter the associated array type, if any */ if (OidIsValid(arrayOid)) ! AlterTypeNamespaceInternal(arrayOid, nspOid, true, true, NULL); + if (CommandFiresAfterTriggers(cmd)) + { + cmd->schemaname = get_namespace_name(nspOid); + ExecAfterCommandTriggers(cmd); + } return oldNspOid; } *** a/src/backend/commands/vacuum.c --- b/src/backend/commands/vacuum.c *************** *** 30,35 **** --- 30,36 ---- #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_namespace.h" + #include "commands/cmdtrigger.h" #include "commands/cluster.h" #include "commands/vacuum.h" #include "miscadmin.h" *************** *** 122,127 **** vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast, --- 123,144 ---- else in_outer_xact = IsInTransactionChain(isTopLevel); + /* CAll BEFORE VACUUM command triggers */ + if (!IsAutoVacuumWorkerProcess() && vacstmt->relation != NULL) + { + CommandContextData cmd; + InitCommandContext(&cmd, (Node *)vacstmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = relid; + cmd.objectname = vacstmt->relation->relname; + cmd.schemaname = vacstmt->relation->schemaname; + + ExecBeforeCommandTriggers(&cmd); + } + } + /* * Send info about dead objects to the statistics collector, unless we are * in autovacuum --- autovacuum.c does this for itself. *************** *** 1054,1060 **** vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound) /* VACUUM FULL is now a variant of CLUSTER; see cluster.c */ cluster_rel(relid, InvalidOid, false, (vacstmt->options & VACOPT_VERBOSE) != 0, ! vacstmt->freeze_min_age, vacstmt->freeze_table_age); } else lazy_vacuum_rel(onerel, vacstmt, vac_strategy); --- 1071,1077 ---- /* VACUUM FULL is now a variant of CLUSTER; see cluster.c */ cluster_rel(relid, InvalidOid, false, (vacstmt->options & VACOPT_VERBOSE) != 0, ! vacstmt->freeze_min_age, vacstmt->freeze_table_age, NULL); } else lazy_vacuum_rel(onerel, vacstmt, vac_strategy); *** a/src/backend/commands/view.c --- b/src/backend/commands/view.c *************** *** 28,33 **** --- 28,34 ---- #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" #include "rewrite/rewriteSupport.h" + #include "tcop/utility.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" *************** *** 99,105 **** isViewOnTempTable_walker(Node *node, void *context) */ static Oid DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace, ! List *options) { Oid viewOid; LOCKMODE lockmode; --- 100,106 ---- */ static Oid DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace, ! List *options, CommandContext cmd) { Oid viewOid; LOCKMODE lockmode; *************** *** 168,173 **** DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace, --- 169,186 ---- lockmode = replace ? AccessExclusiveLock : NoLock; (void) RangeVarGetAndCheckCreationNamespace(relation, lockmode, &viewOid); + /* + * Call BEFORE CREATE VIEW triggers + */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = InvalidOid; + cmd->objectname = relation->relname; + cmd->schemaname = relation->schemaname; + + ExecBeforeCommandTriggers(cmd); + } + if (OidIsValid(viewOid) && replace) { Relation rel; *************** *** 343,349 **** DefineViewRules(Oid viewOid, Query *viewParse, bool replace) CMD_SELECT, true, replace, ! list_make1(viewParse)); /* * Someday: automatic ON INSERT, etc --- 356,363 ---- CMD_SELECT, true, replace, ! list_make1(viewParse), ! NULL); /* * Someday: automatic ON INSERT, etc *************** *** 426,431 **** DefineView(ViewStmt *stmt, const char *queryString) --- 440,446 ---- Query *viewParse; Oid viewOid; RangeVar *view; + CommandContextData cmd; /* * Run parse analysis to convert the raw parse tree to a Query. Note this *************** *** 511,523 **** DefineView(ViewStmt *stmt, const char *queryString) } /* * Create the view relation * * NOTE: if it already exists and replace is false, the xact will be * aborted. */ viewOid = DefineVirtualRelation(view, viewParse->targetList, ! stmt->replace, stmt->options); /* * The relation we have just created is not visible to any other commands --- 526,546 ---- } /* + * Prepare BEFORE CREATE VIEW triggers + */ + InitCommandContext(&cmd, (Node *)stmt, false); + + /* * Create the view relation * * NOTE: if it already exists and replace is false, the xact will be * aborted. */ viewOid = DefineVirtualRelation(view, viewParse->targetList, ! stmt->replace, stmt->options, &cmd); ! ! if (!OidIsValid(viewOid)) ! return; /* * The relation we have just created is not visible to any other commands *************** *** 536,539 **** DefineView(ViewStmt *stmt, const char *queryString) --- 559,569 ---- * Now create the rules associated with the view. */ DefineViewRules(viewOid, viewParse, stmt->replace); + + /* Call AFTER CREATE VIEW triggers */ + if (CommandFiresAfterTriggers(&cmd)) + { + cmd.objectId = viewOid; + ExecAfterCommandTriggers(&cmd); + } } *** a/src/backend/nodes/copyfuncs.c --- b/src/backend/nodes/copyfuncs.c *************** *** 3457,3462 **** _copyCreateTrigStmt(const CreateTrigStmt *from) --- 3457,3487 ---- return newnode; } + static CreateCmdTrigStmt * + _copyCreateCmdTrigStmt(const CreateCmdTrigStmt *from) + { + CreateCmdTrigStmt *newnode = makeNode(CreateCmdTrigStmt); + + COPY_NODE_FIELD(command); + COPY_STRING_FIELD(trigname); + COPY_SCALAR_FIELD(timing); + COPY_NODE_FIELD(funcname); + + return newnode; + } + + static AlterCmdTrigStmt * + _copyAlterCmdTrigStmt(const AlterCmdTrigStmt *from) + { + AlterCmdTrigStmt *newnode = makeNode(AlterCmdTrigStmt); + + COPY_STRING_FIELD(command); + COPY_STRING_FIELD(trigname); + COPY_STRING_FIELD(tgenabled); + + return newnode; + } + static CreatePLangStmt * _copyCreatePLangStmt(const CreatePLangStmt *from) { *************** *** 3686,3692 **** _copyAlterTSConfigurationStmt(const AlterTSConfigurationStmt *from) /* * Perform a deep copy of the specified list, using copyObject(). The * list MUST be of type T_List; T_IntList and T_OidList nodes don't ! * need deep copies, so they should be copied via list_copy() */ #define COPY_NODE_CELL(new, old) \ (new) = (ListCell *) palloc(sizeof(ListCell)); \ --- 3711,3717 ---- /* * Perform a deep copy of the specified list, using copyObject(). The * list MUST be of type T_List; T_IntList and T_OidList nodes don't ! * need deep copies, so they should be copied via list_copy(const ) */ #define COPY_NODE_CELL(new, old) \ (new) = (ListCell *) palloc(sizeof(ListCell)); \ *************** *** 4309,4314 **** copyObject(const void *from) --- 4334,4345 ---- case T_CreateTrigStmt: retval = _copyCreateTrigStmt(from); break; + case T_CreateCmdTrigStmt: + retval = _copyCreateCmdTrigStmt(from); + break; + case T_AlterCmdTrigStmt: + retval = _copyAlterCmdTrigStmt(from); + break; case T_CreatePLangStmt: retval = _copyCreatePLangStmt(from); break; *** a/src/backend/nodes/equalfuncs.c --- b/src/backend/nodes/equalfuncs.c *************** *** 1778,1783 **** _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b) --- 1778,1804 ---- } static bool + _equalCreateCmdTrigStmt(const CreateCmdTrigStmt *a, const CreateCmdTrigStmt *b) + { + COMPARE_NODE_FIELD(command); + COMPARE_STRING_FIELD(trigname); + COMPARE_SCALAR_FIELD(timing); + COMPARE_NODE_FIELD(funcname); + + return true; + } + + static bool + _equalAlterCmdTrigStmt(const AlterCmdTrigStmt *a, const AlterCmdTrigStmt *b) + { + COMPARE_STRING_FIELD(command); + COMPARE_STRING_FIELD(trigname); + COMPARE_STRING_FIELD(tgenabled); + + return true; + } + + static bool _equalCreatePLangStmt(const CreatePLangStmt *a, const CreatePLangStmt *b) { COMPARE_SCALAR_FIELD(replace); *************** *** 2852,2857 **** equal(const void *a, const void *b) --- 2873,2884 ---- case T_CreateTrigStmt: retval = _equalCreateTrigStmt(a, b); break; + case T_CreateCmdTrigStmt: + retval = _equalCreateCmdTrigStmt(a, b); + break; + case T_AlterCmdTrigStmt: + retval = _equalAlterCmdTrigStmt(a, b); + break; case T_CreatePLangStmt: retval = _equalCreatePLangStmt(a, b); break; *** a/src/backend/nodes/readfuncs.c --- b/src/backend/nodes/readfuncs.c *************** *** 255,260 **** _readDeclareCursorStmt(void) --- 255,276 ---- } /* + * _readCreateCmdTrigStmt + */ + static CreateCmdTrigStmt * + _readCreateCmdTrigStmt(void) + { + READ_LOCALS(CreateCmdTrigStmt); + + READ_NODE_FIELD(command); + READ_STRING_FIELD(trigname); + READ_CHAR_FIELD(timing); + READ_NODE_FIELD(funcname); + + READ_DONE(); + } + + /* * _readSortGroupClause */ static SortGroupClause * *************** *** 1255,1260 **** parseNodeString(void) --- 1271,1278 ---- if (MATCH("QUERY", 5)) return_value = _readQuery(); + else if (MATCH("CREATECMDTRIGSTMT", 17)) + return_value = _readCreateCmdTrigStmt(); else if (MATCH("SORTGROUPCLAUSE", 15)) return_value = _readSortGroupClause(); else if (MATCH("WINDOWCLAUSE", 12)) *** a/src/backend/parser/gram.y --- b/src/backend/parser/gram.y *************** *** 53,58 **** --- 53,59 ---- #include "catalog/index.h" #include "catalog/namespace.h" + #include "catalog/pg_cmdtrigger.h" #include "catalog/pg_trigger.h" #include "commands/defrem.h" #include "nodes/makefuncs.h" *************** *** 195,200 **** static void processCASbits(int cas_bits, int location, const char *constrType, --- 196,202 ---- } %type stmt schema_stmt + AlterCmdTrigStmt AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt AlterFdwStmt AlterForeignServerStmt AlterGroupStmt AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt *************** *** 208,219 **** static void processCASbits(int cas_bits, int location, const char *constrType, CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt ! CreateAssertStmt CreateTrigStmt CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt ! DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt ! DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt --- 210,221 ---- CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt ! CreateAssertStmt CreateTrigStmt CreateCmdTrigStmt CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt ! DropAssertStmt DropTrigStmt DropCmdTrigStmt DropRuleStmt DropCastStmt ! DropRoleStmt DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt *************** *** 264,273 **** static void processCASbits(int cas_bits, int location, const char *constrType, %type OptSchemaEltList %type TriggerForSpec TriggerForType ! %type TriggerActionTime %type TriggerEvents TriggerOneEvent %type TriggerFuncArg %type TriggerWhen %type copy_file_name database_name access_method_clause access_method attr_name --- 266,277 ---- %type OptSchemaEltList %type TriggerForSpec TriggerForType ! %type TriggerActionTime CmdTriggerActionTime %type TriggerEvents TriggerOneEvent %type TriggerFuncArg %type TriggerWhen + %type trigger_command enable_trigger + %type trigger_command_list %type copy_file_name database_name access_method_clause access_method attr_name *************** *** 495,501 **** static void processCASbits(int cas_bits, int location, const char *constrType, CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE ! CLUSTER COALESCE COLLATE COLLATION COLUMN COMMENT COMMENTS COMMIT COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CROSS CSV CURRENT_P --- 499,505 ---- CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE ! CLUSTER COALESCE COLLATE COLLATION COLUMN COMMAND COMMENT COMMENTS COMMIT COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CROSS CSV CURRENT_P *************** *** 675,681 **** stmtmulti: stmtmulti ';' stmt ; stmt : ! AlterDatabaseStmt | AlterDatabaseSetStmt | AlterDefaultPrivilegesStmt | AlterDomainStmt --- 679,686 ---- ; stmt : ! AlterCmdTrigStmt ! | AlterDatabaseStmt | AlterDatabaseSetStmt | AlterDefaultPrivilegesStmt | AlterDomainStmt *************** *** 726,731 **** stmt : --- 731,737 ---- | CreateStmt | CreateTableSpaceStmt | CreateTrigStmt + | CreateCmdTrigStmt | CreateRoleStmt | CreateUserStmt | CreateUserMappingStmt *************** *** 749,754 **** stmt : --- 755,761 ---- | DropStmt | DropTableSpaceStmt | DropTrigStmt + | DropCmdTrigStmt | DropRoleStmt | DropUserStmt | DropUserMappingStmt *************** *** 4260,4265 **** DropTrigStmt: --- 4267,4477 ---- /***************************************************************************** * * QUERIES : + * CREATE COMMAND TRIGGER ... BEFORE|AFTER COMMAND ... + * DROP COMMAND TRIGGER ... BEFORE|AFTER ... + * + *****************************************************************************/ + + CreateCmdTrigStmt: + CREATE COMMAND TRIGGER name CmdTriggerActionTime trigger_command_list + EXECUTE PROCEDURE func_name '(' ')' + { + CreateCmdTrigStmt *n = makeNode(CreateCmdTrigStmt); + n->trigname = $4; + n->timing = $5; + n->command = $6; + n->funcname = $9; + $$ = (Node *)n; + } + | CREATE COMMAND TRIGGER name CmdTriggerActionTime ANY COMMAND + EXECUTE PROCEDURE func_name '(' ')' + { + CreateCmdTrigStmt *n = makeNode(CreateCmdTrigStmt); + n->trigname = $4; + n->timing = $5; + n->command = list_make1(makeStringConst("ANY", @5)); + n->funcname = $10; + $$ = (Node *)n; + } + ; + + CmdTriggerActionTime: + BEFORE { $$ = CMD_TRIGGER_FIRED_BEFORE; } + | AFTER { $$ = CMD_TRIGGER_FIRED_AFTER; } + ; + + trigger_command_list: + trigger_command + { + $$ = list_make1(makeStringConst($1, @1)); + } + | trigger_command_list ',' trigger_command + { + $$ = lappend($1, makeStringConst($3, @1)); + } + ; + + + /* + * that will get matched against what CreateCommandTag returns + * + * we don't support Command Triggers on every possible command that PostgreSQL + * supports, this list should match with the implementation. + */ + trigger_command: + CREATE SCHEMA { $$ = "CREATE SCHEMA"; } + | CREATE EXTENSION { $$ = "CREATE EXTENSION"; } + | CREATE LANGUAGE { $$ = "CREATE LANGUAGE"; } + | CREATE FUNCTION { $$ = "CREATE FUNCTION"; } + | CREATE TABLE { $$ = "CREATE TABLE"; } + | CREATE SERVER { $$ = "CREATE SERVER"; } + | CREATE FOREIGN TABLE { $$ = "CREATE FOREIGN TABLE"; } + | CREATE FOREIGN DATA_P WRAPPER { $$ = "CREATE FOREIGN DATA WRAPPER"; } + | CREATE USER MAPPING { $$ = "CREATE USER MAPPING"; } + | CREATE INDEX { $$ = "CREATE INDEX"; } + | CREATE SEQUENCE { $$ = "CREATE SEQUENCE"; } + | CREATE VIEW { $$ = "CREATE VIEW"; } + | CREATE RULE { $$ = "CREATE RULE"; } + | CREATE AGGREGATE { $$ = "CREATE AGGREGATE"; } + | CREATE OPERATOR { $$ = "CREATE OPERATOR"; } + | CREATE COLLATION { $$ = "CREATE COLLATION"; } + | CREATE TEXT_P SEARCH PARSER { $$ = "CREATE TEXT SEARCH PARSER"; } + | CREATE TEXT_P SEARCH DICTIONARY { $$ = "CREATE TEXT SEARCH DICTIONARY"; } + | CREATE TEXT_P SEARCH TEMPLATE { $$ = "CREATE TEXT SEARCH TEMPLATE"; } + | CREATE TEXT_P SEARCH CONFIGURATION { $$ = "CREATE TEXT SEARCH CONFIGURATION"; } + | CREATE TYPE_P { $$ = "CREATE TYPE"; } + | CREATE DOMAIN_P { $$ = "CREATE DOMAIN"; } + | CREATE TRIGGER { $$ = "CREATE TRIGGER"; } + | CREATE CONVERSION_P { $$ = "CREATE CONVERSION"; } + | CREATE CAST { $$ = "CREATE CAST"; } + | CREATE OPERATOR CLASS { $$ = "CREATE OPERATOR CLASS"; } + | CREATE OPERATOR FAMILY { $$ = "CREATE OPERATOR FAMILY"; } + | ALTER SCHEMA { $$ = "ALTER SCHEMA"; } + | ALTER EXTENSION { $$ = "ALTER EXTENSION"; } + | ALTER FUNCTION { $$ = "ALTER FUNCTION"; } + | ALTER TABLE { $$ = "ALTER TABLE"; } + | ALTER SERVER { $$ = "ALTER SERVER"; } + | ALTER FOREIGN TABLE { $$ = "ALTER FOREIGN TABLE"; } + | ALTER FOREIGN DATA_P WRAPPER { $$ = "ALTER FOREIGN DATA WRAPPER"; } + | ALTER USER MAPPING { $$ = "ALTER USER MAPPING"; } + | ALTER AGGREGATE { $$ = "ALTER AGGREGATE"; } + | ALTER OPERATOR { $$ = "ALTER OPERATOR"; } + | ALTER OPERATOR CLASS { $$ = "ALTER OPERATOR CLASS"; } + | ALTER OPERATOR FAMILY { $$ = "ALTER OPERATOR FAMILY"; } + | ALTER COLLATION { $$ = "ALTER COLLATION"; } + | ALTER TEXT_P SEARCH PARSER { $$ = "ALTER TEXT SEARCH PARSER"; } + | ALTER TEXT_P SEARCH DICTIONARY { $$ = "ALTER TEXT SEARCH DICTIONARY"; } + | ALTER TEXT_P SEARCH TEMPLATE { $$ = "ALTER TEXT SEARCH TEMPLATE"; } + | ALTER TEXT_P SEARCH CONFIGURATION { $$ = "ALTER TEXT SEARCH CONFIGURATION"; } + | ALTER TYPE_P { $$ = "ALTER TYPE"; } + | ALTER DOMAIN_P { $$ = "ALTER DOMAIN"; } + | ALTER TRIGGER { $$ = "ALTER TRIGGER"; } + | DROP TABLE { $$ = "DROP TABLE"; } + | DROP SEQUENCE { $$ = "DROP SEQUENCE"; } + | DROP VIEW { $$ = "DROP VIEW"; } + | DROP INDEX { $$ = "DROP INDEX"; } + | DROP TYPE_P { $$ = "DROP TYPE"; } + | DROP DOMAIN_P { $$ = "DROP DOMAIN"; } + | DROP COLLATION { $$ = "DROP COLLATION"; } + | DROP CONVERSION_P { $$ = "DROP CONVERSION_P"; } + | DROP SCHEMA { $$ = "DROP SCHEMA"; } + | DROP EXTENSION { $$ = "DROP EXTENSION"; } + | DROP TEXT_P SEARCH PARSER { $$ = "DROP TEXT SEARCH PARSER"; } + | DROP TEXT_P SEARCH DICTIONARY { $$ = "DROP TEXT SEARCH DICTIONARY"; } + | DROP TEXT_P SEARCH TEMPLATE { $$ = "DROP TEXT SEARCH TEMPLATE"; } + | DROP TEXT_P SEARCH CONFIGURATION { $$ = "DROP TEXT SEARCH CONFIGURATION"; } + | DROP LANGUAGE { $$ = "DROP LANGUAGE"; } + | DROP SERVER { $$ = "DROP SERVER"; } + | DROP FOREIGN TABLE { $$ = "DROP FOREIGN TABLE"; } + | DROP FOREIGN DATA_P WRAPPER { $$ = "DROP FOREIGN DATA WRAPPER"; } + | DROP USER MAPPING { $$ = "DROP USER MAPPING"; } + | DROP TRIGGER { $$ = "DROP TRIGGER"; } + | DROP ASSERTION { $$ = "DROP ASSERTION"; } + | DROP OPERATOR CLASS { $$ = "DROP OPERATOR CLASS"; } + | DROP OPERATOR FAMILY { $$ = "DROP OPERATOR FAMILY"; } + | DROP FUNCTION { $$ = "DROP FUNCTION"; } + | DROP AGGREGATE { $$ = "DROP AGGREGATE"; } + | DROP OPERATOR { $$ = "DROP OPERATOR"; } + | DROP CAST { $$ = "DROP CAST"; } + | DROP RULE { $$ = "DROP RULE"; } + | REINDEX { $$ = "REINDEX"; } + | VACUUM { $$ = "VACUUM"; } + | CLUSTER { $$ = "CLUSTER"; } + | LOAD { $$ = "LOAD"; } + ; + + DropCmdTrigStmt: + DROP COMMAND TRIGGER name ON trigger_command opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_CMDTRIGGER; + n->objects = list_make1(list_make1(makeString($4))); + n->arguments = list_make1(list_make1(makeString($6))); + n->behavior = $7; + n->missing_ok = false; + $$ = (Node *) n; + } + | DROP COMMAND TRIGGER IF_P EXISTS name ON trigger_command opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_CMDTRIGGER; + n->objects = list_make1(list_make1(makeString($6))); + n->arguments = list_make1(list_make1(makeString($8))); + n->behavior = $9; + n->missing_ok = true; + $$ = (Node *) n; + } + | DROP COMMAND TRIGGER name ON ANY COMMAND opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_CMDTRIGGER; + n->objects = list_make1(list_make1(makeString($4))); + n->arguments = list_make1(list_make1(makeString("ANY"))); + n->behavior = $8; + n->missing_ok = false; + $$ = (Node *) n; + } + | DROP COMMAND TRIGGER IF_P EXISTS name ON ANY COMMAND opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_CMDTRIGGER; + n->objects = list_make1(list_make1(makeString($6))); + n->arguments = list_make1(list_make1(makeString("ANY"))); + n->behavior = $10; + n->missing_ok = true; + $$ = (Node *) n; + } + ; + + AlterCmdTrigStmt: + ALTER COMMAND TRIGGER name ON trigger_command SET enable_trigger + { + AlterCmdTrigStmt *n = makeNode(AlterCmdTrigStmt); + n->trigname = $4; + n->command = $6; + n->tgenabled = $8; + $$ = (Node *) n; + } + | ALTER COMMAND TRIGGER name ON ANY COMMAND SET enable_trigger + { + AlterCmdTrigStmt *n = makeNode(AlterCmdTrigStmt); + n->trigname = $4; + n->command = "ANY"; + n->tgenabled = $9; + $$ = (Node *) n; + } + ; + + enable_trigger: + ENABLE_P { $$ = "O"; } + | ENABLE_P REPLICA { $$ = "R"; } + | ENABLE_P ALWAYS { $$ = "A"; } + | DISABLE_P { $$ = "D"; } + ; + + /***************************************************************************** + * + * QUERIES : * CREATE ASSERTION ... * DROP ASSERTION ... * *************** *** 6763,6768 **** RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name --- 6975,6989 ---- n->missing_ok = false; $$ = (Node *)n; } + | ALTER TRIGGER name ON COMMAND trigger_command RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_CMDTRIGGER; + n->object = list_make1(makeString($6)); + n->subname = $3; + n->newname = $9; + $$ = (Node *)n; + } | ALTER ROLE RoleId RENAME TO RoleId { RenameStmt *n = makeNode(RenameStmt); *** a/src/backend/rewrite/rewriteDefine.c --- b/src/backend/rewrite/rewriteDefine.c *************** *** 195,204 **** DefineRule(RuleStmt *stmt, const char *queryString) --- 195,208 ---- List *actions; Node *whereClause; Oid relId; + CommandContextData cmd; /* Parse analysis. */ transformRuleStmt(stmt, queryString, &actions, &whereClause); + /* Prepare command context */ + InitCommandContext(&cmd, (Node *)stmt, false); + /* * Find and lock the relation. Lock level should match * DefineQueryRewrite. *************** *** 212,218 **** DefineRule(RuleStmt *stmt, const char *queryString) stmt->event, stmt->instead, stmt->replace, ! actions); } --- 216,223 ---- stmt->event, stmt->instead, stmt->replace, ! actions, ! &cmd); } *************** *** 230,236 **** DefineQueryRewrite(char *rulename, CmdType event_type, bool is_instead, bool replace, ! List *action) { Relation event_relation; int event_attno; --- 235,242 ---- CmdType event_type, bool is_instead, bool replace, ! List *action, ! CommandContext cmd) { Relation event_relation; int event_attno; *************** *** 480,485 **** DefineQueryRewrite(char *rulename, --- 486,502 ---- } } + /* Call BEFORE CREATE RULE triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = InvalidOid; + cmd->objectname = rulename; + cmd->schemaname = + get_namespace_name(RelationGetNamespace(event_relation)); + + ExecBeforeCommandTriggers(cmd); + } + /* * This rule is allowed - prepare to install it. */ *************** *** 520,525 **** DefineQueryRewrite(char *rulename, --- 537,546 ---- /* Close rel, but keep lock till commit... */ heap_close(event_relation, NoLock); + + /* Call AFTER CREATE RULE triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); } /* *** a/src/backend/tcop/utility.c --- b/src/backend/tcop/utility.c *************** *** 25,30 **** --- 25,31 ---- #include "commands/alter.h" #include "commands/async.h" #include "commands/cluster.h" + #include "commands/cmdtrigger.h" #include "commands/comment.h" #include "commands/collationcmds.h" #include "commands/conversioncmds.h" *************** *** 58,66 **** #include "tcop/utility.h" #include "utils/acl.h" #include "utils/guc.h" #include "utils/syscache.h" - /* Hook for plugins to get control in ProcessUtility() */ ProcessUtility_hook_type ProcessUtility_hook = NULL; --- 59,67 ---- #include "tcop/utility.h" #include "utils/acl.h" #include "utils/guc.h" + #include "utils/lsyscache.h" #include "utils/syscache.h" /* Hook for plugins to get control in ProcessUtility() */ ProcessUtility_hook_type ProcessUtility_hook = NULL; *************** *** 150,155 **** CommandIsReadOnly(Node *parsetree) --- 151,318 ---- } /* + * Support function for calling the command triggers. + */ + static void + call_before_cmdtriggers(CommandContext cmd) + { + switch (nodeTag(cmd->parsetree)) + { + case T_AlterDatabaseStmt: + case T_AlterDatabaseSetStmt: + case T_AlterDomainStmt: + case T_AlterFunctionStmt: + case T_AlterRoleStmt: + case T_AlterRoleSetStmt: + case T_AlterObjectSchemaStmt: + case T_AlterOwnerStmt: + case T_AlterSeqStmt: + case T_AlterTableStmt: + case T_RenameStmt: + case T_CommentStmt: + case T_DefineStmt: + case T_CreateCastStmt: + case T_CreateConversionStmt: + case T_CreatedbStmt: + case T_CreateDomainStmt: + case T_CreateFunctionStmt: + case T_CreateRoleStmt: + case T_IndexStmt: + case T_CreatePLangStmt: + case T_CreateOpClassStmt: + case T_CreateOpFamilyStmt: + case T_AlterOpFamilyStmt: + case T_RuleStmt: + case T_CreateSchemaStmt: + case T_CreateSeqStmt: + case T_CreateStmt: + case T_CreateTableSpaceStmt: + case T_CreateTrigStmt: + case T_CompositeTypeStmt: + case T_CreateEnumStmt: + case T_CreateRangeStmt: + case T_AlterEnumStmt: + case T_ViewStmt: + case T_DropdbStmt: + case T_DropTableSpaceStmt: + case T_DropRoleStmt: + case T_GrantStmt: + case T_GrantRoleStmt: + case T_AlterDefaultPrivilegesStmt: + case T_TruncateStmt: + case T_DropOwnedStmt: + case T_ReassignOwnedStmt: + case T_AlterTSDictionaryStmt: + case T_AlterTSConfigurationStmt: + case T_CreateExtensionStmt: + case T_AlterExtensionStmt: + case T_AlterExtensionContentsStmt: + case T_CreateFdwStmt: + case T_AlterFdwStmt: + case T_CreateForeignServerStmt: + case T_AlterForeignServerStmt: + case T_CreateUserMappingStmt: + case T_AlterUserMappingStmt: + case T_DropUserMappingStmt: + case T_AlterTableSpaceOptionsStmt: + case T_CreateForeignTableStmt: + case T_ClusterStmt: + case T_VacuumStmt: + case T_LoadStmt: + case T_ReindexStmt: + ExecBeforeCommandTriggers(cmd); + + case T_DropStmt: + if (((DropStmt *) cmd->parsetree)->removeType != OBJECT_CMDTRIGGER) + ExecBeforeCommandTriggers(cmd); + + default: + /* commands that don't support triggers */ + return; + } + } + + static void + call_after_cmdtriggers(CommandContext cmd) + { + switch (nodeTag(cmd->parsetree)) + { + case T_AlterDatabaseStmt: + case T_AlterDatabaseSetStmt: + case T_AlterDomainStmt: + case T_AlterFunctionStmt: + case T_AlterRoleStmt: + case T_AlterRoleSetStmt: + case T_AlterObjectSchemaStmt: + case T_AlterOwnerStmt: + case T_AlterSeqStmt: + case T_AlterTableStmt: + case T_RenameStmt: + case T_CommentStmt: + case T_DefineStmt: + case T_CreateCastStmt: + case T_CreateConversionStmt: + case T_CreatedbStmt: + case T_CreateDomainStmt: + case T_CreateFunctionStmt: + case T_CreateRoleStmt: + case T_CreatePLangStmt: + case T_CreateOpClassStmt: + case T_CreateOpFamilyStmt: + case T_AlterOpFamilyStmt: + case T_RuleStmt: + case T_CreateSchemaStmt: + case T_CreateSeqStmt: + case T_CreateStmt: + case T_CreateTableSpaceStmt: + case T_CreateTrigStmt: + case T_CompositeTypeStmt: + case T_CreateEnumStmt: + case T_CreateRangeStmt: + case T_AlterEnumStmt: + case T_ViewStmt: + case T_DropdbStmt: + case T_DropTableSpaceStmt: + case T_DropRoleStmt: + case T_GrantStmt: + case T_GrantRoleStmt: + case T_AlterDefaultPrivilegesStmt: + case T_TruncateStmt: + case T_DropOwnedStmt: + case T_ReassignOwnedStmt: + case T_AlterTSDictionaryStmt: + case T_AlterTSConfigurationStmt: + case T_CreateExtensionStmt: + case T_AlterExtensionStmt: + case T_AlterExtensionContentsStmt: + case T_CreateFdwStmt: + case T_AlterFdwStmt: + case T_CreateForeignServerStmt: + case T_AlterForeignServerStmt: + case T_CreateUserMappingStmt: + case T_AlterUserMappingStmt: + case T_DropUserMappingStmt: + case T_AlterTableSpaceOptionsStmt: + case T_CreateForeignTableStmt: + case T_LoadStmt: + case T_ReindexStmt: + ExecAfterCommandTriggers(cmd); + + case T_IndexStmt: + if (!((IndexStmt *)cmd->parsetree)->concurrent) + ExecAfterCommandTriggers(cmd); + + case T_DropStmt: + if (((DropStmt *) cmd->parsetree)->removeType != OBJECT_CMDTRIGGER) + ExecAfterCommandTriggers(cmd); + + default: + /* commands that don't support triggers */ + return; + } + } + + /* * check_xact_readonly: is a utility command read-only? * * Here we use the loose rules of XactReadOnly mode: no permanent effects *************** *** 184,189 **** check_xact_readonly(Node *parsetree) --- 347,354 ---- case T_CommentStmt: case T_DefineStmt: case T_CreateCastStmt: + case T_CreateCmdTrigStmt: + case T_AlterCmdTrigStmt: case T_CreateConversionStmt: case T_CreatedbStmt: case T_CreateDomainStmt: *************** *** 344,354 **** standard_ProcessUtility(Node *parsetree, --- 509,524 ---- DestReceiver *dest, char *completionTag) { + CommandContextData cmd; check_xact_readonly(parsetree); if (completionTag) completionTag[0] = '\0'; + /* call the BEFORE ANY COMMAND triggers first */ + InitCommandContext(&cmd, parsetree, true); + call_before_cmdtriggers(&cmd); + switch (nodeTag(parsetree)) { /* *************** *** 509,519 **** standard_ProcessUtility(Node *parsetree, { List *stmts; ListCell *l; ! Oid relOid; /* Run parse analysis ... */ ! stmts = transformCreateStmt((CreateStmt *) parsetree, ! queryString); /* ... and do it */ foreach(l, stmts) --- 679,705 ---- { List *stmts; ListCell *l; ! Oid relOid = InvalidOid; ! CreateStmt *stmt = (CreateStmt *) parsetree; ! CommandContextData cmd; ! ! /* ! * Call BEFORE CREATE TABLE triggers ! */ ! InitCommandContext(&cmd, parsetree, false); ! ! if (CommandFiresTriggers(&cmd)) ! { ! cmd.objectId = InvalidOid; ! cmd.objectname = stmt->relation->relname; ! cmd.schemaname = get_namespace_name( ! RangeVarGetCreationNamespace(stmt->relation)); ! ! ExecBeforeCommandTriggers(&cmd); ! } /* Run parse analysis ... */ ! stmts = transformCreateStmt(stmt, queryString); /* ... and do it */ foreach(l, stmts) *************** *** 571,576 **** standard_ProcessUtility(Node *parsetree, --- 757,769 ---- if (lnext(l) != NULL) CommandCounterIncrement(); } + + /* Call AFTER CREATE TABLE triggers */ + if (CommandFiresAfterTriggers(&cmd)) + { + cmd.objectId = relOid; + ExecAfterCommandTriggers(&cmd); + } } break; *************** *** 704,709 **** standard_ProcessUtility(Node *parsetree, --- 897,903 ---- List *stmts; ListCell *l; LOCKMODE lockmode; + CommandContextData cmd; /* * Figure out lock mode, and acquire lock. This also does *************** *** 716,721 **** standard_ProcessUtility(Node *parsetree, --- 910,930 ---- if (OidIsValid(relid)) { + /* + * Call BEFORE ALTER TABLE triggers + */ + InitCommandContext(&cmd, parsetree, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = relid; + cmd.objectname = atstmt->relation->relname; + cmd.schemaname = get_namespace_name( + RangeVarGetCreationNamespace(atstmt->relation)); + + ExecBeforeCommandTriggers(&cmd); + } + /* Run parse analysis ... */ stmts = transformAlterTableStmt(atstmt, queryString); *************** *** 744,749 **** standard_ProcessUtility(Node *parsetree, --- 953,961 ---- if (lnext(l) != NULL) CommandCounterIncrement(); } + /* Call AFTER ALTER TABLE triggers */ + if (CommandFiresAfterTriggers(&cmd)) + ExecAfterCommandTriggers(&cmd); } else ereport(NOTICE, *************** *** 755,760 **** standard_ProcessUtility(Node *parsetree, --- 967,978 ---- case T_AlterDomainStmt: { AlterDomainStmt *stmt = (AlterDomainStmt *) parsetree; + CommandContextData cmd; + + /* + * Prepare BEFORE ALTER DOMAIN triggers + */ + InitCommandContext(&cmd, parsetree, false); /* * Some or all of these functions are recursive to cover *************** *** 769,797 **** standard_ProcessUtility(Node *parsetree, * requested, for descendants */ AlterDomainDefault(stmt->typeName, ! stmt->def); break; case 'N': /* ALTER DOMAIN DROP NOT NULL */ AlterDomainNotNull(stmt->typeName, ! false); break; case 'O': /* ALTER DOMAIN SET NOT NULL */ AlterDomainNotNull(stmt->typeName, ! true); break; case 'C': /* ADD CONSTRAINT */ AlterDomainAddConstraint(stmt->typeName, ! stmt->def); break; case 'X': /* DROP CONSTRAINT */ AlterDomainDropConstraint(stmt->typeName, stmt->name, stmt->behavior, ! stmt->missing_ok); break; case 'V': /* VALIDATE CONSTRAINT */ AlterDomainValidateConstraint(stmt->typeName, ! stmt->name); break; default: /* oops */ elog(ERROR, "unrecognized alter domain type: %d", --- 987,1016 ---- * requested, for descendants */ AlterDomainDefault(stmt->typeName, ! stmt->def, &cmd); break; case 'N': /* ALTER DOMAIN DROP NOT NULL */ AlterDomainNotNull(stmt->typeName, ! false, &cmd); break; case 'O': /* ALTER DOMAIN SET NOT NULL */ AlterDomainNotNull(stmt->typeName, ! true, &cmd); break; case 'C': /* ADD CONSTRAINT */ AlterDomainAddConstraint(stmt->typeName, ! stmt->def, &cmd); break; case 'X': /* DROP CONSTRAINT */ AlterDomainDropConstraint(stmt->typeName, stmt->name, stmt->behavior, ! stmt->missing_ok, ! &cmd); break; case 'V': /* VALIDATE CONSTRAINT */ AlterDomainValidateConstraint(stmt->typeName, ! stmt->name, &cmd); break; default: /* oops */ elog(ERROR, "unrecognized alter domain type: %d", *************** *** 819,858 **** standard_ProcessUtility(Node *parsetree, case T_DefineStmt: { DefineStmt *stmt = (DefineStmt *) parsetree; switch (stmt->kind) { case OBJECT_AGGREGATE: DefineAggregate(stmt->defnames, stmt->args, ! stmt->oldstyle, stmt->definition); break; case OBJECT_OPERATOR: Assert(stmt->args == NIL); ! DefineOperator(stmt->defnames, stmt->definition); break; case OBJECT_TYPE: Assert(stmt->args == NIL); ! DefineType(stmt->defnames, stmt->definition); break; case OBJECT_TSPARSER: Assert(stmt->args == NIL); ! DefineTSParser(stmt->defnames, stmt->definition); break; case OBJECT_TSDICTIONARY: Assert(stmt->args == NIL); ! DefineTSDictionary(stmt->defnames, stmt->definition); break; case OBJECT_TSTEMPLATE: Assert(stmt->args == NIL); ! DefineTSTemplate(stmt->defnames, stmt->definition); break; case OBJECT_TSCONFIGURATION: Assert(stmt->args == NIL); ! DefineTSConfiguration(stmt->defnames, stmt->definition); break; case OBJECT_COLLATION: Assert(stmt->args == NIL); ! DefineCollation(stmt->defnames, stmt->definition); break; default: elog(ERROR, "unrecognized define stmt type: %d", --- 1038,1080 ---- case T_DefineStmt: { DefineStmt *stmt = (DefineStmt *) parsetree; + CommandContextData cmd; + + InitCommandContext(&cmd, parsetree, false); switch (stmt->kind) { case OBJECT_AGGREGATE: DefineAggregate(stmt->defnames, stmt->args, ! stmt->oldstyle, stmt->definition, &cmd); break; case OBJECT_OPERATOR: Assert(stmt->args == NIL); ! DefineOperator(stmt->defnames, stmt->definition, &cmd); break; case OBJECT_TYPE: Assert(stmt->args == NIL); ! DefineType(stmt->defnames, stmt->definition, &cmd); break; case OBJECT_TSPARSER: Assert(stmt->args == NIL); ! DefineTSParser(stmt->defnames, stmt->definition, &cmd); break; case OBJECT_TSDICTIONARY: Assert(stmt->args == NIL); ! DefineTSDictionary(stmt->defnames, stmt->definition, &cmd); break; case OBJECT_TSTEMPLATE: Assert(stmt->args == NIL); ! DefineTSTemplate(stmt->defnames, stmt->definition, &cmd); break; case OBJECT_TSCONFIGURATION: Assert(stmt->args == NIL); ! DefineTSConfiguration(stmt->defnames, stmt->definition, &cmd); break; case OBJECT_COLLATION: Assert(stmt->args == NIL); ! DefineCollation(stmt->defnames, stmt->definition, &cmd); break; default: elog(ERROR, "unrecognized define stmt type: %d", *************** *** 865,872 **** standard_ProcessUtility(Node *parsetree, case T_CompositeTypeStmt: /* CREATE TYPE (composite) */ { CompositeTypeStmt *stmt = (CompositeTypeStmt *) parsetree; ! DefineCompositeType(stmt->typevar, stmt->coldeflist); } break; --- 1087,1096 ---- case T_CompositeTypeStmt: /* CREATE TYPE (composite) */ { CompositeTypeStmt *stmt = (CompositeTypeStmt *) parsetree; + CommandContextData cmd; ! InitCommandContext(&cmd, parsetree, false); ! DefineCompositeType(stmt->typevar, stmt->coldeflist, &cmd); } break; *************** *** 904,909 **** standard_ProcessUtility(Node *parsetree, --- 1128,1136 ---- case T_IndexStmt: /* CREATE INDEX */ { IndexStmt *stmt = (IndexStmt *) parsetree; + CommandContextData cmd; + + InitCommandContext(&cmd, parsetree, false); if (stmt->concurrent) PreventTransactionChain(isTopLevel, *************** *** 934,940 **** standard_ProcessUtility(Node *parsetree, true, /* check_rights */ false, /* skip_build */ false, /* quiet */ ! stmt->concurrent); /* concurrent */ } break; --- 1161,1168 ---- true, /* check_rights */ false, /* skip_build */ false, /* quiet */ ! stmt->concurrent, /* concurrent */ ! &cmd); } break; *************** *** 972,978 **** standard_ProcessUtility(Node *parsetree, DropdbStmt *stmt = (DropdbStmt *) parsetree; PreventTransactionChain(isTopLevel, "DROP DATABASE"); ! dropdb(stmt->dbname, stmt->missing_ok); } break; --- 1200,1206 ---- DropdbStmt *stmt = (DropdbStmt *) parsetree; PreventTransactionChain(isTopLevel, "DROP DATABASE"); ! dropdb(stmt); } break; *************** *** 1012,1021 **** standard_ProcessUtility(Node *parsetree, --- 1240,1263 ---- case T_LoadStmt: { LoadStmt *stmt = (LoadStmt *) parsetree; + CommandContextData cmd; + + InitCommandContext(&cmd, parsetree, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectname = stmt->filename; + cmd.schemaname = NULL; + + ExecBeforeCommandTriggers(&cmd); + } closeAllVfds(); /* probably not necessary... */ /* Allowed names are restricted if you're not superuser */ load_file(stmt->filename, !superuser()); + + if (CommandFiresAfterTriggers(&cmd)) + ExecAfterCommandTriggers(&cmd); } break; *************** *** 1059,1064 **** standard_ProcessUtility(Node *parsetree, --- 1301,1314 ---- InvalidOid, InvalidOid, false); break; + case T_CreateCmdTrigStmt: + CreateCmdTrigger((CreateCmdTrigStmt *) parsetree, queryString); + break; + + case T_AlterCmdTrigStmt: + (void) AlterCmdTrigger((AlterCmdTrigStmt *) parsetree); + break; + case T_CreatePLangStmt: CreateProceduralLanguage((CreatePLangStmt *) parsetree); break; *************** *** 1131,1146 **** standard_ProcessUtility(Node *parsetree, case T_ReindexStmt: { ReindexStmt *stmt = (ReindexStmt *) parsetree; /* we choose to allow this during "read only" transactions */ PreventCommandDuringRecovery("REINDEX"); switch (stmt->kind) { case OBJECT_INDEX: ! ReindexIndex(stmt->relation); break; case OBJECT_TABLE: ! ReindexTable(stmt->relation); break; case OBJECT_DATABASE: --- 1381,1399 ---- case T_ReindexStmt: { ReindexStmt *stmt = (ReindexStmt *) parsetree; + CommandContextData cmd; + + InitCommandContext(&cmd, parsetree, false); /* we choose to allow this during "read only" transactions */ PreventCommandDuringRecovery("REINDEX"); switch (stmt->kind) { case OBJECT_INDEX: ! ReindexIndex(stmt->relation, &cmd); break; case OBJECT_TABLE: ! ReindexTable(stmt->relation, &cmd); break; case OBJECT_DATABASE: *************** *** 1197,1202 **** standard_ProcessUtility(Node *parsetree, --- 1450,1457 ---- (int) nodeTag(parsetree)); break; } + /* call the AFTER ANY COMMAND triggers */ + call_after_cmdtriggers(&cmd); } /* *************** *** 1704,1709 **** CreateCommandTag(Node *parsetree) --- 1959,1967 ---- case OBJECT_TRIGGER: tag = "DROP TRIGGER"; break; + case OBJECT_CMDTRIGGER: + tag = "DROP COMMAND TRIGGER"; + break; case OBJECT_RULE: tag = "DROP RULE"; break; *************** *** 1950,1955 **** CreateCommandTag(Node *parsetree) --- 2208,2221 ---- tag = "CREATE TRIGGER"; break; + case T_CreateCmdTrigStmt: + tag = "CREATE COMMAND TRIGGER"; + break; + + case T_AlterCmdTrigStmt: + tag = "ALTER COMMAND TRIGGER"; + break; + case T_CreatePLangStmt: tag = "CREATE LANGUAGE"; break; *************** *** 2151,2156 **** CreateCommandTag(Node *parsetree) --- 2417,2429 ---- break; } + /* + * Useful to raise WARNINGs for any DDL command not yet supported. + * + elog(WARNING, "Command Tag: %s", tag); + elog(WARNING, "Note to String: %s", nodeToString(parsetree)); + */ + return tag; } *************** *** 2445,2450 **** GetCommandLogLevel(Node *parsetree) --- 2718,2735 ---- lev = LOGSTMT_DDL; break; + case T_DropPropertyStmt: + lev = LOGSTMT_DDL; + break; + + case T_CreateCmdTrigStmt: + lev = LOGSTMT_DDL; + break; + + case T_AlterCmdTrigStmt: + lev = LOGSTMT_DDL; + break; + case T_CreatePLangStmt: lev = LOGSTMT_DDL; break; *** a/src/backend/utils/adt/ruleutils.c --- b/src/backend/utils/adt/ruleutils.c *************** *** 39,47 **** --- 39,49 ---- #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" *************** *** 259,265 **** 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 --- 261,266 ---- *** a/src/bin/pg_dump/pg_dump.c --- b/src/bin/pg_dump/pg_dump.c *************** *** 48,53 **** --- 48,54 ---- #include "access/transam.h" #include "catalog/pg_cast.h" #include "catalog/pg_class.h" + #include "catalog/pg_cmdtrigger.h" #include "catalog/pg_default_acl.h" #include "catalog/pg_largeobject.h" #include "catalog/pg_largeobject_metadata.h" *************** *** 191,196 **** static void dumpConversion(Archive *fout, ConvInfo *convinfo); --- 192,198 ---- static void dumpRule(Archive *fout, RuleInfo *rinfo); static void dumpAgg(Archive *fout, AggInfo *agginfo); static void dumpTrigger(Archive *fout, TriggerInfo *tginfo); + static void dumpCmdTrigger(Archive *fout, CmdTriggerInfo *ctginfo); static void dumpTable(Archive *fout, TableInfo *tbinfo); static void dumpTableSchema(Archive *fout, TableInfo *tbinfo); static void dumpAttrDef(Archive *fout, AttrDefInfo *adinfo); *************** *** 5283,5288 **** getTriggers(Archive *fout, TableInfo tblinfo[], int numTables) --- 5285,5359 ---- } /* + * getCmdTriggers + * get information about every command trigger on a dumpable table + */ + CmdTriggerInfo * + getCmdTriggers(Archive *fout, int *numCmdTriggers) + { + int i; + PQExpBuffer query = createPQExpBuffer(); + PGresult *res; + CmdTriggerInfo *ctginfo; + int i_tableoid, + i_oid, + i_ctgcommand, + i_ctgname, + i_ctgfname, + i_ctgtype, + i_ctgenabled; + int ntups; + + /* Make sure we are in proper schema */ + selectSourceSchema(fout, "pg_catalog"); + + if (fout->remoteVersion >= 90200) + { + appendPQExpBuffer(query, + "SELECT c.tableoid, c.oid, " + "ctgname, ctgtype, ctgcommand, proname as ctgfname, ctgenabled " + "FROM pg_cmdtrigger c JOIN pg_proc p on c.ctgfoid = p.oid " + "ORDER BY c.oid"); + } + + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); + + ntups = PQntuples(res); + + *numCmdTriggers = ntups; + + ctginfo = (CmdTriggerInfo *) pg_malloc(ntups * sizeof(CmdTriggerInfo)); + + i_tableoid = PQfnumber(res, "tableoid"); + i_oid = PQfnumber(res, "oid"); + i_ctgname = PQfnumber(res, "ctgname"); + i_ctgtype = PQfnumber(res, "ctgtype"); + i_ctgcommand = PQfnumber(res, "ctgcommand"); + i_ctgfname = PQfnumber(res, "ctgfname"); + i_ctgenabled = PQfnumber(res, "ctgenabled"); + + for (i = 0; i < ntups; i++) + { + ctginfo[i].dobj.objType = DO_CMDTRIGGER; + ctginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); + ctginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&ctginfo[i].dobj); + ctginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_ctgname)); + ctginfo[i].ctgname = pg_strdup(PQgetvalue(res, i, i_ctgname)); + ctginfo[i].ctgtype = *(PQgetvalue(res, i, i_ctgtype)); + ctginfo[i].ctgcommand = pg_strdup(PQgetvalue(res, i, i_ctgcommand)); + ctginfo[i].ctgfname = pg_strdup(PQgetvalue(res, i, i_ctgfname)); + ctginfo[i].ctgenabled = *(PQgetvalue(res, i, i_ctgenabled)); + } + + PQclear(res); + + destroyPQExpBuffer(query); + + return ctginfo; + } + + /* * getProcLangs * get basic information about every procedural language in the system * *************** *** 7173,7178 **** dumpDumpableObject(Archive *fout, DumpableObject *dobj) --- 7244,7252 ---- case DO_TRIGGER: dumpTrigger(fout, (TriggerInfo *) dobj); break; + case DO_CMDTRIGGER: + dumpCmdTrigger(fout, (CmdTriggerInfo *) dobj); + break; case DO_CONSTRAINT: dumpConstraint(fout, (ConstraintInfo *) dobj); break; *************** *** 13645,13650 **** dumpTrigger(Archive *fout, TriggerInfo *tginfo) --- 13719,13777 ---- destroyPQExpBuffer(labelq); } + static void + dumpCmdTrigger(Archive *fout, CmdTriggerInfo *ctginfo) + { + PQExpBuffer query; + PQExpBuffer labelq; + + query = createPQExpBuffer(); + labelq = createPQExpBuffer(); + + appendPQExpBuffer(query, "CREATE TRIGGER "); + appendPQExpBufferStr(query, fmtId(ctginfo->dobj.name)); + + /* Trigger type */ + if (ctginfo->ctgtype == CMD_TRIGGER_FIRED_BEFORE) + appendPQExpBuffer(query, " BEFORE"); + else if (ctginfo->ctgtype == CMD_TRIGGER_FIRED_AFTER) + appendPQExpBuffer(query, " AFTER"); + else + { + write_msg(NULL, "unexpected ctgtype value: %d\n", ctginfo->ctgtype); + exit_nicely(1); + } + + if (strcmp("ANY", ctginfo->ctgcommand) == 0) + appendPQExpBufferStr(query, " ANY COMMAND"); + else + { + appendPQExpBufferStr(query, " COMMAND "); + appendPQExpBufferStr(query, fmtId(ctginfo->ctgcommand)); + } + + appendPQExpBuffer(query, " EXECUTE PROCEDURE "); + appendPQExpBufferStr(query, fmtId(ctginfo->ctgfname)); + appendPQExpBuffer(query, " ();\n"); + + appendPQExpBuffer(labelq, "TRIGGER %s ", + fmtId(ctginfo->dobj.name)); + appendPQExpBuffer(labelq, "ON COMMAND %s", + fmtId(ctginfo->ctgcommand)); + + ArchiveEntry(fout, ctginfo->dobj.catId, ctginfo->dobj.dumpId, + ctginfo->dobj.name, NULL, NULL, "", false, + "COMMAND TRIGGER", SECTION_POST_DATA, + query->data, "", NULL, NULL, 0, NULL, NULL); + + dumpComment(fout, labelq->data, + NULL, NULL, + ctginfo->dobj.catId, 0, ctginfo->dobj.dumpId); + + destroyPQExpBuffer(query); + destroyPQExpBuffer(labelq); + } + /* * dumpRule * Dump a rule *** a/src/bin/pg_dump/pg_dump.h --- b/src/bin/pg_dump/pg_dump.h *************** *** 118,124 **** typedef enum DO_DEFAULT_ACL, DO_BLOB, DO_BLOB_DATA, ! DO_COLLATION } DumpableObjectType; typedef struct _dumpableObject --- 118,125 ---- DO_DEFAULT_ACL, DO_BLOB, DO_BLOB_DATA, ! DO_COLLATION, ! DO_CMDTRIGGER } DumpableObjectType; typedef struct _dumpableObject *************** *** 350,355 **** typedef struct _triggerInfo --- 351,366 ---- char *tgdef; } TriggerInfo; + typedef struct _cmdtriggerInfo + { + DumpableObject dobj; + char *ctgcommand; + char *ctgname; + char *ctgfname; + char ctgtype; + char ctgenabled; + } CmdTriggerInfo; + /* * struct ConstraintInfo is used for all constraint types. However we * use a different objType for foreign key constraints, to make it easier *************** *** 558,562 **** extern ForeignServerInfo *getForeignServers(Archive *fout, --- 569,574 ---- extern DefaultACLInfo *getDefaultACLs(Archive *fout, int *numDefaultACLs); extern void getExtensionMembership(Archive *fout, ExtensionInfo extinfo[], int numExtensions); + extern CmdTriggerInfo *getCmdTriggers(Archive *fout, int *numCmdTriggers); #endif /* PG_DUMP_H */ *** a/src/bin/pg_dump/pg_dump_sort.c --- b/src/bin/pg_dump/pg_dump_sort.c *************** *** 59,65 **** static const int oldObjectTypePriority[] = 17, /* DO_DEFAULT_ACL */ 9, /* DO_BLOB */ 11, /* DO_BLOB_DATA */ ! 2 /* DO_COLLATION */ }; /* --- 59,66 ---- 17, /* DO_DEFAULT_ACL */ 9, /* DO_BLOB */ 11, /* DO_BLOB_DATA */ ! 2, /* DO_COLLATION */ ! 18 /* DO_CMDTRIGGER */ }; /* *************** *** 98,104 **** static const int newObjectTypePriority[] = 29, /* DO_DEFAULT_ACL */ 21, /* DO_BLOB */ 23, /* DO_BLOB_DATA */ ! 3 /* DO_COLLATION */ }; --- 99,106 ---- 29, /* DO_DEFAULT_ACL */ 21, /* DO_BLOB */ 23, /* DO_BLOB_DATA */ ! 3, /* DO_COLLATION */ ! 30 /* DO_CMDTRIGGER */ }; *************** *** 1114,1119 **** describeDumpableObject(DumpableObject *obj, char *buf, int bufsize) --- 1116,1126 ---- "TRIGGER %s (ID %d OID %u)", obj->name, obj->dumpId, obj->catId.oid); return; + case DO_CMDTRIGGER: + snprintf(buf, bufsize, + "TRIGGER %s ON COMMAND %s (ID %d OID %u)", + obj->name, ((CmdTriggerInfo *)obj)->ctgcommand, obj->dumpId, obj->catId.oid); + return; case DO_CONSTRAINT: snprintf(buf, bufsize, "CONSTRAINT %s (ID %d OID %u)", *** a/src/bin/psql/command.c --- b/src/bin/psql/command.c *************** *** 363,369 **** exec_command(const char *cmd, success = describeTablespaces(pattern, show_verbose); break; case 'c': ! success = listConversions(pattern, show_verbose, show_system); break; case 'C': success = listCasts(pattern, show_verbose); --- 363,380 ---- success = describeTablespaces(pattern, show_verbose); break; case 'c': ! switch (cmd[2]) ! { ! case '\0': ! success = listConversions(pattern, show_verbose, show_system); ! break; ! case 't': ! success = listCmdTriggers(pattern, show_verbose); ! break; ! default: ! status = PSQL_CMD_UNKNOWN; ! break; ! } break; case 'C': success = listCasts(pattern, show_verbose); *** a/src/bin/psql/describe.c --- b/src/bin/psql/describe.c *************** *** 2941,2946 **** listConversions(const char *pattern, bool verbose, bool showSystem) --- 2941,3003 ---- } /* + * \dct + * + * Describes command triggers. + */ + bool + listCmdTriggers(const char *pattern, bool verbose) + { + PQExpBufferData buf; + PGresult *res; + printQueryOpt myopt = pset.popt; + static const bool translate_columns[] = {true, true}; + + initPQExpBuffer(&buf); + + printfPQExpBuffer(&buf, + "SELECT ctgname as \"%s\", " + "'CREATE TRIGGER ' || ctgname || ' ' || " + "case ctgtype when 'A' then 'AFTER' " + " when 'B' then 'BEFORE' " + " when 'I' then 'INSTEAD OF' " + "end || " + "case ctgcommand when 'ANY' then ' ANY COMMAND '" + " else ' COMMAND ' || ctgcommand || ' '" + "end ||" + " 'EXECUTE PROCEDURE ' || proname || '();' as \"%s\" " + "FROM pg_cmdtrigger c " + "JOIN pg_proc p on c.ctgfoid = p.oid ", + gettext_noop("Name"), + gettext_noop("Definition")); + + if (pattern) + { + processSQLNamePattern(pset.db, &buf, pattern, false, false, + NULL, "ctgcommand", NULL, NULL); + + appendPQExpBuffer(&buf, " OR ctgcommand = 'ANY' "); + } + + appendPQExpBuffer(&buf, "ORDER BY c.oid"); + + res = PSQLexec(buf.data, false); + termPQExpBuffer(&buf); + if (!res) + return false; + + myopt.nullPrint = NULL; + myopt.title = _("List of command triggers"); + myopt.translate_header = true; + myopt.translate_columns = translate_columns; + + printQuery(res, &myopt, pset.queryFout, pset.logfile); + + PQclear(res); + return true; + } + + /* * \dC * * Describes casts. *** a/src/bin/psql/describe.h --- b/src/bin/psql/describe.h *************** *** 66,71 **** extern bool listDomains(const char *pattern, bool verbose, bool showSystem); --- 66,74 ---- /* \dc */ extern bool listConversions(const char *pattern, bool verbose, bool showSystem); + /* \dcT */ + extern bool listCmdTriggers(const char *pattern, bool verbose); + /* \dC */ extern bool listCasts(const char *pattern, bool verbose); *** a/src/bin/psql/help.c --- b/src/bin/psql/help.c *************** *** 199,204 **** slashUsage(unsigned short int pager) --- 199,205 ---- fprintf(output, _(" \\d[S+] NAME describe table, view, sequence, or index\n")); fprintf(output, _(" \\da[S] [PATTERN] list aggregates\n")); fprintf(output, _(" \\db[+] [PATTERN] list tablespaces\n")); + fprintf(output, _(" \\dct [PATTERN] list command triggers\n")); fprintf(output, _(" \\dc[S+] [PATTERN] list conversions\n")); fprintf(output, _(" \\dC[+] [PATTERN] list casts\n")); fprintf(output, _(" \\dd[S] [PATTERN] show object descriptions not displayed elsewhere\n")); *** a/src/include/catalog/dependency.h --- b/src/include/catalog/dependency.h *************** *** 146,151 **** typedef enum ObjectClass --- 146,152 ---- OCLASS_USER_MAPPING, /* pg_user_mapping */ OCLASS_DEFACL, /* pg_default_acl */ OCLASS_EXTENSION, /* pg_extension */ + OCLASS_CMDTRIGGER, /* pg_cmdtrigger */ MAX_OCLASS /* MUST BE LAST */ } ObjectClass; *** a/src/include/catalog/index.h --- b/src/include/catalog/index.h *************** *** 14,19 **** --- 14,20 ---- #ifndef INDEX_H #define INDEX_H + #include "commands/cmdtrigger.h" #include "nodes/execnodes.h" *************** *** 88,101 **** extern double IndexBuildHeapScan(Relation heapRelation, extern void validate_index(Oid heapId, Oid indexId, Snapshot snapshot); ! extern void reindex_index(Oid indexId, bool skip_constraint_checks); /* Flag bits for reindex_relation(): */ #define REINDEX_REL_PROCESS_TOAST 0x01 #define REINDEX_REL_SUPPRESS_INDEX_USE 0x02 #define REINDEX_REL_CHECK_CONSTRAINTS 0x04 ! extern bool reindex_relation(Oid relid, int flags); extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); --- 89,103 ---- extern void validate_index(Oid heapId, Oid indexId, Snapshot snapshot); ! extern void reindex_index(Oid indexId, bool skip_constraint_checks, ! CommandContext cmd); /* Flag bits for reindex_relation(): */ #define REINDEX_REL_PROCESS_TOAST 0x01 #define REINDEX_REL_SUPPRESS_INDEX_USE 0x02 #define REINDEX_REL_CHECK_CONSTRAINTS 0x04 ! extern bool reindex_relation(Oid relid, int flags, CommandContext cmd); extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); *** a/src/include/catalog/indexing.h --- b/src/include/catalog/indexing.h *************** *** 234,239 **** DECLARE_UNIQUE_INDEX(pg_trigger_tgrelid_tgname_index, 2701, on pg_trigger using --- 234,244 ---- DECLARE_UNIQUE_INDEX(pg_trigger_oid_index, 2702, on pg_trigger using btree(oid oid_ops)); #define TriggerOidIndexId 2702 + DECLARE_UNIQUE_INDEX(pg_cmdtrigger_ctgcommand_ctgname_index, 3467, on pg_cmdtrigger using btree(ctgcommand name_ops, ctgname name_ops)); + #define CmdTriggerCommandNameIndexId 3467 + DECLARE_UNIQUE_INDEX(pg_cmdtrigger_oid_index, 3468, on pg_cmdtrigger using btree(oid oid_ops)); + #define CmdTriggerOidIndexId 3468 + DECLARE_UNIQUE_INDEX(pg_ts_config_cfgname_index, 3608, on pg_ts_config using btree(cfgname name_ops, cfgnamespace oid_ops)); #define TSConfigNameNspIndexId 3608 DECLARE_UNIQUE_INDEX(pg_ts_config_oid_index, 3712, on pg_ts_config using btree(oid oid_ops)); *** a/src/include/catalog/pg_aggregate.h --- b/src/include/catalog/pg_aggregate.h *************** *** 19,24 **** --- 19,25 ---- #ifndef PG_AGGREGATE_H #define PG_AGGREGATE_H + #include "commands/cmdtrigger.h" #include "catalog/genbki.h" #include "nodes/pg_list.h" *************** *** 242,247 **** extern void AggregateCreate(const char *aggName, List *aggfinalfnName, List *aggsortopName, Oid aggTransType, ! const char *agginitval); #endif /* PG_AGGREGATE_H */ --- 243,249 ---- List *aggfinalfnName, List *aggsortopName, Oid aggTransType, ! const char *agginitval, ! CommandContext cmd); #endif /* PG_AGGREGATE_H */ *** /dev/null --- b/src/include/catalog/pg_cmdtrigger.h *************** *** 0 **** --- 1,70 ---- + /*------------------------------------------------------------------------- + * + * pg_cmdtrigger.h + * definition of the system "command trigger" relation (pg_cmdtrigger) + * along with the relation's initial contents. + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/catalog/pg_trigger.h + * + * NOTES + * the genbki.pl script reads this file and generates .bki + * information from the DATA() statements. + * + *------------------------------------------------------------------------- + */ + #ifndef PG_CMDTRIGGER_H + #define PG_CMDTRIGGER_H + + #include "catalog/genbki.h" + + /* ---------------- + * pg_cmdtrigger definition. cpp turns this into + * typedef struct FormData_pg_cmdtrigger + * ---------------- + */ + #define CmdTriggerRelationId 3466 + + CATALOG(pg_cmdtrigger,3466) + { + NameData ctgcommand; /* trigger's command */ + NameData ctgname; /* trigger's name */ + Oid ctgfoid; /* OID of function to be called */ + char ctgtype; /* BEFORE/AFTER */ + char ctgenabled; /* trigger's firing configuration WRT + * session_replication_role */ + } FormData_pg_cmdtrigger; + + /* ---------------- + * Form_pg_cmdtrigger corresponds to a pointer to a tuple with + * the format of pg_cmdtrigger relation. + * ---------------- + */ + typedef FormData_pg_cmdtrigger *Form_pg_cmdtrigger; + + /* ---------------- + * compiler constants for pg_cmdtrigger + * ---------------- + */ + #define Natts_pg_cmdtrigger 5 + #define Anum_pg_cmdtrigger_ctgcommand 1 + #define Anum_pg_cmdtrigger_ctgname 2 + #define Anum_pg_cmdtrigger_ctgfoid 3 + #define Anum_pg_cmdtrigger_ctgtype 4 + #define Anum_pg_cmdtrigger_ctgenabled 5 + + /* + * Times at which a command trigger can be fired. These are the + * possible values for pg_cmdtrigger.ctgtype. + * + * pg_trigger is using binary mask tricks to make it super fast, but we don't + * need to be that tricky here: we're talking about commands, not data editing, + * and we don't have so many conditions, only type and enabled. + */ + #define CMD_TRIGGER_FIRED_BEFORE 'B' + #define CMD_TRIGGER_FIRED_AFTER 'A' + + #endif /* PG_CMDTRIGGER_H */ *** a/src/include/catalog/pg_collation_fn.h --- b/src/include/catalog/pg_collation_fn.h *************** *** 14,23 **** #ifndef PG_COLLATION_FN_H #define PG_COLLATION_FN_H extern Oid CollationCreate(const char *collname, Oid collnamespace, Oid collowner, int32 collencoding, ! const char *collcollate, const char *collctype); extern void RemoveCollationById(Oid collationOid); #endif /* PG_COLLATION_FN_H */ --- 14,26 ---- #ifndef PG_COLLATION_FN_H #define PG_COLLATION_FN_H + #include "commands/cmdtrigger.h" + extern Oid CollationCreate(const char *collname, Oid collnamespace, Oid collowner, int32 collencoding, ! const char *collcollate, const char *collctype, ! CommandContext cmd); extern void RemoveCollationById(Oid collationOid); #endif /* PG_COLLATION_FN_H */ *** a/src/include/catalog/pg_operator.h --- b/src/include/catalog/pg_operator.h *************** *** 23,28 **** --- 23,29 ---- #define PG_OPERATOR_H #include "catalog/genbki.h" + #include "commands/cmdtrigger.h" #include "nodes/pg_list.h" /* ---------------- *************** *** 1722,1727 **** extern void OperatorCreate(const char *operatorName, Oid restrictionId, Oid joinId, bool canMerge, ! bool canHash); #endif /* PG_OPERATOR_H */ --- 1723,1729 ---- Oid restrictionId, Oid joinId, bool canMerge, ! bool canHash, ! CommandContext cmd); #endif /* PG_OPERATOR_H */ *** a/src/include/catalog/pg_type_fn.h --- b/src/include/catalog/pg_type_fn.h *************** *** 14,19 **** --- 14,20 ---- #ifndef PG_TYPE_FN_H #define PG_TYPE_FN_H + #include "commands/cmdtrigger.h" #include "nodes/nodes.h" *************** *** 73,79 **** extern void GenerateTypeDependencies(Oid typeNamespace, bool rebuild); extern void RenameTypeInternal(Oid typeOid, const char *newTypeName, ! Oid typeNamespace); extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace); --- 74,80 ---- bool rebuild); extern void RenameTypeInternal(Oid typeOid, const char *newTypeName, ! Oid typeNamespace, CommandContext cmd); extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace); *** a/src/include/commands/alter.h --- b/src/include/commands/alter.h *************** *** 14,19 **** --- 14,20 ---- #ifndef ALTER_H #define ALTER_H + #include "commands/cmdtrigger.h" #include "utils/acl.h" #include "utils/relcache.h" *************** *** 23,29 **** extern Oid AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid); extern Oid AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId, Oid objid, Oid nspOid, int Anum_name, int Anum_namespace, int Anum_owner, ! AclObjectKind acl_kind); extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt); #endif /* ALTER_H */ --- 24,30 ---- extern Oid AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId, Oid objid, Oid nspOid, int Anum_name, int Anum_namespace, int Anum_owner, ! AclObjectKind acl_kind, CommandContext cmd); extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt); #endif /* ALTER_H */ *** a/src/include/commands/cluster.h --- b/src/include/commands/cluster.h *************** *** 13,18 **** --- 13,19 ---- #ifndef CLUSTER_H #define CLUSTER_H + #include "commands/cmdtrigger.h" #include "nodes/parsenodes.h" #include "storage/lock.h" #include "utils/relcache.h" *************** *** 20,26 **** extern void cluster(ClusterStmt *stmt, bool isTopLevel); extern void cluster_rel(Oid tableOid, Oid indexOid, bool recheck, ! bool verbose, int freeze_min_age, int freeze_table_age); extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck, LOCKMODE lockmode); extern void mark_index_clustered(Relation rel, Oid indexOid); --- 21,28 ---- extern void cluster(ClusterStmt *stmt, bool isTopLevel); extern void cluster_rel(Oid tableOid, Oid indexOid, bool recheck, ! bool verbose, int freeze_min_age, int freeze_table_age, ! CommandContext cmd); extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck, LOCKMODE lockmode); extern void mark_index_clustered(Relation rel, Oid indexOid); *** /dev/null --- b/src/include/commands/cmdtrigger.h *************** *** 0 **** --- 1,54 ---- + /*------------------------------------------------------------------------- + * + * cmdtrigger.h + * Declarations for command trigger handling. + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/commands/cmdtrigger.h + * + *------------------------------------------------------------------------- + */ + #ifndef CMDTRIGGER_H + #define CMDTRIGGER_H + + #include "nodes/parsenodes.h" + + /* + * Command Trigger Procedure are passed 4 arguments which are maintained in a + * global (bachend private) variable, command_context. That allows each command + * implementation to fill in the context then call the Exec...CommandTriggers + * API functions. + */ + typedef struct CommandContextData + { + char *tag; /* Command Tag */ + Oid objectId; /* oid of the existing object, if any */ + char *schemaname; /* schemaname or NULL if not relevant */ + char *objectname; /* objectname */ + Node *parsetree; /* command parsetree, given as an internal */ + List *before; /* procedures to call before the command */ + List *after; /* procedures to call after the command */ + } CommandContextData; + + typedef struct CommandContextData *CommandContext; + + extern CommandContext command_context; + + extern void CreateCmdTrigger(CreateCmdTrigStmt *stmt, const char *queryString); + extern void RemoveCmdTriggerById(Oid ctrigOid); + extern Oid get_cmdtrigger_oid(const char *trigname, const char *command, bool missing_ok); + extern void AlterCmdTrigger(AlterCmdTrigStmt *stmt); + extern void RenameCmdTrigger(List *command, const char *trigname, const char *newname); + + extern void InitCommandContext(CommandContext cmd, const Node *stmt, bool list_triggers); + extern bool ListCommandTriggers(CommandContext cmd); + extern bool CommandFiresTriggers(CommandContext cmd); + extern bool CommandFiresAfterTriggers(CommandContext cmd); + extern void ExecBeforeCommandTriggers(CommandContext cmd); + extern void ExecBeforeAnyCommandTriggers(CommandContext cmd); + extern void ExecAfterCommandTriggers(CommandContext cmd); + extern void ExecAfterAnyCommandTriggers(CommandContext cmd); + + #endif /* CMD_TRIGGER_H */ *** a/src/include/commands/collationcmds.h --- b/src/include/commands/collationcmds.h *************** *** 15,27 **** #ifndef COLLATIONCMDS_H #define COLLATIONCMDS_H #include "nodes/parsenodes.h" ! extern void DefineCollation(List *names, List *parameters); ! extern void RenameCollation(List *name, const char *newname); ! extern void AlterCollationOwner(List *name, Oid newOwnerId); ! extern void AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId); ! extern void AlterCollationNamespace(List *name, const char *newschema); ! extern Oid AlterCollationNamespace_oid(Oid collOid, Oid newNspOid); #endif /* COLLATIONCMDS_H */ --- 15,30 ---- #ifndef COLLATIONCMDS_H #define COLLATIONCMDS_H + #include "commands/cmdtrigger.h" #include "nodes/parsenodes.h" ! extern void DefineCollation(List *names, List *parameters, CommandContext cmd); ! extern void RenameCollation(List *name, const char *newname, CommandContext cmd); ! extern void AlterCollationOwner(List *name, Oid newOwnerId, CommandContext cmd); ! extern void AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId, CommandContext cmd); ! extern void AlterCollationNamespace(List *name, const char *newschema, ! CommandContext cmd); ! extern Oid AlterCollationNamespace_oid(Oid collOid, Oid newNspOid, ! CommandContext cmd); #endif /* COLLATIONCMDS_H */ *** a/src/include/commands/conversioncmds.h --- b/src/include/commands/conversioncmds.h *************** *** 18,27 **** #include "nodes/parsenodes.h" extern void CreateConversionCommand(CreateConversionStmt *parsetree); ! extern void RenameConversion(List *name, const char *newname); ! extern void AlterConversionOwner(List *name, Oid newOwnerId); ! extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId); ! extern void AlterConversionNamespace(List *name, const char *newschema); extern Oid AlterConversionNamespace_oid(Oid convOid, Oid newNspOid); #endif /* CONVERSIONCMDS_H */ --- 18,28 ---- #include "nodes/parsenodes.h" extern void CreateConversionCommand(CreateConversionStmt *parsetree); ! extern void RenameConversion(List *name, const char *newname, CommandContext cmd); ! extern void AlterConversionOwner(List *name, Oid newOwnerId, CommandContext cmd); ! extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId, CommandContext cmd); ! extern void AlterConversionNamespace(List *name, const char *newschema, ! CommandContext cmd); extern Oid AlterConversionNamespace_oid(Oid convOid, Oid newNspOid); #endif /* CONVERSIONCMDS_H */ *** a/src/include/commands/dbcommands.h --- b/src/include/commands/dbcommands.h *************** *** 53,59 **** typedef struct xl_dbase_drop_rec } xl_dbase_drop_rec; extern void createdb(const CreatedbStmt *stmt); ! extern void dropdb(const char *dbname, bool missing_ok); extern void RenameDatabase(const char *oldname, const char *newname); extern void AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel); extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt); --- 53,59 ---- } xl_dbase_drop_rec; extern void createdb(const CreatedbStmt *stmt); ! extern void dropdb(const DropdbStmt *stmt); extern void RenameDatabase(const char *oldname, const char *newname); extern void AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel); extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt); *** a/src/include/commands/defrem.h --- b/src/include/commands/defrem.h *************** *** 14,19 **** --- 14,20 ---- #ifndef DEFREM_H #define DEFREM_H + #include "commands/cmdtrigger.h" #include "nodes/parsenodes.h" /* commands/dropcmds.c */ *************** *** 39,47 **** extern Oid DefineIndex(RangeVar *heapRelation, bool check_rights, bool skip_build, bool quiet, ! bool concurrent); ! extern void ReindexIndex(RangeVar *indexRelation); ! extern void ReindexTable(RangeVar *relation); extern void ReindexDatabase(const char *databaseName, bool do_system, bool do_user); extern char *makeObjectName(const char *name1, const char *name2, --- 40,49 ---- bool check_rights, bool skip_build, bool quiet, ! bool concurrent, ! CommandContext cmd); ! extern void ReindexIndex(RangeVar *indexRelation, CommandContext cmd); ! extern void ReindexTable(RangeVar *relation, CommandContext cmd); extern void ReindexDatabase(const char *databaseName, bool do_system, bool do_user); extern char *makeObjectName(const char *name1, const char *name2, *************** *** 64,95 **** extern void CreateFunction(CreateFunctionStmt *stmt, const char *queryString); extern void RemoveFunctionById(Oid funcOid); extern void SetFunctionReturnType(Oid funcOid, Oid newRetType); extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType); ! extern void RenameFunction(List *name, List *argtypes, const char *newname); ! extern void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId); ! extern void AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId); extern void AlterFunction(AlterFunctionStmt *stmt); extern void CreateCast(CreateCastStmt *stmt); extern void DropCastById(Oid castOid); extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg, ! const char *newschema); ! extern Oid AlterFunctionNamespace_oid(Oid procOid, Oid nspOid); extern void ExecuteDoStmt(DoStmt *stmt); extern Oid get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok); /* commands/operatorcmds.c */ ! extern void DefineOperator(List *names, List *parameters); extern void RemoveOperatorById(Oid operOid); extern void AlterOperatorOwner(List *name, TypeName *typeName1, ! TypeName *typename2, Oid newOwnerId); extern void AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId); ! extern void AlterOperatorNamespace(List *names, List *argtypes, const char *newschema); extern Oid AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid); /* commands/aggregatecmds.c */ extern void DefineAggregate(List *name, List *args, bool oldstyle, ! List *parameters); ! extern void RenameAggregate(List *name, List *args, const char *newname); ! extern void AlterAggregateOwner(List *name, List *args, Oid newOwnerId); /* commands/opclasscmds.c */ extern void DefineOpClass(CreateOpClassStmt *stmt); --- 66,99 ---- extern void RemoveFunctionById(Oid funcOid); extern void SetFunctionReturnType(Oid funcOid, Oid newRetType); extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType); ! extern void RenameFunction(List *name, List *argtypes, const char *newname, CommandContext cmd); ! extern void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId, CommandContext cmd); ! extern void AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId, CommandContext cmd); extern void AlterFunction(AlterFunctionStmt *stmt); extern void CreateCast(CreateCastStmt *stmt); extern void DropCastById(Oid castOid); extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg, ! const char *newschema, CommandContext cmd); ! extern Oid AlterFunctionNamespace_oid(Oid procOid, Oid nspOid, ! CommandContext cmd); extern void ExecuteDoStmt(DoStmt *stmt); extern Oid get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok); /* commands/operatorcmds.c */ ! extern void DefineOperator(List *names, List *parameters, CommandContext cmd); extern void RemoveOperatorById(Oid operOid); extern void AlterOperatorOwner(List *name, TypeName *typeName1, ! TypeName *typename2, Oid newOwnerId, CommandContext cmd); extern void AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId); ! extern void AlterOperatorNamespace(List *names, List *argtypes, ! const char *newschema, CommandContext cmd); extern Oid AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid); /* commands/aggregatecmds.c */ extern void DefineAggregate(List *name, List *args, bool oldstyle, ! List *parameters, CommandContext cmd); ! extern void RenameAggregate(List *name, List *args, const char *newname, CommandContext cmd); ! extern void AlterAggregateOwner(List *name, List *args, Oid newOwnerId, CommandContext cmd); /* commands/opclasscmds.c */ extern void DefineOpClass(CreateOpClassStmt *stmt); *************** *** 103,145 **** extern void RenameOpClass(List *name, const char *access_method, const char *new extern void RenameOpFamily(List *name, const char *access_method, const char *newname); extern void AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId); extern void AlterOpClassOwner_oid(Oid opclassOid, Oid newOwnerId); ! extern void AlterOpClassNamespace(List *name, char *access_method, const char *newschema); extern Oid AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid); extern void AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId); extern void AlterOpFamilyOwner_oid(Oid opfamilyOid, Oid newOwnerId); ! extern void AlterOpFamilyNamespace(List *name, char *access_method, const char *newschema); extern Oid AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid); extern Oid get_am_oid(const char *amname, bool missing_ok); extern Oid get_opclass_oid(Oid amID, List *opclassname, bool missing_ok); extern Oid get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok); /* commands/tsearchcmds.c */ ! extern void DefineTSParser(List *names, List *parameters); ! extern void RenameTSParser(List *oldname, const char *newname); ! extern void AlterTSParserNamespace(List *name, const char *newschema); extern Oid AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid); extern void RemoveTSParserById(Oid prsId); ! extern void DefineTSDictionary(List *names, List *parameters); ! extern void RenameTSDictionary(List *oldname, const char *newname); extern void RemoveTSDictionaryById(Oid dictId); extern void AlterTSDictionary(AlterTSDictionaryStmt *stmt); ! extern void AlterTSDictionaryOwner(List *name, Oid newOwnerId); ! extern void AlterTSDictionaryNamespace(List *name, const char *newschema); extern Oid AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid); ! extern void DefineTSTemplate(List *names, List *parameters); ! extern void RenameTSTemplate(List *oldname, const char *newname); ! extern void AlterTSTemplateNamespace(List *name, const char *newschema); extern Oid AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid); extern void RemoveTSTemplateById(Oid tmplId); ! extern void DefineTSConfiguration(List *names, List *parameters); ! extern void RenameTSConfiguration(List *oldname, const char *newname); extern void RemoveTSConfigurationById(Oid cfgId); extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt); ! extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId); ! extern void AlterTSConfigurationNamespace(List *name, const char *newschema); extern Oid AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid); extern text *serialize_deflist(List *deflist); --- 107,151 ---- extern void RenameOpFamily(List *name, const char *access_method, const char *newname); extern void AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId); extern void AlterOpClassOwner_oid(Oid opclassOid, Oid newOwnerId); ! extern void AlterOpClassNamespace(List *name, char *access_method, ! const char *newschema, CommandContext cmd); extern Oid AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid); extern void AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId); extern void AlterOpFamilyOwner_oid(Oid opfamilyOid, Oid newOwnerId); ! extern void AlterOpFamilyNamespace(List *name, char *access_method, ! const char *newschema, CommandContext cmd); extern Oid AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid); extern Oid get_am_oid(const char *amname, bool missing_ok); extern Oid get_opclass_oid(Oid amID, List *opclassname, bool missing_ok); extern Oid get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok); /* commands/tsearchcmds.c */ ! extern void DefineTSParser(List *names, List *parameters, CommandContext cmd); ! extern void RenameTSParser(List *oldname, const char *newname, CommandContext cmd); ! extern void AlterTSParserNamespace(List *name, const char *newschema, CommandContext cmd); extern Oid AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid); extern void RemoveTSParserById(Oid prsId); ! extern void DefineTSDictionary(List *names, List *parameters, CommandContext cmd); ! extern void RenameTSDictionary(List *oldname, const char *newname, CommandContext cmd); extern void RemoveTSDictionaryById(Oid dictId); extern void AlterTSDictionary(AlterTSDictionaryStmt *stmt); ! extern void AlterTSDictionaryOwner(List *name, Oid newOwnerId, CommandContext cmd); ! extern void AlterTSDictionaryNamespace(List *name, const char *newschema, CommandContext cmd); extern Oid AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid); ! extern void DefineTSTemplate(List *names, List *parameters, CommandContext cmd); ! extern void RenameTSTemplate(List *oldname, const char *newname, CommandContext cmd); ! extern void AlterTSTemplateNamespace(List *name, const char *newschema, CommandContext cmd); extern Oid AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid); extern void RemoveTSTemplateById(Oid tmplId); ! extern void DefineTSConfiguration(List *names, List *parameters, CommandContext cmd); ! extern void RenameTSConfiguration(List *oldname, const char *newname, CommandContext cmd); extern void RemoveTSConfigurationById(Oid cfgId); extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt); ! extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId, CommandContext cmd); ! extern void AlterTSConfigurationNamespace(List *name, const char *newschema, CommandContext cmd); extern Oid AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid); extern text *serialize_deflist(List *deflist); *** a/src/include/commands/extension.h --- b/src/include/commands/extension.h *************** *** 14,19 **** --- 14,20 ---- #ifndef EXTENSION_H #define EXTENSION_H + #include "commands/cmdtrigger.h" #include "nodes/parsenodes.h" *************** *** 43,48 **** extern void ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt); extern Oid get_extension_oid(const char *extname, bool missing_ok); extern char *get_extension_name(Oid ext_oid); ! extern void AlterExtensionNamespace(List *names, const char *newschema); #endif /* EXTENSION_H */ --- 44,50 ---- extern Oid get_extension_oid(const char *extname, bool missing_ok); extern char *get_extension_name(Oid ext_oid); ! extern void AlterExtensionNamespace(List *names, const char *newschema, ! CommandContext cmd); #endif /* EXTENSION_H */ *** a/src/include/commands/schemacmds.h --- b/src/include/commands/schemacmds.h *************** *** 22,29 **** extern void CreateSchemaCommand(CreateSchemaStmt *parsetree, extern void RemoveSchemaById(Oid schemaOid); ! extern void RenameSchema(const char *oldname, const char *newname); ! extern void AlterSchemaOwner(const char *name, Oid newOwnerId); extern void AlterSchemaOwner_oid(Oid schemaOid, Oid newOwnerId); #endif /* SCHEMACMDS_H */ --- 22,29 ---- extern void RemoveSchemaById(Oid schemaOid); ! extern void RenameSchema(const char *oldname, const char *newname, CommandContext cmd); ! extern void AlterSchemaOwner(const char *name, Oid newOwnerId, CommandContext cmd); extern void AlterSchemaOwner_oid(Oid schemaOid, Oid newOwnerId); #endif /* SCHEMACMDS_H */ *** a/src/include/commands/tablecmds.h --- b/src/include/commands/tablecmds.h *************** *** 15,20 **** --- 15,21 ---- #define TABLECMDS_H #include "access/htup.h" + #include "commands/cmdtrigger.h" #include "nodes/parsenodes.h" #include "storage/lock.h" #include "utils/relcache.h" *************** *** 48,57 **** extern void SetRelationHasSubclass(Oid relationId, bool relhassubclass); extern void renameatt(RenameStmt *stmt); ! extern void RenameRelation(RenameStmt *stmt); extern void RenameRelationInternal(Oid myrelid, ! const char *newrelname); extern void find_composite_type_dependencies(Oid typeOid, Relation origRelation, --- 49,58 ---- extern void renameatt(RenameStmt *stmt); ! extern void RenameRelation(RenameStmt *stmt, CommandContext cmd); extern void RenameRelationInternal(Oid myrelid, ! const char *newrelname, CommandContext cmd); extern void find_composite_type_dependencies(Oid typeOid, Relation origRelation, *** a/src/include/commands/trigger.h --- b/src/include/commands/trigger.h *************** *** 13,18 **** --- 13,19 ---- #ifndef TRIGGER_H #define TRIGGER_H + #include "commands/cmdtrigger.h" #include "nodes/execnodes.h" #include "nodes/parsenodes.h" *************** *** 115,121 **** extern Oid CreateTrigger(CreateTrigStmt *stmt, const char *queryString, extern void RemoveTriggerById(Oid trigOid); extern Oid get_trigger_oid(Oid relid, const char *name, bool missing_ok); ! extern void renametrig(RenameStmt *stmt); extern void EnableDisableTrigger(Relation rel, const char *tgname, char fires_when, bool skip_system); --- 116,122 ---- extern void RemoveTriggerById(Oid trigOid); extern Oid get_trigger_oid(Oid relid, const char *name, bool missing_ok); ! extern void renametrig(RenameStmt *stmt, CommandContext cmd); extern void EnableDisableTrigger(Relation rel, const char *tgname, char fires_when, bool skip_system); *** a/src/include/commands/typecmds.h --- b/src/include/commands/typecmds.h *************** *** 14,50 **** #ifndef TYPECMDS_H #define TYPECMDS_H #include "nodes/parsenodes.h" #define DEFAULT_TYPDELIM ',' ! extern void DefineType(List *names, List *parameters); extern void RemoveTypeById(Oid typeOid); extern void DefineDomain(CreateDomainStmt *stmt); extern void DefineEnum(CreateEnumStmt *stmt); extern void DefineRange(CreateRangeStmt *stmt); extern void AlterEnum(AlterEnumStmt *stmt); ! extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist); extern Oid AssignTypeArrayOid(void); ! extern void AlterDomainDefault(List *names, Node *defaultRaw); ! extern void AlterDomainNotNull(List *names, bool notNull); ! extern void AlterDomainAddConstraint(List *names, Node *constr); ! extern void AlterDomainValidateConstraint(List *names, char *constrName); extern void AlterDomainDropConstraint(List *names, const char *constrName, ! DropBehavior behavior, bool missing_ok); extern List *GetDomainConstraints(Oid typeOid); ! extern void RenameType(RenameStmt *stmt); ! extern void AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype); extern void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId, bool hasDependEntry); ! extern void AlterTypeNamespace(List *names, const char *newschema, ObjectType objecttype); ! extern Oid AlterTypeNamespace_oid(Oid typeOid, Oid nspOid); extern Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, ! bool errorOnTableType); #endif /* TYPECMDS_H */ --- 14,56 ---- #ifndef TYPECMDS_H #define TYPECMDS_H + #include "commands/cmdtrigger.h" + #include "utils/lsyscache.h" #include "nodes/parsenodes.h" #define DEFAULT_TYPDELIM ',' ! extern void DefineType(List *names, List *parameters, CommandContext cmd); extern void RemoveTypeById(Oid typeOid); extern void DefineDomain(CreateDomainStmt *stmt); extern void DefineEnum(CreateEnumStmt *stmt); extern void DefineRange(CreateRangeStmt *stmt); extern void AlterEnum(AlterEnumStmt *stmt); ! extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist, CommandContext cmd); extern Oid AssignTypeArrayOid(void); ! extern void AlterDomainDefault(List *names, Node *defaultRaw, CommandContext cmd); ! extern void AlterDomainNotNull(List *names, bool notNull, CommandContext cmd); ! extern void AlterDomainAddConstraint(List *names, Node *constr, CommandContext cmd); ! extern void AlterDomainValidateConstraint(List *names, char *constrName, CommandContext cmd); extern void AlterDomainDropConstraint(List *names, const char *constrName, ! DropBehavior behavior, bool missing_ok, ! CommandContext cmd); extern List *GetDomainConstraints(Oid typeOid); ! extern void RenameType(RenameStmt *stmt, CommandContext cmd); ! extern void AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype, ! CommandContext cmd); extern void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId, bool hasDependEntry); ! extern void AlterTypeNamespace(List *names, const char *newschema, ! ObjectType objecttype, CommandContext cmd); ! extern Oid AlterTypeNamespace_oid(Oid typeOid, Oid nspOid, CommandContext cmd); extern Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, ! bool errorOnTableType, ! CommandContext cmd); #endif /* TYPECMDS_H */ *** a/src/include/nodes/nodes.h --- b/src/include/nodes/nodes.h *************** *** 290,295 **** typedef enum NodeTag --- 290,296 ---- T_IndexStmt, T_CreateFunctionStmt, T_AlterFunctionStmt, + T_RemoveFuncStmt, T_DoStmt, T_RenameStmt, T_RuleStmt, *************** *** 310,316 **** typedef enum NodeTag --- 311,319 ---- T_VariableShowStmt, T_DiscardStmt, T_CreateTrigStmt, + T_DropPropertyStmt, T_CreatePLangStmt, + T_DropPLangStmt, T_CreateRoleStmt, T_AlterRoleStmt, T_DropRoleStmt, *************** *** 324,332 **** typedef enum NodeTag --- 327,338 ---- T_AlterRoleSetStmt, T_CreateConversionStmt, T_CreateCastStmt, + T_DropCastStmt, T_CreateOpClassStmt, T_CreateOpFamilyStmt, T_AlterOpFamilyStmt, + T_RemoveOpClassStmt, + T_RemoveOpFamilyStmt, T_PrepareStmt, T_ExecuteStmt, T_DeallocateStmt, *************** *** 345,352 **** typedef enum NodeTag --- 351,360 ---- T_AlterTSConfigurationStmt, T_CreateFdwStmt, T_AlterFdwStmt, + T_DropFdwStmt, T_CreateForeignServerStmt, T_AlterForeignServerStmt, + T_DropForeignServerStmt, T_CreateUserMappingStmt, T_AlterUserMappingStmt, T_DropUserMappingStmt, *************** *** 356,361 **** typedef enum NodeTag --- 364,371 ---- T_CreateExtensionStmt, T_AlterExtensionStmt, T_AlterExtensionContentsStmt, + T_CreateCmdTrigStmt, + T_AlterCmdTrigStmt, /* * TAGS FOR PARSE TREE NODES (parsenodes.h) *** a/src/include/nodes/parsenodes.h --- b/src/include/nodes/parsenodes.h *************** *** 1107,1112 **** typedef enum ObjectType --- 1107,1113 ---- OBJECT_AGGREGATE, OBJECT_ATTRIBUTE, /* type's attribute, when distinct from column */ OBJECT_CAST, + OBJECT_CMDTRIGGER, OBJECT_COLUMN, OBJECT_CONSTRAINT, OBJECT_COLLATION, *************** *** 1729,1734 **** typedef struct CreateTrigStmt --- 1730,1763 ---- } CreateTrigStmt; /* ---------------------- + * Create COMMAND TRIGGER Statement + * ---------------------- + */ + typedef struct CreateCmdTrigStmt + { + NodeTag type; + List *command; /* commands name */ + char *trigname; /* TRIGGER's name */ + /* timing uses the TRIGGER_TYPE bits defined in catalog/pg_trigger.h */ + char timing; /* BEFORE, AFTER */ + List *funcname; /* qual. name of function to call */ + } CreateCmdTrigStmt; + + /* ---------------------- + * Alter COMMAND TRIGGER Statement + * ---------------------- + */ + typedef struct AlterCmdTrigStmt + { + NodeTag type; + char *command; /* command's name */ + char *trigname; /* TRIGGER's name */ + char *tgenabled; /* trigger's firing configuration WRT + * session_replication_role */ + } AlterCmdTrigStmt; + + /* ---------------------- + * Create/Drop PROCEDURAL LANGUAGE Statements * Create PROCEDURAL LANGUAGE Statements * ---------------------- */ *************** *** 1911,1916 **** typedef struct DropStmt --- 1940,1963 ---- } DropStmt; /* ---------------------- + * Drop Rule|Trigger Statement + * + * In general this may be used for dropping any property of a relation; + * for example, someday soon we may have DROP ATTRIBUTE. + * ---------------------- + */ + + typedef struct DropPropertyStmt + { + NodeTag type; + RangeVar *relation; /* owning relation */ + char *property; /* name of rule, trigger, etc */ + ObjectType removeType; /* OBJECT_RULE or OBJECT_TRIGGER */ + DropBehavior behavior; /* RESTRICT or CASCADE behavior */ + bool missing_ok; /* skip error if missing? */ + } DropPropertyStmt; + + /* ---------------------- * Truncate Table Statement * ---------------------- */ *** a/src/include/parser/kwlist.h --- b/src/include/parser/kwlist.h *************** *** 81,86 **** PG_KEYWORD("coalesce", COALESCE, COL_NAME_KEYWORD) --- 81,87 ---- PG_KEYWORD("collate", COLLATE, RESERVED_KEYWORD) PG_KEYWORD("collation", COLLATION, UNRESERVED_KEYWORD) PG_KEYWORD("column", COLUMN, RESERVED_KEYWORD) + PG_KEYWORD("command", COMMAND, UNRESERVED_KEYWORD) PG_KEYWORD("comment", COMMENT, UNRESERVED_KEYWORD) PG_KEYWORD("comments", COMMENTS, UNRESERVED_KEYWORD) PG_KEYWORD("commit", COMMIT, UNRESERVED_KEYWORD) *** a/src/include/rewrite/rewriteDefine.h --- b/src/include/rewrite/rewriteDefine.h *************** *** 14,19 **** --- 14,20 ---- #ifndef REWRITEDEFINE_H #define REWRITEDEFINE_H + #include "commands/cmdtrigger.h" #include "nodes/parsenodes.h" #include "utils/relcache.h" *************** *** 30,36 **** extern void DefineQueryRewrite(char *rulename, CmdType event_type, bool is_instead, bool replace, ! List *action); extern void RenameRewriteRule(Oid owningRel, const char *oldName, const char *newName); --- 31,38 ---- CmdType event_type, bool is_instead, bool replace, ! List *action, ! CommandContext cmd); extern void RenameRewriteRule(Oid owningRel, const char *oldName, const char *newName); *** /dev/null --- b/src/test/regress/expected/cmdtriggers.out *************** *** 0 **** --- 1,283 ---- + -- + -- COMMAND TRIGGERS + -- + create or replace function snitch + (in tg_when text, in cmd_tag text, in objectid oid, in schemaname text, in objectname text) + returns void language plpgsql + as $$ + begin + -- can't output the objectid here that would break pg_regress + raise notice 'snitch: % % % %', tg_when, cmd_tag, schemaname, objectname; + end; + $$; + create command trigger snitch_before before any command execute procedure snitch(); + create command trigger snitch_after after any command execute procedure snitch(); + alter command trigger snitch_before on any command set disable; + alter command trigger snitch_before on any command set enable; + create command trigger snitch_some_more + after create table, alter table, drop table, + create function, create collation, + alter operator, create domain, alter schema + execute procedure snitch(); + create command trigger snitch_some_even_more + before create trigger, alter trigger, drop trigger, + create schema, drop schema, + create aggregate, alter collation, create operator, + alter domain, create type, alter type + execute procedure snitch(); + create schema cmd; + NOTICE: snitch: BEFORE CREATE SCHEMA + NOTICE: snitch: BEFORE CREATE SCHEMA + NOTICE: snitch: BEFORE CREATE SCHEMA cmd + NOTICE: snitch: AFTER CREATE SCHEMA + NOTICE: snitch: AFTER CREATE SCHEMA + NOTICE: snitch: AFTER CREATE SCHEMA + create table cmd.foo(id bigserial primary key); + NOTICE: snitch: BEFORE CREATE TABLE + NOTICE: snitch: BEFORE CREATE TABLE + NOTICE: CREATE TABLE will create implicit sequence "foo_id_seq" for serial column "foo.id" + NOTICE: snitch: BEFORE CREATE SEQUENCE + NOTICE: snitch: BEFORE CREATE SEQUENCE + NOTICE: snitch: AFTER CREATE SEQUENCE + NOTICE: snitch: AFTER CREATE SEQUENCE + NOTICE: snitch: AFTER CREATE SEQUENCE + NOTICE: snitch: BEFORE CREATE INDEX + NOTICE: snitch: BEFORE CREATE INDEX + NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo" + NOTICE: snitch: AFTER CREATE INDEX + NOTICE: snitch: AFTER CREATE INDEX + NOTICE: snitch: BEFORE ALTER SEQUENCE + NOTICE: snitch: BEFORE ALTER SEQUENCE + NOTICE: snitch: AFTER ALTER SEQUENCE + NOTICE: snitch: AFTER ALTER SEQUENCE + NOTICE: snitch: AFTER ALTER SEQUENCE + NOTICE: snitch: AFTER CREATE TABLE cmd foo + NOTICE: snitch: AFTER CREATE TABLE + NOTICE: snitch: AFTER CREATE TABLE + create view cmd.v as select * from cmd.foo; + NOTICE: snitch: BEFORE CREATE VIEW + NOTICE: snitch: BEFORE CREATE VIEW + NOTICE: snitch: AFTER CREATE VIEW + NOTICE: snitch: AFTER CREATE VIEW + NOTICE: snitch: AFTER CREATE VIEW + alter table cmd.foo add column t text; + NOTICE: snitch: BEFORE ALTER TABLE + NOTICE: snitch: BEFORE ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE cmd foo + NOTICE: snitch: AFTER ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE + cluster cmd.foo using foo_pkey; + NOTICE: snitch: BEFORE CLUSTER + NOTICE: snitch: BEFORE CLUSTER + vacuum cmd.foo; + NOTICE: snitch: BEFORE VACUUM + NOTICE: snitch: BEFORE VACUUM + vacuum; + NOTICE: snitch: BEFORE VACUUM + NOTICE: snitch: BEFORE VACUUM + set session_replication_role to replica; + create table cmd.bar(); + reset session_replication_role; + create index idx_foo on cmd.foo(t); + NOTICE: snitch: BEFORE CREATE INDEX + NOTICE: snitch: BEFORE CREATE INDEX + NOTICE: snitch: AFTER CREATE INDEX + NOTICE: snitch: AFTER CREATE INDEX + drop index cmd.idx_foo; + NOTICE: snitch: BEFORE DROP INDEX + NOTICE: snitch: AFTER DROP INDEX + create function cmd.fun(int) returns text language sql + as $$ select t from cmd.foo where id = $1; $$; + NOTICE: snitch: BEFORE CREATE FUNCTION + NOTICE: snitch: BEFORE CREATE FUNCTION + NOTICE: snitch: AFTER CREATE FUNCTION cmd fun + NOTICE: snitch: AFTER CREATE FUNCTION + NOTICE: snitch: AFTER CREATE FUNCTION + NOTICE: snitch: AFTER CREATE FUNCTION + alter function cmd.fun(int) strict; + NOTICE: snitch: BEFORE ALTER FUNCTION + NOTICE: snitch: BEFORE ALTER FUNCTION + NOTICE: snitch: AFTER ALTER FUNCTION + NOTICE: snitch: AFTER ALTER FUNCTION + NOTICE: snitch: AFTER ALTER FUNCTION + alter function cmd.fun(int) rename to notfun; + NOTICE: snitch: BEFORE ALTER FUNCTION + NOTICE: snitch: BEFORE ALTER FUNCTION + NOTICE: snitch: AFTER ALTER FUNCTION + NOTICE: snitch: AFTER ALTER FUNCTION + NOTICE: snitch: AFTER ALTER FUNCTION + drop function cmd.notfun(int); + NOTICE: snitch: BEFORE DROP FUNCTION + NOTICE: snitch: AFTER DROP FUNCTION + create function cmd.plus1(int) returns bigint language sql + as $$ select $1::bigint + 1; $$; + NOTICE: snitch: BEFORE CREATE FUNCTION + NOTICE: snitch: BEFORE CREATE FUNCTION + NOTICE: snitch: AFTER CREATE FUNCTION cmd plus1 + NOTICE: snitch: AFTER CREATE FUNCTION + NOTICE: snitch: AFTER CREATE FUNCTION + NOTICE: snitch: AFTER CREATE FUNCTION + create operator cmd.+!(procedure = cmd.plus1, leftarg = int); + NOTICE: snitch: BEFORE CREATE OPERATOR + NOTICE: snitch: BEFORE CREATE OPERATOR + NOTICE: snitch: BEFORE CREATE OPERATOR cmd +! + NOTICE: snitch: AFTER CREATE OPERATOR + NOTICE: snitch: AFTER CREATE OPERATOR + NOTICE: snitch: AFTER CREATE OPERATOR + alter operator cmd.+!(int, NONE) set schema public; + NOTICE: snitch: BEFORE ALTER OPERATOR + NOTICE: snitch: BEFORE ALTER OPERATOR + NOTICE: snitch: AFTER ALTER OPERATOR public +! + NOTICE: snitch: AFTER ALTER OPERATOR + NOTICE: snitch: AFTER ALTER OPERATOR + NOTICE: snitch: AFTER ALTER OPERATOR + drop operator public.+!(int, NONE); + NOTICE: snitch: BEFORE DROP OPERATOR + NOTICE: snitch: AFTER DROP OPERATOR + create aggregate cmd.avg (float8) + ( + sfunc = float8_accum, + stype = float8[], + finalfunc = float8_avg, + initcond = '{0,0,0}' + ); + NOTICE: snitch: BEFORE CREATE AGGREGATE + NOTICE: snitch: BEFORE CREATE AGGREGATE + NOTICE: snitch: BEFORE CREATE AGGREGATE cmd avg + NOTICE: snitch: AFTER CREATE AGGREGATE + NOTICE: snitch: AFTER CREATE AGGREGATE + NOTICE: snitch: AFTER CREATE AGGREGATE + alter aggregate cmd.avg(float8) set schema public; + NOTICE: snitch: BEFORE ALTER AGGREGATE + NOTICE: snitch: BEFORE ALTER AGGREGATE + NOTICE: snitch: AFTER ALTER AGGREGATE + NOTICE: snitch: AFTER ALTER AGGREGATE + NOTICE: snitch: AFTER ALTER AGGREGATE + drop aggregate public.avg(float8); + NOTICE: snitch: BEFORE DROP AGGREGATE + NOTICE: snitch: AFTER DROP AGGREGATE + create collation cmd.french (LOCALE = 'fr_FR'); + NOTICE: snitch: BEFORE CREATE COLLATION + NOTICE: snitch: BEFORE CREATE COLLATION + NOTICE: snitch: AFTER CREATE COLLATION cmd french + NOTICE: snitch: AFTER CREATE COLLATION + NOTICE: snitch: AFTER CREATE COLLATION + NOTICE: snitch: AFTER CREATE COLLATION + alter collation cmd.french rename to francais; + NOTICE: snitch: BEFORE ALTER COLLATION + NOTICE: snitch: BEFORE ALTER COLLATION + NOTICE: snitch: BEFORE ALTER COLLATION cmd french + NOTICE: snitch: AFTER ALTER COLLATION + NOTICE: snitch: AFTER ALTER COLLATION + NOTICE: snitch: AFTER ALTER COLLATION + create type cmd.compfoo AS (f1 int, f2 text); + NOTICE: snitch: BEFORE CREATE TYPE + NOTICE: snitch: BEFORE CREATE TYPE + NOTICE: snitch: BEFORE CREATE TYPE cmd compfoo + NOTICE: snitch: AFTER CREATE TYPE + NOTICE: snitch: AFTER CREATE TYPE + NOTICE: snitch: AFTER CREATE TYPE + alter type cmd.compfoo add attribute f3 text; + NOTICE: snitch: BEFORE ALTER TYPE + NOTICE: snitch: BEFORE ALTER TYPE + NOTICE: snitch: BEFORE ALTER TYPE cmd compfoo + NOTICE: snitch: AFTER ALTER TYPE + NOTICE: snitch: AFTER ALTER TYPE + NOTICE: snitch: AFTER ALTER TYPE + drop type cmd.compfoo; + NOTICE: snitch: BEFORE DROP TYPE + NOTICE: snitch: AFTER DROP TYPE + create type cmd.bug_status as enum ('new', 'open', 'closed'); + NOTICE: snitch: BEFORE CREATE TYPE + NOTICE: snitch: BEFORE CREATE TYPE + NOTICE: snitch: BEFORE CREATE TYPE cmd bug_status + NOTICE: snitch: AFTER CREATE TYPE + NOTICE: snitch: AFTER CREATE TYPE + NOTICE: snitch: AFTER CREATE TYPE + alter type cmd.bug_status add value 'wontfix'; + NOTICE: snitch: BEFORE ALTER TYPE + NOTICE: snitch: BEFORE ALTER TYPE + NOTICE: snitch: AFTER ALTER TYPE + NOTICE: snitch: AFTER ALTER TYPE + NOTICE: snitch: AFTER ALTER TYPE + create domain cmd.us_postal_code as text check(value ~ '^\d{5}$' or value ~ '^\d{5}-\d{4}$'); + NOTICE: snitch: BEFORE CREATE DOMAIN + NOTICE: snitch: BEFORE CREATE DOMAIN + NOTICE: snitch: AFTER CREATE DOMAIN cmd us_postal_code + NOTICE: snitch: AFTER CREATE DOMAIN + NOTICE: snitch: AFTER CREATE DOMAIN + NOTICE: snitch: AFTER CREATE DOMAIN + alter domain cmd.us_postal_code set not null; + NOTICE: snitch: BEFORE ALTER DOMAIN + NOTICE: snitch: BEFORE ALTER DOMAIN + NOTICE: snitch: BEFORE ALTER DOMAIN cmd us_postal_code + NOTICE: snitch: AFTER ALTER DOMAIN + NOTICE: snitch: AFTER ALTER DOMAIN + NOTICE: snitch: AFTER ALTER DOMAIN + create function cmd.trigfunc() returns trigger language plpgsql as + $$ begin raise notice 'trigfunc'; end;$$; + NOTICE: snitch: BEFORE CREATE FUNCTION + NOTICE: snitch: BEFORE CREATE FUNCTION + NOTICE: snitch: AFTER CREATE FUNCTION cmd trigfunc + NOTICE: snitch: AFTER CREATE FUNCTION + NOTICE: snitch: AFTER CREATE FUNCTION + NOTICE: snitch: AFTER CREATE FUNCTION + create trigger footg before update on cmd.foo for each row execute procedure cmd.trigfunc(); + NOTICE: snitch: BEFORE CREATE TRIGGER + NOTICE: snitch: BEFORE CREATE TRIGGER + NOTICE: snitch: BEFORE CREATE TRIGGER cmd footg + NOTICE: snitch: AFTER CREATE TRIGGER + NOTICE: snitch: AFTER CREATE TRIGGER + alter trigger footg on cmd.foo rename to foo_trigger; + NOTICE: snitch: BEFORE ALTER TRIGGER + NOTICE: snitch: BEFORE ALTER TRIGGER + NOTICE: snitch: BEFORE ALTER TRIGGER cmd footg + NOTICE: snitch: AFTER ALTER TRIGGER + NOTICE: snitch: AFTER ALTER TRIGGER + NOTICE: snitch: AFTER ALTER TRIGGER + drop trigger foo_trigger on cmd.foo; + NOTICE: snitch: BEFORE DROP TRIGGER + NOTICE: snitch: BEFORE DROP TRIGGER foo_trigger + NOTICE: snitch: AFTER DROP TRIGGER + alter schema cmd rename to cmd1; + NOTICE: snitch: BEFORE ALTER SCHEMA + NOTICE: snitch: BEFORE ALTER SCHEMA + NOTICE: snitch: AFTER ALTER SCHEMA cmd1 + NOTICE: snitch: AFTER ALTER SCHEMA + NOTICE: snitch: AFTER ALTER SCHEMA + NOTICE: snitch: AFTER ALTER SCHEMA + drop schema cmd1 cascade; + NOTICE: snitch: BEFORE DROP SCHEMA + NOTICE: snitch: BEFORE DROP SCHEMA cmd1 + NOTICE: drop cascades to 8 other objects + DETAIL: drop cascades to table cmd1.foo + drop cascades to view cmd1.v + drop cascades to table cmd1.bar + drop cascades to function cmd1.plus1(integer) + drop cascades to collation francais + drop cascades to type cmd1.bug_status + drop cascades to type cmd1.us_postal_code + drop cascades to function cmd1.trigfunc() + NOTICE: snitch: AFTER DROP SCHEMA + drop command trigger snitch_before on any command; + drop command trigger snitch_after on any command; + drop command trigger snitch_some_more on create table; + drop command trigger snitch_some_more on alter table; + drop command trigger snitch_some_more on drop table; + drop command trigger snitch_some_more on create function; + drop command trigger snitch_some_more on create collation; + drop command trigger snitch_some_more on alter operator; + drop command trigger snitch_some_more on create domain; + drop command trigger snitch_some_more on alter schema; + drop command trigger snitch_even_more on create trigger; + drop command trigger snitch_even_more on alter trigger; + drop command trigger snitch_even_more on drop trigger; + drop command trigger snitch_even_more on create schema; + drop command trigger snitch_even_more on drop schema; + drop command trigger snitch_even_more on create aggregate; + drop command trigger snitch_even_more on alter collation; + drop command trigger snitch_even_more on create operator; + drop command trigger snitch_even_more on alter domain; + drop command trigger snitch_even_more on create type; + drop command trigger snitch_even_more on alter type; *** a/src/test/regress/expected/sanity_check.out --- b/src/test/regress/expected/sanity_check.out *************** *** 93,98 **** SELECT relname, relhasindex --- 93,99 ---- pg_authid | t pg_cast | t pg_class | t + pg_cmdtrigger | t pg_collation | t pg_constraint | t pg_conversion | t *************** *** 164,170 **** SELECT relname, relhasindex timetz_tbl | f tinterval_tbl | f varchar_tbl | f ! (153 rows) -- -- another sanity check: every system catalog that has OIDs should have --- 165,171 ---- timetz_tbl | f tinterval_tbl | f varchar_tbl | f ! (154 rows) -- -- another sanity check: every system catalog that has OIDs should have *** a/src/test/regress/serial_schedule --- b/src/test/regress/serial_schedule *************** *** 62,67 **** test: create_aggregate --- 62,68 ---- test: create_cast test: constraints test: triggers + test: cmdtriggers test: inherit test: create_table_like test: typed_table *** /dev/null --- b/src/test/regress/sql/cmdtriggers.sql *************** *** 0 **** --- 1,119 ---- + -- + -- COMMAND TRIGGERS + -- + create or replace function snitch + (in tg_when text, in cmd_tag text, in objectid oid, in schemaname text, in objectname text) + returns void language plpgsql + as $$ + begin + -- can't output the objectid here that would break pg_regress + raise notice 'snitch: % % % %', tg_when, cmd_tag, schemaname, objectname; + end; + $$; + + create command trigger snitch_before before any command execute procedure snitch(); + create command trigger snitch_after after any command execute procedure snitch(); + + alter command trigger snitch_before on any command set disable; + alter command trigger snitch_before on any command set enable; + + create command trigger snitch_some_more + after create table, alter table, drop table, + create function, create collation, + alter operator, create domain, alter schema + execute procedure snitch(); + + create command trigger snitch_some_even_more + before create trigger, alter trigger, drop trigger, + create schema, drop schema, + create aggregate, alter collation, create operator, + alter domain, create type, alter type + execute procedure snitch(); + + create schema cmd; + create table cmd.foo(id bigserial primary key); + create view cmd.v as select * from cmd.foo; + alter table cmd.foo add column t text; + + cluster cmd.foo using foo_pkey; + vacuum cmd.foo; + vacuum; + + set session_replication_role to replica; + create table cmd.bar(); + reset session_replication_role; + + create index idx_foo on cmd.foo(t); + drop index cmd.idx_foo; + + create function cmd.fun(int) returns text language sql + as $$ select t from cmd.foo where id = $1; $$; + + alter function cmd.fun(int) strict; + alter function cmd.fun(int) rename to notfun; + drop function cmd.notfun(int); + + create function cmd.plus1(int) returns bigint language sql + as $$ select $1::bigint + 1; $$; + + create operator cmd.+!(procedure = cmd.plus1, leftarg = int); + alter operator cmd.+!(int, NONE) set schema public; + drop operator public.+!(int, NONE); + + create aggregate cmd.avg (float8) + ( + sfunc = float8_accum, + stype = float8[], + finalfunc = float8_avg, + initcond = '{0,0,0}' + ); + alter aggregate cmd.avg(float8) set schema public; + drop aggregate public.avg(float8); + + create collation cmd.french (LOCALE = 'fr_FR'); + alter collation cmd.french rename to francais; + + create type cmd.compfoo AS (f1 int, f2 text); + alter type cmd.compfoo add attribute f3 text; + drop type cmd.compfoo; + + create type cmd.bug_status as enum ('new', 'open', 'closed'); + alter type cmd.bug_status add value 'wontfix'; + + create domain cmd.us_postal_code as text check(value ~ '^\d{5}$' or value ~ '^\d{5}-\d{4}$'); + alter domain cmd.us_postal_code set not null; + + create function cmd.trigfunc() returns trigger language plpgsql as + $$ begin raise notice 'trigfunc'; end;$$; + + create trigger footg before update on cmd.foo for each row execute procedure cmd.trigfunc(); + alter trigger footg on cmd.foo rename to foo_trigger; + drop trigger foo_trigger on cmd.foo; + + alter schema cmd rename to cmd1; + + drop schema cmd1 cascade; + + drop command trigger snitch_before on any command; + drop command trigger snitch_after on any command; + + drop command trigger snitch_some_more on create table; + drop command trigger snitch_some_more on alter table; + drop command trigger snitch_some_more on drop table; + drop command trigger snitch_some_more on create function; + drop command trigger snitch_some_more on create collation; + drop command trigger snitch_some_more on alter operator; + drop command trigger snitch_some_more on create domain; + drop command trigger snitch_some_more on alter schema; + drop command trigger snitch_even_more on create trigger; + drop command trigger snitch_even_more on alter trigger; + drop command trigger snitch_even_more on drop trigger; + drop command trigger snitch_even_more on create schema; + drop command trigger snitch_even_more on drop schema; + drop command trigger snitch_even_more on create aggregate; + drop command trigger snitch_even_more on alter collation; + drop command trigger snitch_even_more on create operator; + drop command trigger snitch_even_more on alter domain; + drop command trigger snitch_even_more on create type; + drop command trigger snitch_even_more on alter type; + *** a/src/test/regress/sql/triggers.sql --- b/src/test/regress/sql/triggers.sql *************** *** 962,968 **** SELECT * FROM city_view; DROP TABLE city_table CASCADE; DROP TABLE country_table; - -- Test pg_trigger_depth() create table depth_a (id int not null primary key); --- 962,967 ----