*** 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,103 ---- + + + + + ALTER COMMAND TRIGGER + 7 + SQL - Language Statements + + + + ALTER COMMAND TRIGGER + change the definition of a trigger + + + + ALTER COMMAND TRIGGER + + + + + ALTER COMMAND TRIGGER name SET enabled + ALTER COMMAND TRIGGER name RENAME TO newname + + where enabled can be one of: + + ENABLE + ENABLE ALWAYS + ENABLE REPLICA + DISABLE + + and where command can be one of the same list as in . + + + + + + Description + + + ALTER 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. + + + + + + newname + + + The new name of the command trigger. + + + + + + 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,330 ---- + + + + + 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: + + ALTER AGGREGATE + ALTER CAST + ALTER COLLATION + ALTER CONVERSION + ALTER DOMAIN + ALTER EXTENSION + ALTER FOREIGN DATA WRAPPER + ALTER FOREIGN TABLE + ALTER FUNCTION + ALTER LANGUAGE + ALTER OPERATOR + ALTER OPERATOR CLASS + ALTER OPERATOR FAMILY + ALTER SCHEMA + ALTER SEQUENCE + ALTER SERVER + ALTER TABLE + ALTER TEXT SEARCH CONFIGURATION + ALTER TEXT SEARCH DICTIONARY + ALTER TEXT SEARCH PARSER + ALTER TEXT SEARCH TEMPLATE + ALTER TRIGGER + ALTER TYPE + ALTER USER MAPPING + ALTER VIEW + CLUSTER + CREATE AGGREGATE + CREATE COLLATION + CREATE CONVERSION + CREATE DOMAIN + CREATE EXTENSION + CREATE FOREIGN DATA WRAPPER + CREATE FOREIGN TABLE + CREATE FUNCTION + CREATE INDEX + CREATE LANGUAGE + CREATE OPERATOR + CREATE OPERATOR CLASS + CREATE OPERATOR FAMILY + CREATE RULE + CREATE SCHEMA + CREATE SEQUENCE + CREATE SERVER + CREATE TABLE + CREATE TEXT SEARCH CONFIGURATION + CREATE TEXT SEARCH DICTIONARY + CREATE TEXT SEARCH PARSER + CREATE TEXT SEARCH TEMPLATE + CREATE TRIGGER + CREATE TYPE + CREATE USER MAPPING + CREATE VIEW + DROP AGGREGATE + DROP CAST + DROP COLLATION + DROP CONVERSION + DROP DOMAIN + DROP EXTENSION + DROP FOREIGN DATA WRAPPER + DROP FOREIGN TABLE + DROP FUNCTION + DROP INDEX + DROP LANGUAGE + DROP OPERATOR + DROP OPERATOR CLASS + DROP OPERATOR FAMILY + DROP RULE + DROP SCHEMA + DROP SEQUENCE + DROP SERVER + DROP TABLE + DROP TEXT SEARCH CONFIGURATION + DROP TEXT SEARCH DICTIONARY + DROP TEXT SEARCH PARSER + DROP TEXT SEARCH TEMPLATE + DROP TRIGGER + DROP TYPE + DROP USER MAPPING + DROP VIEW + LOAD + REINDEX + VACUUM + + + + + + 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. It can then only abort the execution of + the command by raising an exception. + + + + Note that objects dropped by the effect of DROP + CASCADE will not result in a command trigger firing, only the + top-level command for the main object will fire a command trigger. That + also applies to other dependencies following, as in DROP OWNED + BY. + + + + 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. + + + + + + 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 exercise 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 provide NULL values for every argument except for the argument + that determines whether the trigger was before or after the command + event, and the command tag. + + 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 object name (can be null + for any command triggers). + + + + + + + + + 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,120 ---- + + + + + DROP COMMAND TRIGGER + 7 + SQL - Language Statements + + + + DROP COMMAND TRIGGER + remove a command trigger + + + + DROP COMMAND TRIGGER + + + + + DROP COMMAND TRIGGER [ IF EXISTS ] name [ CASCADE | RESTRICT ] + + + + + 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 INDEX command triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = indexId; + cmd->objectname = RelationGetRelationName(iRel); + cmd->schemaname = get_namespace_name(RelationGetNamespace(iRel)); + + 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 INDEX 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->schemaname = 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, 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, + missing_ok); + break; case OBJECT_DATABASE: case OBJECT_EXTENSION: case OBJECT_TABLESPACE: *************** *** 902,907 **** get_object_address_opcf(ObjectType objtype, --- 916,941 ---- } /* + * Find the ObjectAddress for a command trigger. + */ + static ObjectAddress + get_object_address_cmdtrigger(ObjectType objtype, List *objname, bool missing_ok) + { + char *name; + ObjectAddress address; + + Assert(list_length(objname) == 1); /* command triggers are not schema qualified */ + + name = strVal(linitial(objname)); + + address.classId = CmdTriggerRelationId; + address.objectId = get_cmdtrigger_oid(name, 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, --- 1088,1094 ---- 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 = pstrdup(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 = pstrdup(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->newname); break; case OBJECT_DATABASE: *************** *** 66,92 **** ExecRenameStmt(RenameStmt *stmt) break; case OBJECT_FDW: ! RenameForeignDataWrapper(stmt->subname, stmt->newname); break; case OBJECT_FOREIGN_SERVER: ! RenameForeignServer(stmt->subname, stmt->newname); break; case OBJECT_FUNCTION: ! RenameFunction(stmt->object, stmt->objarg, stmt->newname); break; case OBJECT_LANGUAGE: ! RenameLanguage(stmt->subname, stmt->newname); break; case OBJECT_OPCLASS: ! RenameOpClass(stmt->object, stmt->subname, stmt->newname); break; case OBJECT_OPFAMILY: ! RenameOpFamily(stmt->object, stmt->subname, stmt->newname); break; case OBJECT_ROLE: --- 74,100 ---- break; case OBJECT_FDW: ! RenameForeignDataWrapper(stmt->subname, stmt->newname, &cmd); break; case OBJECT_FOREIGN_SERVER: ! RenameForeignServer(stmt->subname, stmt->newname, &cmd); break; case OBJECT_FUNCTION: ! RenameFunction(stmt->object, stmt->objarg, stmt->newname, &cmd); break; case OBJECT_LANGUAGE: ! RenameLanguage(stmt->subname, stmt->newname, &cmd); break; case OBJECT_OPCLASS: ! RenameOpClass(stmt->object, stmt->subname, stmt->newname, &cmd); break; case OBJECT_OPFAMILY: ! RenameOpFamily(stmt->object, stmt->subname, stmt->newname, &cmd); break; case OBJECT_ROLE: *************** *** 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,142 **** ExecRenameStmt(RenameStmt *stmt) case OBJECT_VIEW: case OBJECT_INDEX: case OBJECT_FOREIGN_TABLE: ! RenameRelation(stmt); break; case OBJECT_COLUMN: case OBJECT_ATTRIBUTE: ! renameatt(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: --- 114,150 ---- case OBJECT_VIEW: case OBJECT_INDEX: case OBJECT_FOREIGN_TABLE: ! RenameRelation(stmt, &cmd); break; case OBJECT_COLUMN: case OBJECT_ATTRIBUTE: ! renameatt(stmt, &cmd); 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 = pstrdup(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,483 **** ExecAlterOwnerStmt(AlterOwnerStmt *stmt) break; case OBJECT_FUNCTION: ! AlterFunctionOwner(stmt->object, stmt->objarg, newowner); break; case OBJECT_LANGUAGE: ! AlterLanguageOwner(strVal(linitial(stmt->object)), newowner); break; case OBJECT_LARGEOBJECT: --- 506,516 ---- break; case OBJECT_FUNCTION: ! AlterFunctionOwner(stmt->object, stmt->objarg, newowner, &cmd); break; case OBJECT_LANGUAGE: ! AlterLanguageOwner(strVal(linitial(stmt->object)), newowner, &cmd); break; case OBJECT_LARGEOBJECT: *************** *** 489,507 **** ExecAlterOwnerStmt(AlterOwnerStmt *stmt) AlterOperatorOwner(stmt->object, (TypeName *) linitial(stmt->objarg), (TypeName *) lsecond(stmt->objarg), ! newowner); break; case OBJECT_OPCLASS: ! AlterOpClassOwner(stmt->object, stmt->addname, newowner); break; case OBJECT_OPFAMILY: ! AlterOpFamilyOwner(stmt->object, stmt->addname, newowner); break; case OBJECT_SCHEMA: ! AlterSchemaOwner(strVal(linitial(stmt->object)), newowner); break; case OBJECT_TABLESPACE: --- 522,540 ---- AlterOperatorOwner(stmt->object, (TypeName *) linitial(stmt->objarg), (TypeName *) lsecond(stmt->objarg), ! newowner, &cmd); break; case OBJECT_OPCLASS: ! AlterOpClassOwner(stmt->object, stmt->addname, newowner, &cmd); break; case OBJECT_OPFAMILY: ! AlterOpFamilyOwner(stmt->object, stmt->addname, newowner, &cmd); break; case OBJECT_SCHEMA: ! AlterSchemaOwner(strVal(linitial(stmt->object)), newowner, &cmd); break; case OBJECT_TABLESPACE: *************** *** 510,533 **** 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: AlterForeignDataWrapperOwner(strVal(linitial(stmt->object)), ! newowner); break; case OBJECT_FOREIGN_SERVER: ! AlterForeignServerOwner(strVal(linitial(stmt->object)), newowner); break; default: --- 543,566 ---- 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: AlterForeignDataWrapperOwner(strVal(linitial(stmt->object)), ! newowner, &cmd); break; case OBJECT_FOREIGN_SERVER: ! AlterForeignServerOwner(strVal(linitial(stmt->object)), newowner, &cmd); break; default: *** 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 *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; + /* 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, trigoid; + 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); + + /* + * 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(stmt->trigname, tgrel); + + /* + * 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(stmt->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(stmt->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(stmt->command, "CREATE INDEX") == 0)) + ereport(WARNING, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("AFTER CREATE INDEX CONCURRENTLY triggers are not supported"), + errdetail("The command trigger will not fire on concurrently-created indexes."))); + + if (strcmp(stmt->command, "REINDEX") == 0) + ereport(WARNING, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("REINDEX DATABASE triggers are not supported"), + errdetail("The command trigger will not fire on REINDEX DATABASE."))); + + if (funcrettype != VOIDOID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("function \"%s\" must return type \"void\"", + NameListToString(stmt->funcname)))); + + trigoid = InsertCmdTriggerTuple(tgrel, stmt->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[1]; + 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_ctgname, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(stmt->trigname)); + + tgscan = systable_beginscan(tgrel, CmdTriggerNameIndexId, true, + SnapshotNow, 1, skey); + + tup = systable_getnext(tgscan); + + if (!HeapTupleIsValid(tup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("trigger \"%s\" does not exist, skipping", + stmt->trigname))); + + /* 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 *newname) + { + SysScanDesc tgscan; + ScanKeyData skey[1]; + HeapTuple tup; + Relation rel; + Form_pg_cmdtrigger cmdForm; + char *trigname; + + Assert(list_length(name) == 1); + trigname = strVal((Value *)linitial(name)); + + CheckCmdTriggerPrivileges(); + + rel = heap_open(CmdTriggerRelationId, RowExclusiveLock); + + /* newname must be available */ + check_cmdtrigger_name(newname, rel); + + /* get existing tuple */ + ScanKeyInit(&skey[0], + Anum_pg_cmdtrigger_ctgname, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(trigname)); + + tgscan = systable_beginscan(rel, CmdTriggerNameIndexId, true, + SnapshotNow, 1, skey); + + tup = systable_getnext(tgscan); + + if (!HeapTupleIsValid(tup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("command trigger \"%s\" does not exist, skipping", + trigname))); + + /* 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, bool missing_ok) + { + Relation tgrel; + ScanKeyData skey[1]; + 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_ctgname, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(trigname)); + + tgscan = systable_beginscan(tgrel, CmdTriggerNameIndexId, true, + SnapshotNow, 1, skey); + + tup = systable_getnext(tgscan); + + if (!HeapTupleIsValid(tup)) + { + if (!missing_ok) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("command trigger \"%s\" does not exist, skipping", + trigname))); + 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 *trigname, Relation tgrel) + { + SysScanDesc tgscan; + ScanKeyData skey[1]; + HeapTuple tuple; + + ScanKeyInit(&skey[0], + Anum_pg_cmdtrigger_ctgname, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(trigname)); + + tgscan = systable_beginscan(tgrel, CmdTriggerNameIndexId, true, + SnapshotNow, 1, skey); + + tuple = systable_getnext(tgscan); + + if (HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("command trigger \"%s\" already exists", trigname))); + 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. + */ + static 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) + { + FmgrInfo flinfo; + FunctionCallInfoData fcinfo; + PgStat_FunctionCallUsage fcusage; + Datum result; + HeapTuple procedureTuple; + Form_pg_proc procedureStruct; + int nargs = 5; + + fmgr_info(proc, &flinfo); + + /* + * 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) + { + ListCell *cell; + + foreach(cell, procs) + { + Oid proc = lfirst_oid(cell); + call_cmdtrigger_procedure(cmd, (RegProcedure)proc, when); + } + } + + /* + * 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; + cmd->oldmctx = NULL; + cmd->cmdmctx = NULL; + + 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. + * + * Integration is always on the form: + * + * if (CommandFiresTriggers(cmd) + * { + * cmd->objectname = pstrdup(...); + * ... + * + * ExecBeforeCommandTriggers(cmd); + * } + * + * The same applies to after command triggers, so that we are able to switch + * Memory contexts all from here. + */ + bool + CommandFiresTriggers(CommandContext cmd) + { + if (cmd != NULL && (cmd->before != NIL || cmd->after != NIL)) + { + cmd->oldmctx = CurrentMemoryContext; + cmd->cmdmctx = + AllocSetContextCreate(CurrentMemoryContext, + "CommandTriggerContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + + MemoryContextSwitchTo(cmd->cmdmctx); + + return true; + } + return false; + } + + /* + * 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) + { + if (cmd != NULL && cmd->after != NIL) + { + MemoryContextSwitchTo(cmd->cmdmctx); + return true; + } + return false; + } + + /* + * 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) + { + /* that will execute under command trigger memory context */ + if (cmd != NULL && cmd->before != NIL) + exec_command_triggers_internal(cmd, cmd->before, "BEFORE"); + + /* switch back to the command Memory Context now */ + MemoryContextSwitchTo(cmd->oldmctx); + } + + void + ExecAfterCommandTriggers(CommandContext cmd) + { + /* that will execute under command trigger memory context */ + if (cmd != NULL && cmd->after != NIL) + exec_command_triggers_internal(cmd, cmd->after, "AFTER"); + + /* switch back to the command Memory Context now */ + MemoryContextSwitchTo(cmd->oldmctx); + } *** 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 = pstrdup(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 = pstrdup(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 = pstrdup(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 = pstrdup(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 = pstrdup(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 = pstrdup(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,37 **** --- 21,44 ---- #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, List *objname, List *objargs); + static void get_object_name(CommandContext cmd, + ObjectType objtype, + Oid objectId, List *objname); + /* * Drop one or more objects. * *************** *** 49,54 **** RemoveObjects(DropStmt *stmt) --- 56,63 ---- 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) --- 68,74 ---- List *objargs = NIL; Relation relation = NULL; Oid namespaceId; + CommandContextData cmd; if (stmt->arguments) { *************** *** 77,82 **** RemoveObjects(DropStmt *stmt) --- 87,93 ---- if (!OidIsValid(address.objectId)) { does_not_exist_skipping(stmt->removeType, objname, objargs); + cmds[i++] = NULL; continue; } *************** *** 115,126 **** RemoveObjects(DropStmt *stmt) --- 126,161 ---- if (relation) heap_close(relation, NoLock); + /* + * Call BEFORE DROP command triggers + */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = address.objectId; + get_object_name(&cmd, stmt->removeType, address.objectId, objname); + 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) --- 241,251 ---- 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)); *************** *** 240,242 **** does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs) --- 280,335 ---- else ereport(NOTICE, (errmsg(msg, name, args))); } + + /* + * Fill in the CommandContext name, with the non-qualified part fo the name. + */ + static void + get_object_name(CommandContext cmd, ObjectType objtype, + Oid objectId, List *objname) + { + switch (objtype) + { + case OBJECT_TYPE: + case OBJECT_DOMAIN: + cmd->objectname = format_type_be_without_namespace(objectId); + break; + case OBJECT_CAST: + cmd->objectname = NULL; + break; + case OBJECT_TRIGGER: + cmd->objectname = pstrdup(strVal(llast(objname))); + break; + case OBJECT_COLLATION: + case OBJECT_CONVERSION: + case OBJECT_SCHEMA: + case OBJECT_TSPARSER: + case OBJECT_TSDICTIONARY: + case OBJECT_TSTEMPLATE: + case OBJECT_TSCONFIGURATION: + case OBJECT_EXTENSION: + case OBJECT_FUNCTION: + case OBJECT_AGGREGATE: + case OBJECT_OPERATOR: + case OBJECT_LANGUAGE: + case OBJECT_CMDTRIGGER: + case OBJECT_RULE: + case OBJECT_FDW: + case OBJECT_FOREIGN_SERVER: + case OBJECT_OPCLASS: + case OBJECT_OPFAMILY: + { + int len = list_length(objname); + if (len == 1) + cmd->objectname = pstrdup(strVal(linitial(objname))); + else if (len == 2) + cmd->objectname = pstrdup(strVal(lsecond(objname))); + else + elog(ERROR, "unexpected name list length (%d)", len); + break; + } + default: + elog(ERROR, "unexpected object type (%d)", (int)objtype); + break; + } + } *** 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,1206 **** CreateExtension(CreateExtensionStmt *stmt) List *requiredSchemas; Oid extensionOid; ListCell *lc; /* Check extension name validity before any filesystem access */ check_valid_extension_name(stmt->extname); /* * 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 * besides we need a check to support IF NOT EXISTS. */ ! if (get_extension_oid(stmt->extname, true) != InvalidOid) { if (stmt->if_not_exists) { --- 1191,1222 ---- 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 * besides we need a check to support IF NOT EXISTS. */ ! extensionOid = get_extension_oid(stmt->extname, true); ! if ( extensionOid != InvalidOid) { if (stmt->if_not_exists) { *************** *** 1208,1213 **** CreateExtension(CreateExtensionStmt *stmt) --- 1224,1236 ---- (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("extension \"%s\" already exists, skipping", stmt->extname))); + + /* Call AFTER CREATE EXTENSION triggers */ + if (CommandFiresAfterTriggers(&cmd)) + { + cmd.objectId = extensionOid; + ExecAfterCommandTriggers(&cmd); + } return; } else *************** *** 1467,1472 **** CreateExtension(CreateExtensionStmt *stmt) --- 1490,1502 ---- */ 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; --- 2216,2222 ---- * 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) --- 2277,2292 ---- 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) --- 2382,2391 ---- /* 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) --- 2407,2413 ---- 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) --- 2460,2479 ---- 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) --- 2539,2548 ---- */ ApplyExtensionUpdates(extensionOid, control, oldVersionName, updateVersions); + + /* Call AFTER ALTER EXTENSION triggers */ + if (CommandFiresAfterTriggers(&cmd)) + ExecAfterCommandTriggers(&cmd); } /* *************** *** 2649,2654 **** ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt) --- 2712,2718 ---- ObjectAddress object; Relation relation; Oid oldExtension; + CommandContextData cmd; extension.classId = ExtensionRelationId; extension.objectId = get_extension_oid(stmt->extname, false); *************** *** 2677,2682 **** ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt) --- 2741,2753 ---- */ 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) --- 2760,2769 ---- 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) --- 2781,2790 ---- getObjectDescription(&object), stmt->extname))); + /* Call BEFORE ALTER EXTENSION command triggers */ + if (CommandFiresTriggers(&cmd)) + ExecBeforeCommandTriggers(&cmd); + /* * OK, drop the dependency. */ *************** *** 2723,2726 **** ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt) --- 2802,2809 ---- */ 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 *************** *** 204,210 **** GetUserOidFromMapping(const char *username, bool missing_ok) * Rename foreign-data wrapper */ void ! RenameForeignDataWrapper(const char *oldname, const char *newname) { HeapTuple tup; Relation rel; --- 204,211 ---- * Rename foreign-data wrapper */ void ! RenameForeignDataWrapper(const char *oldname, const char *newname, ! CommandContext cmd) { HeapTuple tup; Relation rel; *************** *** 228,233 **** RenameForeignDataWrapper(const char *oldname, const char *newname) --- 229,244 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FDW, oldname); + /* Call BEFORE ALTER FOREIGN DATA WRAPPER triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = pstrdup(oldname); + cmd->schemaname = NULL; + + ExecBeforeCommandTriggers(cmd); + } + /* rename */ namestrcpy(&(((Form_pg_foreign_data_wrapper) GETSTRUCT(tup))->fdwname), newname); simple_heap_update(rel, &tup->t_self, tup); *************** *** 235,240 **** RenameForeignDataWrapper(const char *oldname, const char *newname) --- 246,258 ---- heap_close(rel, NoLock); heap_freetuple(tup); + + /* Call AFTER ALTER FOREIGN DATA WRAPPER triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectname = pstrdup(newname); + ExecAfterCommandTriggers(cmd); + } } *************** *** 242,248 **** RenameForeignDataWrapper(const char *oldname, const char *newname) * Rename foreign server */ void ! RenameForeignServer(const char *oldname, const char *newname) { HeapTuple tup; Relation rel; --- 260,266 ---- * Rename foreign server */ void ! RenameForeignServer(const char *oldname, const char *newname, CommandContext cmd) { HeapTuple tup; Relation rel; *************** *** 266,271 **** RenameForeignServer(const char *oldname, const char *newname) --- 284,299 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, oldname); + /* Call BEFORE ALTER SERVER triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = pstrdup(oldname); + cmd->schemaname = NULL; + + ExecBeforeCommandTriggers(cmd); + } + /* rename */ namestrcpy(&(((Form_pg_foreign_server) GETSTRUCT(tup))->srvname), newname); simple_heap_update(rel, &tup->t_self, tup); *************** *** 273,278 **** RenameForeignServer(const char *oldname, const char *newname) --- 301,313 ---- heap_close(rel, NoLock); heap_freetuple(tup); + + /* Call AFTER ALTER SERVER triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectname = pstrdup(newname); + ExecAfterCommandTriggers(cmd); + } } *************** *** 283,289 **** RenameForeignServer(const char *oldname, const char *newname) * superuser. */ static void ! AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) { Form_pg_foreign_data_wrapper form; --- 318,325 ---- * superuser. */ static void ! AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId, ! CommandContext cmd) { Form_pg_foreign_data_wrapper form; *************** *** 305,310 **** AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerI --- 341,356 ---- NameStr(form->fdwname)), errhint("The owner of a foreign-data wrapper must be a superuser."))); + /* Call BEFORE ALTER FOREIGN DATA WRAPPER triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = pstrdup(NameStr(form->fdwname)); + cmd->schemaname = NULL; + + ExecBeforeCommandTriggers(cmd); + } + if (form->fdwowner != newOwnerId) { form->fdwowner = newOwnerId; *************** *** 317,322 **** AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerI --- 363,372 ---- HeapTupleGetOid(tup), newOwnerId); } + + /* Call AFTER ALTER FOREIGN DATA WRAPPER triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); } /* *************** *** 325,331 **** AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerI * Note restrictions in the "_internal" function, above. */ void ! AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId) { HeapTuple tup; Relation rel; --- 375,382 ---- * Note restrictions in the "_internal" function, above. */ void ! AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId, ! CommandContext cmd) { HeapTuple tup; Relation rel; *************** *** 339,345 **** AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId) (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("foreign-data wrapper \"%s\" does not exist", name))); ! AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId); heap_freetuple(tup); --- 390,396 ---- (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("foreign-data wrapper \"%s\" does not exist", name))); ! AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId, cmd); heap_freetuple(tup); *************** *** 366,372 **** AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId) (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("foreign-data wrapper with OID %u does not exist", fwdId))); ! AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId); heap_freetuple(tup); --- 417,423 ---- (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("foreign-data wrapper with OID %u does not exist", fwdId))); ! AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId, NULL); heap_freetuple(tup); *************** *** 377,383 **** AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId) * Internal workhorse for changing a foreign server's owner */ static void ! AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) { Form_pg_foreign_server form; --- 428,435 ---- * Internal workhorse for changing a foreign server's owner */ static void ! AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId, ! CommandContext cmd) { Form_pg_foreign_server form; *************** *** 411,416 **** AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) --- 463,478 ---- } } + /* Call BEFORE ALTER SERVER triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = pstrdup(NameStr(form->srvname)); + cmd->schemaname = NULL; + + ExecBeforeCommandTriggers(cmd); + } + form->srvowner = newOwnerId; simple_heap_update(rel, &tup->t_self, tup); *************** *** 419,424 **** AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) --- 481,490 ---- /* Update owner dependency reference */ changeDependencyOnOwner(ForeignServerRelationId, HeapTupleGetOid(tup), newOwnerId); + + /* Call AFTER ALTER SERVER triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); } } *************** *** 426,432 **** AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) * Change foreign server owner -- by name */ void ! AlterForeignServerOwner(const char *name, Oid newOwnerId) { HeapTuple tup; Relation rel; --- 492,498 ---- * Change foreign server owner -- by name */ void ! AlterForeignServerOwner(const char *name, Oid newOwnerId, CommandContext cmd) { HeapTuple tup; Relation rel; *************** *** 440,446 **** AlterForeignServerOwner(const char *name, Oid newOwnerId) (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("server \"%s\" does not exist", name))); ! AlterForeignServerOwner_internal(rel, tup, newOwnerId); heap_freetuple(tup); --- 506,512 ---- (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("server \"%s\" does not exist", name))); ! AlterForeignServerOwner_internal(rel, tup, newOwnerId, cmd); heap_freetuple(tup); *************** *** 465,471 **** AlterForeignServerOwner_oid(Oid srvId, Oid newOwnerId) (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("foreign server with OID %u does not exist", srvId))); ! AlterForeignServerOwner_internal(rel, tup, newOwnerId); heap_freetuple(tup); --- 531,537 ---- (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("foreign server with OID %u does not exist", srvId))); ! AlterForeignServerOwner_internal(rel, tup, newOwnerId, NULL); heap_freetuple(tup); *************** *** 578,583 **** CreateForeignDataWrapper(CreateFdwStmt *stmt) --- 644,650 ---- Oid ownerId; ObjectAddress myself; ObjectAddress referenced; + CommandContextData cmd; rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock); *************** *** 601,606 **** CreateForeignDataWrapper(CreateFdwStmt *stmt) --- 668,685 ---- 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) --- 748,760 ---- 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) --- 777,783 ---- bool validator_given; Oid fdwhandler; Oid fdwvalidator; + CommandContextData cmd; rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock); *************** *** 713,718 **** AlterForeignDataWrapper(AlterFdwStmt *stmt) --- 800,817 ---- 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) --- 929,938 ---- } heap_close(rel, RowExclusiveLock); + + /* Call AFTER ALTER FOREIGN DATA WRAPPER triggers */ + if (CommandFiresAfterTriggers(&cmd)) + ExecAfterCommandTriggers(&cmd); } *************** *** 874,879 **** CreateForeignServer(CreateForeignServerStmt *stmt) --- 977,983 ---- ObjectAddress myself; ObjectAddress referenced; ForeignDataWrapper *fdw; + CommandContextData cmd; rel = heap_open(ForeignServerRelationId, RowExclusiveLock); *************** *** 899,904 **** CreateForeignServer(CreateForeignServerStmt *stmt) --- 1003,1020 ---- 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) --- 1081,1093 ---- 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) --- 1104,1110 ---- 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) --- 1126,1143 ---- 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) --- 1194,1203 ---- heap_freetuple(tp); heap_close(rel, RowExclusiveLock); + + /* Call AFTER ALTER SERVER triggers */ + if (CommandFiresAfterTriggers(&cmd)) + ExecAfterCommandTriggers(&cmd); } *************** *** 1129,1134 **** CreateUserMapping(CreateUserMappingStmt *stmt) --- 1269,1275 ---- ObjectAddress referenced; ForeignServer *srv; ForeignDataWrapper *fdw; + CommandContextData cmd; rel = heap_open(UserMappingRelationId, RowExclusiveLock); *************** *** 1154,1159 **** CreateUserMapping(CreateUserMappingStmt *stmt) --- 1295,1312 ---- 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) --- 1358,1370 ---- 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) --- 1382,1388 ---- Oid useId; Oid umId; ForeignServer *srv; + CommandContextData cmd; rel = heap_open(UserMappingRelationId, RowExclusiveLock); *************** *** 1244,1249 **** AlterUserMapping(AlterUserMappingStmt *stmt) --- 1405,1422 ---- 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) --- 1464,1473 ---- heap_freetuple(tp); heap_close(rel, RowExclusiveLock); + + /* Call AFTER ALTER SERVER triggers */ + if (CommandFiresAfterTriggers(&cmd)) + ExecAfterCommandTriggers(&cmd); } *************** *** 1304,1309 **** RemoveUserMapping(DropUserMappingStmt *stmt) --- 1481,1487 ---- 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) --- 1529,1546 ---- 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) --- 1549,1560 ---- 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 = pstrdup(NameStr(procForm->proname)); + 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 = pstrdup(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 = pstrdup(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); *************** *** 1268,1273 **** AlterFunction(AlterFunctionStmt *stmt) --- 1324,1330 ---- List *set_items = NIL; DefElem *cost_item = NULL; DefElem *rows_item = NULL; + CommandContextData cmd; rel = heap_open(ProcedureRelationId, RowExclusiveLock); *************** *** 1377,1388 **** AlterFunction(AlterFunctionStmt *stmt) --- 1434,1461 ---- repl_val, repl_null, repl_repl); } + /* Call BEFORE ALTER FUNCTION command triggers */ + InitCommandContext(&cmd, (Node *)stmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = InvalidOid; + cmd.objectname = pstrdup(NameStr(procForm->proname)); + cmd.schemaname = get_namespace_name(procForm->pronamespace); + + ExecBeforeCommandTriggers(&cmd); + } + /* Do the update */ simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); heap_close(rel, NoLock); heap_freetuple(tup); + + /* Call AFTER ALTER FUNCTION command triggers */ + if (CommandFiresAfterTriggers(&cmd)) + ExecAfterCommandTriggers(&cmd); } /* *************** *** 1479,1484 **** CreateCast(CreateCastStmt *stmt) --- 1552,1558 ---- ObjectAddress myself, referenced; AclResult aclresult; + CommandContextData cmd; sourcetypeid = typenameTypeId(NULL, stmt->sourcetype); targettypeid = typenameTypeId(NULL, stmt->targettype); *************** *** 1697,1702 **** CreateCast(CreateCastStmt *stmt) --- 1771,1788 ---- 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) --- 1850,1861 ---- 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; --- 1914,1920 ---- */ 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; --- 1928,1938 ---- /* 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) --- 1967,1982 ---- NameStr(proc->proname), get_namespace_name(nspOid)))); + /* Call BEFORE ALTER FUNCTION triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = procOid; + cmd->objectname = pstrdup(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) --- 1995,2005 ---- 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 *************** *** 81,89 **** static void dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid, static void dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid, List *procedures); static void AlterOpClassOwner_internal(Relation rel, HeapTuple tuple, ! Oid newOwnerId); static void AlterOpFamilyOwner_internal(Relation rel, HeapTuple tuple, ! Oid newOwnerId); /* --- 81,89 ---- static void dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid, List *procedures); static void AlterOpClassOwner_internal(Relation rel, HeapTuple tuple, ! Oid newOwnerId, CommandContext cmd); static void AlterOpFamilyOwner_internal(Relation rel, HeapTuple tuple, ! Oid newOwnerId, CommandContext cmd); /* *************** *** 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 = pstrdup(opcname); + 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); } /* *************** *** 1667,1673 **** RemoveAmProcEntryById(Oid entryOid) * Rename opclass */ void ! RenameOpClass(List *name, const char *access_method, const char *newname) { Oid opcOid; Oid amOid; --- 1733,1740 ---- * Rename opclass */ void ! RenameOpClass(List *name, const char *access_method, const char *newname, ! CommandContext cmd) { Oid opcOid; Oid amOid; *************** *** 1712,1717 **** RenameOpClass(List *name, const char *access_method, const char *newname) --- 1779,1794 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* Call BEFORE ALTER OPERATOR CLASS triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = opcOid; + cmd->objectname = NameListToString(name); + cmd->schemaname = get_namespace_name(namespaceOid); + + ExecBeforeCommandTriggers(cmd); + } + /* rename */ namestrcpy(&(((Form_pg_opclass) GETSTRUCT(tup))->opcname), newname); simple_heap_update(rel, &tup->t_self, tup); *************** *** 1719,1731 **** RenameOpClass(List *name, const char *access_method, const char *newname) heap_close(rel, NoLock); heap_freetuple(tup); } /* * Rename opfamily */ void ! RenameOpFamily(List *name, const char *access_method, const char *newname) { Oid opfOid; Oid amOid; --- 1796,1816 ---- heap_close(rel, NoLock); heap_freetuple(tup); + + /* Call AFTER ALTER OPERATOR CLASS triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectname = pstrdup(newname); + ExecAfterCommandTriggers(cmd); + } } /* * Rename opfamily */ void ! RenameOpFamily(List *name, const char *access_method, const char *newname, ! CommandContext cmd) { Oid opfOid; Oid amOid; *************** *** 1801,1806 **** RenameOpFamily(List *name, const char *access_method, const char *newname) --- 1886,1901 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* Call BEFORE ALTER OPERATOR FAMILY triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = opfOid; + cmd->objectname = pstrdup(opfname); + cmd->schemaname = get_namespace_name(namespaceOid); + + ExecBeforeCommandTriggers(cmd); + } + /* rename */ namestrcpy(&(((Form_pg_opfamily) GETSTRUCT(tup))->opfname), newname); simple_heap_update(rel, &tup->t_self, tup); *************** *** 1808,1820 **** RenameOpFamily(List *name, const char *access_method, const char *newname) heap_close(rel, NoLock); heap_freetuple(tup); } /* * Change opclass owner by name */ void ! AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId) { Oid amOid; Relation rel; --- 1903,1923 ---- heap_close(rel, NoLock); heap_freetuple(tup); + + /* Call AFTER ALTER OPERATOR FAMILY triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectname = pstrdup(newname); + ExecAfterCommandTriggers(cmd); + } } /* * Change opclass owner by name */ void ! AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId, ! CommandContext cmd) { Oid amOid; Relation rel; *************** *** 1830,1836 **** AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId) tup = heap_copytuple(origtup); ReleaseSysCache(origtup); ! AlterOpClassOwner_internal(rel, tup, newOwnerId); heap_freetuple(tup); heap_close(rel, NoLock); --- 1933,1939 ---- tup = heap_copytuple(origtup); ReleaseSysCache(origtup); ! AlterOpClassOwner_internal(rel, tup, newOwnerId, cmd); heap_freetuple(tup); heap_close(rel, NoLock); *************** *** 1851,1857 **** AlterOpClassOwner_oid(Oid opclassOid, Oid newOwnerId) if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for opclass %u", opclassOid); ! AlterOpClassOwner_internal(rel, tup, newOwnerId); heap_freetuple(tup); heap_close(rel, NoLock); --- 1954,1960 ---- if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for opclass %u", opclassOid); ! AlterOpClassOwner_internal(rel, tup, newOwnerId, NULL); heap_freetuple(tup); heap_close(rel, NoLock); *************** *** 1862,1868 **** AlterOpClassOwner_oid(Oid opclassOid, Oid newOwnerId) * parameter is a copy of the tuple from pg_opclass we want to modify. */ static void ! AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) { Oid namespaceOid; AclResult aclresult; --- 1965,1972 ---- * parameter is a copy of the tuple from pg_opclass we want to modify. */ static void ! AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId, ! CommandContext cmd) { Oid namespaceOid; AclResult aclresult; *************** *** 1900,1905 **** AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) --- 2004,2019 ---- get_namespace_name(namespaceOid)); } + /* Call BEFORE ALTER OPERATOR CLASS triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = pstrdup(NameStr(opcForm->opcname)); + cmd->schemaname = get_namespace_name(opcForm->opcnamespace); + + ExecBeforeCommandTriggers(cmd); + } + /* * Modify the owner --- okay to scribble on tup because it's a copy */ *************** *** 1912,1917 **** AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) --- 2026,2035 ---- /* Update owner dependency reference */ changeDependencyOnOwner(OperatorClassRelationId, HeapTupleGetOid(tup), newOwnerId); + + /* Call AFTER ALTER OPERATOR CLASS 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; --- 2037,2044 ---- * 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); } --- 2060,2066 ---- 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); --- 2079,2085 ---- Anum_pg_opclass_opcname, Anum_pg_opclass_opcnamespace, Anum_pg_opclass_opcowner, ! ACL_KIND_OPCLASS, NULL); heap_close(rel, RowExclusiveLock); *************** *** 1971,1977 **** AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid) * Change opfamily owner by name */ void ! AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId) { Oid amOid; Relation rel; --- 2090,2097 ---- * Change opfamily owner by name */ void ! AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId, ! CommandContext cmd) { Oid amOid; Relation rel; *************** *** 2020,2026 **** AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId) elog(ERROR, "cache lookup failed for opfamily %u", opfOid); } ! AlterOpFamilyOwner_internal(rel, tup, newOwnerId); heap_freetuple(tup); heap_close(rel, NoLock); --- 2140,2146 ---- elog(ERROR, "cache lookup failed for opfamily %u", opfOid); } ! AlterOpFamilyOwner_internal(rel, tup, newOwnerId, cmd); heap_freetuple(tup); heap_close(rel, NoLock); *************** *** 2041,2047 **** AlterOpFamilyOwner_oid(Oid opfamilyOid, Oid newOwnerId) if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for opfamily %u", opfamilyOid); ! AlterOpFamilyOwner_internal(rel, tup, newOwnerId); heap_freetuple(tup); heap_close(rel, NoLock); --- 2161,2167 ---- if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for opfamily %u", opfamilyOid); ! AlterOpFamilyOwner_internal(rel, tup, newOwnerId, NULL); heap_freetuple(tup); heap_close(rel, NoLock); *************** *** 2052,2058 **** AlterOpFamilyOwner_oid(Oid opfamilyOid, Oid newOwnerId) * parameter is a copy of the tuple from pg_opfamily we want to modify. */ static void ! AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) { Oid namespaceOid; AclResult aclresult; --- 2172,2179 ---- * parameter is a copy of the tuple from pg_opfamily we want to modify. */ static void ! AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId, ! CommandContext cmd) { Oid namespaceOid; AclResult aclresult; *************** *** 2090,2095 **** AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) --- 2211,2226 ---- get_namespace_name(namespaceOid)); } + /* Call BEFORE ALTER OPERATOR FAMILY triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = pstrdup(NameStr(opfForm->opfname)); + cmd->schemaname = get_namespace_name(opfForm->opfnamespace); + + ExecBeforeCommandTriggers(cmd); + } + /* * Modify the owner --- okay to scribble on tup because it's a copy */ *************** *** 2102,2107 **** AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) --- 2233,2242 ---- /* Update owner dependency reference */ changeDependencyOnOwner(OperatorFamilyRelationId, HeapTupleGetOid(tup), newOwnerId); + + /* Call AFTER ALTER OPERATOR FAMILY triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); } } *************** *** 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; --- 2263,2270 ---- * 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); } --- 2286,2292 ---- 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); --- 2305,2311 ---- 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 = pstrdup(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,60 **** 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); static void AlterLanguageOwner_internal(HeapTuple tup, Relation rel, ! Oid newOwnerId); /* --------------------------------------------------------------------- --- 49,60 ---- 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); static void AlterLanguageOwner_internal(HeapTuple tup, Relation rel, ! Oid newOwnerId, CommandContext cmd); /* --------------------------------------------------------------------- *************** *** 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; } /* *************** *** 536,542 **** DropProceduralLanguageById(Oid langOid) * Rename language */ void ! RenameLanguage(const char *oldname, const char *newname) { HeapTuple tup; Relation rel; --- 576,582 ---- * Rename language */ void ! RenameLanguage(const char *oldname, const char *newname, CommandContext cmd) { HeapTuple tup; Relation rel; *************** *** 560,565 **** RenameLanguage(const char *oldname, const char *newname) --- 600,615 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, oldname); + /* Call BEFORE ALTER SERVER triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = pstrdup(oldname); + cmd->schemaname = NULL; + + ExecBeforeCommandTriggers(cmd); + } + /* rename */ namestrcpy(&(((Form_pg_language) GETSTRUCT(tup))->lanname), newname); simple_heap_update(rel, &tup->t_self, tup); *************** *** 567,579 **** RenameLanguage(const char *oldname, const char *newname) heap_close(rel, NoLock); heap_freetuple(tup); } /* * Change language owner */ void ! AlterLanguageOwner(const char *name, Oid newOwnerId) { HeapTuple tup; Relation rel; --- 617,636 ---- heap_close(rel, NoLock); heap_freetuple(tup); + + /* Call AFTER ALTER LANGUAGE triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectname = pstrdup(newname); + ExecAfterCommandTriggers(cmd); + } } /* * Change language owner */ void ! AlterLanguageOwner(const char *name, Oid newOwnerId, CommandContext cmd) { HeapTuple tup; Relation rel; *************** *** 586,592 **** AlterLanguageOwner(const char *name, Oid newOwnerId) (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("language \"%s\" does not exist", name))); ! AlterLanguageOwner_internal(tup, rel, newOwnerId); ReleaseSysCache(tup); --- 643,649 ---- (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("language \"%s\" does not exist", name))); ! AlterLanguageOwner_internal(tup, rel, newOwnerId, cmd); ReleaseSysCache(tup); *************** *** 609,615 **** AlterLanguageOwner_oid(Oid oid, Oid newOwnerId) if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for language %u", oid); ! AlterLanguageOwner_internal(tup, rel, newOwnerId); ReleaseSysCache(tup); --- 666,672 ---- if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for language %u", oid); ! AlterLanguageOwner_internal(tup, rel, newOwnerId, NULL); ReleaseSysCache(tup); *************** *** 620,626 **** AlterLanguageOwner_oid(Oid oid, Oid newOwnerId) * Workhorse for AlterLanguageOwner variants */ static void ! AlterLanguageOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) { Form_pg_language lanForm; --- 677,684 ---- * Workhorse for AlterLanguageOwner variants */ static void ! AlterLanguageOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId, ! CommandContext cmd) { Form_pg_language lanForm; *************** *** 648,653 **** AlterLanguageOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) --- 706,721 ---- /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); + /* Call BEFORE ALTER LANGUAGE triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = pstrdup(NameStr(lanForm->lanname)); + cmd->schemaname = NULL; + + ExecBeforeCommandTriggers(cmd); + } + memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); *************** *** 680,685 **** AlterLanguageOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) --- 748,757 ---- /* Update owner dependency reference */ changeDependencyOnOwner(LanguageRelationId, HeapTupleGetOid(tup), newOwnerId); + + /* Call AFTER ALTER LANGUAGE triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); } } *** 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 = pstrdup(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 = pstrdup(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 = pstrdup(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,217 **** DefineSequence(CreateSeqStmt *seq) stmt->tablespacename = NULL; stmt->if_not_exists = false; ! seqoid = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId); Assert(seqoid != InvalidOid); rel = heap_open(seqoid, AccessExclusiveLock); --- 213,222 ---- stmt->tablespacename = NULL; stmt->if_not_exists = false; ! /* Prepare BEFORE CREATE SEQUENCE triggers */ ! InitCommandContext(&cmd, (Node *)seq, false); ! ! seqoid = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, &cmd); Assert(seqoid != InvalidOid); rel = heap_open(seqoid, AccessExclusiveLock); *************** *** 226,231 **** DefineSequence(CreateSeqStmt *seq) --- 231,243 ---- 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) --- 435,441 ---- 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) --- 471,490 ---- /* 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 (CommandFiresTriggers(&cmd)) + { + cmd.objectId = relid; + cmd.objectname = pstrdup(stmt->sequence->relname); + cmd.schemaname = pstrdup(stmt->sequence->schemaname); + + ExecBeforeCommandTriggers(&cmd); + } + START_CRIT_SECTION(); MarkBufferDirty(buf); *************** *** 496,501 **** AlterSequence(AlterSeqStmt *stmt) --- 523,532 ---- 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" *************** *** 413,419 **** static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, * ---------------------------------------------------------------- */ Oid ! DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) { char relname[NAMEDATALEN]; Oid namespaceId; --- 415,421 ---- * ---------------------------------------------------------------- */ Oid ! DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, CommandContext cmd) { char relname[NAMEDATALEN]; Oid namespaceId; *************** *** 606,611 **** DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) --- 608,623 ---- } } + /* Exec BEFORE CREATE view|sequence|table|type command triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = InvalidOid; + cmd->objectname = pstrdup(relname); + cmd->schemaname = get_namespace_name(namespaceId); + + ExecBeforeCommandTriggers(cmd); + } + /* * Create the relation. Inherited defaults and constraints are passed in * for immediate handling --- since they don't need parsing, they can be *************** *** 668,673 **** DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) --- 680,686 ---- */ relation_close(rel, NoLock); + /* AFTER command triggers are fired from well outside this function */ return relationId; } *************** *** 738,743 **** RemoveRelations(DropStmt *drop) --- 751,758 ---- 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 *************** *** 784,789 **** RemoveRelations(DropStmt *drop) --- 799,805 ---- Oid relOid; ObjectAddress obj; struct DropRelationCallbackState state; + CommandContextData cmd; /* * These next few steps are a great deal like relation_openrv, but we *************** *** 809,817 **** RemoveRelations(DropStmt *drop) --- 825,849 ---- 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; *************** *** 822,827 **** RemoveRelations(DropStmt *drop) --- 854,869 ---- performMultipleDeletions(objects, drop->behavior, 0); + /* Call AFTER DROP command triggers */ + for(i = 0; iobjectId = InvalidOid; + ExecAfterCommandTriggers(cmds[i]); + } + } + free_object_addresses(objects); } *************** *** 1139,1145 **** ExecuteTruncate(TruncateStmt *stmt) /* * Reconstruct the indexes to match, and we're done. */ ! reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST); } } --- 1181,1187 ---- /* * Reconstruct the indexes to match, and we're done. */ ! reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST, NULL); } } *************** *** 2300,2306 **** RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid, * renameatt - changes the name of a attribute in a relation */ void ! renameatt(RenameStmt *stmt) { Oid relid; --- 2342,2348 ---- * renameatt - changes the name of a attribute in a relation */ void ! renameatt(RenameStmt *stmt, CommandContext cmd) { Oid relid; *************** *** 2318,2323 **** renameatt(RenameStmt *stmt) --- 2360,2375 ---- return; } + /* BEFORE command triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = relid; + cmd->objectname = stmt->relation->relname; + cmd->schemaname = stmt->relation->schemaname; + + ExecBeforeCommandTriggers(cmd); + } + renameatt_internal(relid, stmt->subname, /* old att name */ stmt->newname, /* new att name */ *************** *** 2325,2337 **** renameatt(RenameStmt *stmt) false, /* recursing? */ 0, /* expected inhcount */ stmt->behavior); } /* * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE RENAME */ void ! RenameRelation(RenameStmt *stmt) { Oid relid; --- 2377,2393 ---- false, /* recursing? */ 0, /* expected inhcount */ stmt->behavior); + + /* AFTER command triggers */ + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); } /* * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE RENAME */ void ! RenameRelation(RenameStmt *stmt, CommandContext cmd) { Oid relid; *************** *** 2356,2362 **** RenameRelation(RenameStmt *stmt) } /* Do the work */ ! RenameRelationInternal(relid, stmt->newname); } /* --- 2412,2418 ---- } /* Do the work */ ! RenameRelationInternal(relid, stmt->newname, cmd); } /* *************** *** 2369,2375 **** 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 */ --- 2425,2431 ---- * 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 */ *************** *** 2400,2405 **** RenameRelationInternal(Oid myrelid, const char *newrelname) --- 2456,2471 ---- errmsg("relation \"%s\" already exists", newrelname))); + /* Call BEFORE ALTER relation triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(reltup); + cmd->objectname = pstrdup(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...) *************** *** 2419,2425 **** 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. --- 2485,2491 ---- */ if (OidIsValid(targetrelation->rd_rel->reltype)) RenameTypeInternal(targetrelation->rd_rel->reltype, ! newrelname, namespaceId, NULL); /* * Also rename the associated constraint, if any. *************** *** 2436,2441 **** RenameRelationInternal(Oid myrelid, const char *newrelname) --- 2502,2514 ---- * Close rel, but keep exclusive lock! */ relation_close(targetrelation, NoLock); + + /* Call AFTER ALTER relation triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectname = pstrdup(newrelname); + ExecAfterCommandTriggers(cmd); + } } /* *************** *** 2534,2540 **** AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode) * Thanks to the magic of MVCC, an error anywhere along the way rolls back * the whole operation; we don't have to do anything special to clean up. * ! * The caller must lock the relation, with an appropriate lock level * for the subcommands requested. Any subcommand that needs to rewrite * tuples in the table forces the whole command to be executed with * AccessExclusiveLock (actually, that is currently required always, but --- 2607,2613 ---- * Thanks to the magic of MVCC, an error anywhere along the way rolls back * the whole operation; we don't have to do anything special to clean up. * ! * The caller must lock the relation, with an appropriate lock level * for the subcommands requested. Any subcommand that needs to rewrite * tuples in the table forces the whole command to be executed with * AccessExclusiveLock (actually, that is currently required always, but *************** *** 5334,5340 **** ATExecAddIndex(AlteredTableInfo *tab, Relation rel, check_rights, skip_build, quiet, ! false); /* * If TryReuseIndex() stashed a relfilenode for us, we used it for the new --- 5407,5413 ---- check_rights, skip_build, quiet, ! false, NULL); /* * If TryReuseIndex() stashed a relfilenode for us, we used it for the new *************** *** 5393,5399 **** 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 */ --- 5466,5472 ---- 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 */ *************** *** 6871,6877 **** ATExecDropConstraint(Relation rel, const char *constrName, /* Right now only CHECK constraints can be inherited */ if (con->contype == CONSTRAINT_CHECK) is_check_constraint = true; ! if (con->conisonly) { Assert(is_check_constraint); --- 6944,6950 ---- /* Right now only CHECK constraints can be inherited */ if (con->contype == CONSTRAINT_CHECK) is_check_constraint = true; ! if (con->conisonly) { Assert(is_check_constraint); *************** *** 9655,9660 **** AlterTableNamespace(AlterObjectSchemaStmt *stmt) --- 9728,9734 ---- Oid nspOid; Relation classRel; RangeVar *newrv; + CommandContextData cmd; relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock, stmt->missing_ok, false, *************** *** 9695,9707 **** 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) --- 9769,9793 ---- /* 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) *************** *** 9716,9721 **** AlterTableNamespace(AlterObjectSchemaStmt *stmt) --- 9802,9814 ---- /* 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); + } } /* *************** *** 9863,9869 **** 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); --- 9956,9962 ---- * 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 = pstrdup(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 = pstrdup(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 = pstrdup(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 = pstrdup(dictname); + 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 = pstrdup(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 = pstrdup(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 = pstrdup(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 = pstrdup(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 = pstrdup(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 = pstrdup(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 = pstrdup(cfgname); + 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 = pstrdup(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 = pstrdup(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 = pstrdup(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 = pstrdup(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(RangeVar *typevar, List *coldeflist) { CreateStmt *createStmt = makeNode(CreateStmt); Oid old_type_oid; --- 2067,2074 ---- *------------------------------------------------------------------- */ Oid ! DefineCompositeType(RangeVar *typevar, List *coldeflist, ! CommandContext cmd) { CreateStmt *createStmt = makeNode(CreateStmt); Oid old_type_oid; *************** *** 2024,2031 **** DefineCompositeType(RangeVar *typevar, List *coldeflist) /* * Finally create the relation. This also creates the type. */ ! relid = DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid); Assert(relid != InvalidOid); return relid; } --- 2112,2126 ---- /* * Finally create the relation. This also creates the type. */ ! relid = DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, cmd); Assert(relid != InvalidOid); + + /* Call AFTER CREATE (composite) TYPE triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectId = relid; + ExecAfterCommandTriggers(cmd); + } return relid; } *************** *** 2035,2041 **** DefineCompositeType(RangeVar *typevar, List *coldeflist) * Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements. */ void ! AlterDomainDefault(List *names, Node *defaultRaw) { TypeName *typename; Oid domainoid; --- 2130,2136 ---- * 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) --- 2160,2175 ---- /* 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 = pstrdup(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) --- 2266,2275 ---- /* 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; --- 2278,2284 ---- * 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) --- 2301,2316 ---- /* 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 = pstrdup(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) --- 2376,2385 ---- /* 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; --- 2389,2395 ---- */ 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, --- 2415,2432 ---- /* Check it's a domain and check user has permission for ALTER DOMAIN */ checkDomainOwner(tup); + /* Call BEFORE ALTER DOMAIN triggers */ + if (CommandFiresTriggers(cmd)) + { + Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup); + + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = pstrdup(NameStr(typTup->typname)); + cmd->schemaname = get_namespace_name(typTup->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, --- 2476,2485 ---- (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; --- 2488,2494 ---- * 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) --- 2517,2532 ---- elog(ERROR, "unrecognized node type: %d", (int) nodeTag(newConstraint)); + /* Call BEFORE ALTER DOMAIN triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = pstrdup(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) --- 2593,2602 ---- /* 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; --- 2605,2611 ---- * 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) --- 2678,2695 ---- HeapTupleGetOid(tuple)); conbin = TextDatumGetCString(val); + /* Call BEFORE ALTER DOMAIN triggers */ + if (CommandFiresTriggers(cmd)) + { + Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup); + + cmd->objectId = HeapTupleGetOid(tup); + cmd->objectname = pstrdup(NameStr(typTup->typname)); + cmd->schemaname = get_namespace_name(typTup->typnamespace); + + ExecBeforeCommandTriggers(cmd); + } + validateDomainConstraint(domainoid, conbin); /* *************** *** 2543,2548 **** AlterDomainValidateConstraint(List *names, char *constrName) --- 2708,2717 ---- 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; --- 3260,3266 ---- * 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); --- 3322,3331 ---- * 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; --- 3335,3342 ---- * 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) --- 3422,3437 ---- get_namespace_name(typTup->typnamespace)); } + /* Call BEFORE ALTER TYPE triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = typeOid; + cmd->objectname = pstrdup(NameStr(typTup->typname)); + 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) --- 3459,3468 ---- 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; --- 3520,3527 ---- * 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; --- 3541,3551 ---- /* 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); } /* --- 3565,3571 ---- 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; --- 3586,3593 ---- Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, ! bool errorOnTableType, ! CommandContext cmd) { Relation rel; HeapTuple tup; *************** *** 3447,3452 **** AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, --- 3633,3648 ---- format_type_be(typeOid)), errhint("Use ALTER TABLE instead."))); + /* Call BEFORE ALTER TYPE triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = typeOid; + cmd->objectname = pstrdup(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; } --- 3700,3711 ---- /* 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,147 ---- else in_outer_xact = IsInTransactionChain(isTopLevel); + /* Call BEFORE VACUUM command triggers */ + if (!IsAutoVacuumWorkerProcess()) + { + CommandContextData cmd; + InitCommandContext(&cmd, (Node *)vacstmt, false); + + if (CommandFiresTriggers(&cmd)) + { + cmd.objectId = relid; + + if (vacstmt->relation != NULL) + { + 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); --- 1074,1080 ---- /* 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; *************** *** 195,200 **** DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace, --- 196,211 ---- */ Assert(relation->relpersistence == rel->rd_rel->relpersistence); + /* Call BEFORE CREATE VIEW triggers */ + if (CommandFiresTriggers(cmd)) + { + cmd->objectId = viewOid; + cmd->objectname = RelationGetRelationName(rel); + cmd->schemaname = get_namespace_name(RelationGetNamespace(rel)); + + ExecBeforeCommandTriggers(cmd); + } + /* * Create a tuple descriptor to compare against the existing view, and * verify that the old column list is an initial prefix of the new *************** *** 269,275 **** DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace, * existing view, so we don't need more code to complain if "replace" * is false). */ ! relid = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid); Assert(relid != InvalidOid); return relid; } --- 280,286 ---- * existing view, so we don't need more code to complain if "replace" * is false). */ ! relid = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, cmd); Assert(relid != InvalidOid); return relid; } *************** *** 343,349 **** DefineViewRules(Oid viewOid, Query *viewParse, bool replace) CMD_SELECT, true, replace, ! list_make1(viewParse)); /* * Someday: automatic ON INSERT, etc --- 354,361 ---- CMD_SELECT, true, replace, ! list_make1(viewParse), ! NULL); /* * Someday: automatic ON INSERT, etc *************** *** 426,431 **** DefineView(ViewStmt *stmt, const char *queryString) --- 438,444 ---- 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 --- 524,544 ---- } /* + * 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) --- 557,567 ---- * 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 *************** *** 3458,3463 **** _copyCreateTrigStmt(const CreateTrigStmt *from) --- 3458,3486 ---- return newnode; } + static CreateCmdTrigStmt * + _copyCreateCmdTrigStmt(const CreateCmdTrigStmt *from) + { + CreateCmdTrigStmt *newnode = makeNode(CreateCmdTrigStmt); + + 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(trigname); + COPY_STRING_FIELD(tgenabled); + + return newnode; + } + static CreatePLangStmt * _copyCreatePLangStmt(const CreatePLangStmt *from) { *************** *** 3687,3693 **** _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)); \ --- 3710,3716 ---- /* * 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)); \ *************** *** 4310,4315 **** copyObject(const void *from) --- 4333,4344 ---- 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,1802 ---- } static bool + _equalCreateCmdTrigStmt(const CreateCmdTrigStmt *a, const CreateCmdTrigStmt *b) + { + 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(trigname); + COMPARE_STRING_FIELD(tgenabled); + + return true; + } + + static bool _equalCreatePLangStmt(const CreatePLangStmt *a, const CreatePLangStmt *b) { COMPARE_SCALAR_FIELD(replace); *************** *** 2853,2858 **** equal(const void *a, const void *b) --- 2872,2883 ---- 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,276 ---- %type OptSchemaEltList %type TriggerForSpec TriggerForType ! %type TriggerActionTime CmdTriggerActionTime %type TriggerEvents TriggerOneEvent %type TriggerFuncArg %type TriggerWhen + %type trigger_command enable_trigger %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 --- 498,504 ---- 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 --- 678,685 ---- ; stmt : ! AlterCmdTrigStmt ! | AlterDatabaseStmt | AlterDatabaseSetStmt | AlterDefaultPrivilegesStmt | AlterDomainStmt *************** *** 726,731 **** stmt : --- 730,736 ---- | CreateStmt | CreateTableSpaceStmt | CreateTrigStmt + | CreateCmdTrigStmt | CreateRoleStmt | CreateUserStmt | CreateUserMappingStmt *************** *** 749,754 **** stmt : --- 754,760 ---- | DropStmt | DropTableSpaceStmt | DropTrigStmt + | DropCmdTrigStmt | DropRoleStmt | DropUserStmt | DropUserMappingStmt *************** *** 4260,4265 **** DropTrigStmt: --- 4266,4436 ---- /***************************************************************************** * * QUERIES : + * CREATE COMMAND TRIGGER ... BEFORE|AFTER COMMAND ... + * DROP COMMAND TRIGGER ... BEFORE|AFTER ... + * + *****************************************************************************/ + + CreateCmdTrigStmt: + CREATE COMMAND TRIGGER name CmdTriggerActionTime trigger_command + 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 = "ANY"; + n->funcname = $10; + $$ = (Node *)n; + } + ; + + CmdTriggerActionTime: + BEFORE { $$ = CMD_TRIGGER_FIRED_BEFORE; } + | AFTER { $$ = CMD_TRIGGER_FIRED_AFTER; } + ; + + /* + * 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 LANGUAGE { $$ = "ALTER LANGUAGE"; } + | 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 SEQUENCE { $$ = "ALTER SEQUENCE"; } + | ALTER VIEW { $$ = "ALTER VIEW"; } + | 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"; } + | ALTER CONVERSION_P { $$ = "ALTER CONVERSION"; } + | 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"; } + | 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 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 opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_CMDTRIGGER; + n->objects = list_make1(list_make1(makeString($4))); + n->behavior = $5; + n->missing_ok = false; + $$ = (Node *) n; + } + | DROP COMMAND TRIGGER IF_P EXISTS name opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_CMDTRIGGER; + n->objects = list_make1(list_make1(makeString($6))); + n->behavior = $7; + n->missing_ok = true; + $$ = (Node *) n; + } + ; + + AlterCmdTrigStmt: + ALTER COMMAND TRIGGER name SET enable_trigger + { + AlterCmdTrigStmt *n = makeNode(AlterCmdTrigStmt); + n->trigname = $4; + n->tgenabled = $6; + $$ = (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 --- 6934,6947 ---- n->missing_ok = false; $$ = (Node *)n; } + | ALTER COMMAND TRIGGER name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_CMDTRIGGER; + n->object = list_make1(makeString($4)); + n->newname = $7; + $$ = (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,237 **** DefineQueryRewrite(char *rulename, CmdType event_type, bool is_instead, bool replace, ! List *action) { Relation event_relation; int event_attno; ListCell *l; --- 235,244 ---- CmdType event_type, bool is_instead, bool replace, ! List *action, ! CommandContext cmd) { + Oid ruleOid; Relation event_relation; int event_attno; ListCell *l; *************** *** 480,485 **** DefineQueryRewrite(char *rulename, --- 487,503 ---- } } + /* 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. */ *************** *** 488,501 **** DefineQueryRewrite(char *rulename, /* discard rule if it's null action and not INSTEAD; it's a no-op */ if (action != NIL || is_instead) { ! InsertRule(rulename, ! event_type, ! event_relid, ! event_attno, ! is_instead, ! event_qual, ! action, ! replace); /* * Set pg_class 'relhasrules' field TRUE for event relation. If --- 506,519 ---- /* discard rule if it's null action and not INSTEAD; it's a no-op */ if (action != NIL || is_instead) { ! ruleOid = InsertRule(rulename, ! event_type, ! event_relid, ! event_attno, ! is_instead, ! event_qual, ! action, ! replace); /* * Set pg_class 'relhasrules' field TRUE for event relation. If *************** *** 520,525 **** DefineQueryRewrite(char *rulename, --- 538,550 ---- /* Close rel, but keep lock till commit... */ heap_close(event_relation, NoLock); + + /* Call AFTER CREATE RULE triggers */ + if (CommandFiresAfterTriggers(cmd)) + { + cmd->objectId = ruleOid; + 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,320 ---- } /* + * Support function for calling the command triggers. + */ + static void + call_before_cmdtriggers(Node *parsetree, CommandContext cmd) + { + switch (nodeTag(parsetree)) + { + case T_AlterDomainStmt: + case T_AlterFunctionStmt: + case T_AlterObjectSchemaStmt: + case T_AlterOwnerStmt: + case T_AlterSeqStmt: + case T_AlterTableStmt: + case T_CommentStmt: + case T_DefineStmt: + case T_CreateCastStmt: + case T_CreateConversionStmt: + case T_CreatedbStmt: + case T_CreateDomainStmt: + case T_CreateFunctionStmt: + 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_CreateTrigStmt: + case T_CompositeTypeStmt: + case T_CreateEnumStmt: + case T_CreateRangeStmt: + case T_AlterEnumStmt: + case T_ViewStmt: + 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_CreateForeignTableStmt: + case T_ClusterStmt: + case T_VacuumStmt: + case T_LoadStmt: + case T_ReindexStmt: + if (CommandFiresTriggers(cmd)) + ExecBeforeCommandTriggers(cmd); + return; + + case T_RenameStmt: + if (((RenameStmt *) parsetree)->renameType != OBJECT_CMDTRIGGER) + if (CommandFiresTriggers(cmd)) + ExecBeforeCommandTriggers(cmd); + return; + + case T_DropStmt: + if (((DropStmt *) parsetree)->removeType != OBJECT_CMDTRIGGER) + if (CommandFiresTriggers(cmd)) + ExecBeforeCommandTriggers(cmd); + return; + + default: + /* commands that don't support triggers */ + return; + } + } + + static void + call_after_cmdtriggers(Node *parsetree, CommandContext cmd) + { + switch (nodeTag(parsetree)) + { + case T_AlterDomainStmt: + case T_AlterFunctionStmt: + case T_AlterObjectSchemaStmt: + case T_AlterOwnerStmt: + case T_AlterSeqStmt: + case T_AlterTableStmt: + case T_CommentStmt: + case T_DefineStmt: + case T_CreateCastStmt: + case T_CreateConversionStmt: + case T_CreateDomainStmt: + case T_CreateFunctionStmt: + 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_CreateTrigStmt: + case T_CompositeTypeStmt: + case T_CreateEnumStmt: + case T_CreateRangeStmt: + case T_AlterEnumStmt: + case T_ViewStmt: + 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: + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); + return; + + case T_IndexStmt: + if (!((IndexStmt *)cmd->parsetree)->concurrent) + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); + return; + + case T_RenameStmt: + if (((RenameStmt *) parsetree)->renameType != OBJECT_CMDTRIGGER) + if (CommandFiresTriggers(cmd)) + ExecAfterCommandTriggers(cmd); + return; + + case T_DropStmt: + if (((DropStmt *) parsetree)->removeType != OBJECT_CMDTRIGGER) + if (CommandFiresAfterTriggers(cmd)) + ExecAfterCommandTriggers(cmd); + return; + + 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) --- 349,356 ---- 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, --- 511,527 ---- 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(parsetree, &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) --- 682,698 ---- { List *stmts; ListCell *l; ! Oid relOid = InvalidOid; ! CreateStmt *stmt = (CreateStmt *) parsetree; ! CommandContextData cmd; ! ! /* ! * Call BEFORE CREATE TABLE triggers ! */ ! InitCommandContext(&cmd, parsetree, false); /* Run parse analysis ... */ ! stmts = transformCreateStmt(stmt, queryString); /* ... and do it */ foreach(l, stmts) *************** *** 528,534 **** standard_ProcessUtility(Node *parsetree, /* Create the table itself */ relOid = DefineRelation((CreateStmt *) stmt, RELKIND_RELATION, ! InvalidOid); /* * Let AlterTableCreateToastTable decide if this one --- 707,714 ---- /* Create the table itself */ relOid = DefineRelation((CreateStmt *) stmt, RELKIND_RELATION, ! InvalidOid, ! &cmd); /* * Let AlterTableCreateToastTable decide if this one *************** *** 552,558 **** standard_ProcessUtility(Node *parsetree, /* Create the table itself */ relOid = DefineRelation((CreateStmt *) stmt, RELKIND_FOREIGN_TABLE, ! InvalidOid); CreateForeignTable((CreateForeignTableStmt *) stmt, relOid); } --- 732,739 ---- /* Create the table itself */ relOid = DefineRelation((CreateStmt *) stmt, RELKIND_FOREIGN_TABLE, ! InvalidOid, ! &cmd); CreateForeignTable((CreateForeignTableStmt *) stmt, relOid); } *************** *** 571,576 **** standard_ProcessUtility(Node *parsetree, --- 752,764 ---- 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, --- 892,898 ---- List *stmts; ListCell *l; LOCKMODE lockmode; + CommandContextData cmd; /* * Figure out lock mode, and acquire lock. This also does *************** *** 716,721 **** standard_ProcessUtility(Node *parsetree, --- 905,925 ---- 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, --- 948,956 ---- if (lnext(l) != NULL) CommandCounterIncrement(); } + /* Call AFTER ALTER TABLE triggers */ + if (CommandFiresAfterTriggers(&cmd)) + ExecAfterCommandTriggers(&cmd); } else ereport(NOTICE, *************** *** 755,760 **** standard_ProcessUtility(Node *parsetree, --- 962,973 ---- 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", --- 982,1011 ---- * 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", --- 1033,1075 ---- 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; --- 1082,1091 ---- 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, --- 1123,1131 ---- 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; --- 1156,1163 ---- 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; --- 1195,1201 ---- DropdbStmt *stmt = (DropdbStmt *) parsetree; PreventTransactionChain(isTopLevel, "DROP DATABASE"); ! dropdb(stmt); } break; *************** *** 1012,1021 **** standard_ProcessUtility(Node *parsetree, --- 1235,1258 ---- 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, --- 1296,1309 ---- 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: --- 1376,1394 ---- 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, --- 1445,1453 ---- (int) nodeTag(parsetree)); break; } + + /* call the AFTER ANY COMMAND triggers */ + call_after_cmdtriggers(parsetree, &cmd); } /* *************** *** 1435,1440 **** AlterObjectTypeCommandTag(ObjectType objtype) --- 1686,1694 ---- case OBJECT_TRIGGER: tag = "ALTER TRIGGER"; break; + case OBJECT_CMDTRIGGER: + tag = "ALTER COMMAND TRIGGER"; + break; case OBJECT_TSCONFIGURATION: tag = "ALTER TEXT SEARCH CONFIGURATION"; break; *************** *** 1704,1709 **** CreateCommandTag(Node *parsetree) --- 1958,1966 ---- 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) --- 2207,2220 ---- 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) --- 2416,2428 ---- 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) --- 2717,2734 ---- 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/format_type.c --- b/src/backend/utils/adt/format_type.c *************** *** 28,34 **** #define MAX_INT32_LEN 11 static char *format_type_internal(Oid type_oid, int32 typemod, ! bool typemod_given, bool allow_invalid); static char *printTypmod(const char *typname, int32 typmod, Oid typmodout); static char * psnprintf(size_t len, const char *fmt,...) --- 28,35 ---- #define MAX_INT32_LEN 11 static char *format_type_internal(Oid type_oid, int32 typemod, ! bool typemod_given, bool allow_invalid, ! bool qualify); static char *printTypmod(const char *typname, int32 typmod, Oid typmodout); static char * psnprintf(size_t len, const char *fmt,...) *************** *** 76,86 **** format_type(PG_FUNCTION_ARGS) type_oid = PG_GETARG_OID(0); if (PG_ARGISNULL(1)) ! result = format_type_internal(type_oid, -1, false, true); else { typemod = PG_GETARG_INT32(1); ! result = format_type_internal(type_oid, typemod, true, true); } PG_RETURN_TEXT_P(cstring_to_text(result)); --- 77,87 ---- type_oid = PG_GETARG_OID(0); if (PG_ARGISNULL(1)) ! result = format_type_internal(type_oid, -1, false, true, true); else { typemod = PG_GETARG_INT32(1); ! result = format_type_internal(type_oid, typemod, true, true, true); } PG_RETURN_TEXT_P(cstring_to_text(result)); *************** *** 95,101 **** format_type(PG_FUNCTION_ARGS) char * format_type_be(Oid type_oid) { ! return format_type_internal(type_oid, -1, false, false); } /* --- 96,113 ---- char * format_type_be(Oid type_oid) { ! return format_type_internal(type_oid, -1, false, false, true); ! } ! ! /* ! * Allow formating a type name without namespace, useful for command context ! * where we probide object name and namespace separately and still want nice ! * formating of type names. ! */ ! char * ! format_type_be_without_namespace(Oid type_oid) ! { ! return format_type_internal(type_oid, -1, false, false, false); } /* *************** *** 104,117 **** format_type_be(Oid type_oid) char * format_type_with_typemod(Oid type_oid, int32 typemod) { ! return format_type_internal(type_oid, typemod, true, false); } static char * format_type_internal(Oid type_oid, int32 typemod, ! bool typemod_given, bool allow_invalid) { bool with_typemod = typemod_given && (typemod >= 0); HeapTuple tuple; --- 116,129 ---- char * format_type_with_typemod(Oid type_oid, int32 typemod) { ! return format_type_internal(type_oid, typemod, true, false, true); } static char * format_type_internal(Oid type_oid, int32 typemod, ! bool typemod_given, bool allow_invalid, bool qualify) { bool with_typemod = typemod_given && (typemod >= 0); HeapTuple tuple; *************** *** 299,305 **** format_type_internal(Oid type_oid, int32 typemod, char *nspname; char *typname; ! if (TypeIsVisible(type_oid)) nspname = NULL; else nspname = get_namespace_name(typeform->typnamespace); --- 311,319 ---- char *nspname; char *typname; ! if (!qualify) ! nspname = NULL; ! else if (TypeIsVisible(type_oid)) nspname = NULL; else nspname = get_namespace_name(typeform->typnamespace); *************** *** 420,426 **** oidvectortypes(PG_FUNCTION_ARGS) for (num = 0; num < numargs; num++) { char *typename = format_type_internal(oidArray->values[num], -1, ! false, true); size_t slen = strlen(typename); if (left < (slen + 2)) --- 434,440 ---- for (num = 0; num < numargs; num++) { char *typename = format_type_internal(oidArray->values[num], -1, ! false, true, true); size_t slen = strlen(typename); if (left < (slen + 2)) *** 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/common.c --- b/src/bin/pg_dump/common.c *************** *** 98,103 **** getSchemaData(Archive *fout, int *numTablesPtr) --- 98,104 ---- int numForeignDataWrappers; int numForeignServers; int numDefaultACLs; + int numCmdTriggers; if (g_verbose) write_msg(NULL, "reading schemas\n"); *************** *** 234,239 **** getSchemaData(Archive *fout, int *numTablesPtr) --- 235,244 ---- write_msg(NULL, "reading triggers\n"); getTriggers(fout, tblinfo, numTables); + if (g_verbose) + write_msg(NULL, "reading command triggers\n"); + getCmdTriggers(fout, &numCmdTriggers); + *numTablesPtr = numTables; return tblinfo; } *** 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,5361 ---- } /* + * 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, " + "n.nspname || '.' || p.proname as ctgfname, ctgenabled " + "FROM pg_cmdtrigger c JOIN pg_proc p on c.ctgfoid = p.oid " + "JOIN pg_namespace n ON p.pronamespace = n.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) --- 7246,7254 ---- 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) --- 13721,13797 ---- destroyPQExpBuffer(labelq); } + static void + dumpCmdTrigger(Archive *fout, CmdTriggerInfo *ctginfo) + { + PQExpBuffer query; + PQExpBuffer labelq; + + query = createPQExpBuffer(); + labelq = createPQExpBuffer(); + + appendPQExpBuffer(query, "CREATE COMMAND 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, ctginfo->ctgcommand); + } + + appendPQExpBuffer(query, " EXECUTE PROCEDURE "); + appendPQExpBufferStr(query, ctginfo->ctgfname); + appendPQExpBuffer(query, " ();\n"); + + if (ctginfo->ctgenabled != 'O') + { + appendPQExpBuffer(query, "\nALTER COMMAND TRIGGER %s SET ", + fmtId(ctginfo->dobj.name)); + switch (ctginfo->ctgenabled) + { + case 'D': + appendPQExpBuffer(query, "DISABLE"); + break; + case 'A': + appendPQExpBuffer(query, "ENABLE ALWAYS"); + break; + case 'R': + appendPQExpBuffer(query, "ENABLE REPLICA"); + break; + default: + appendPQExpBuffer(query, "ENABLE"); + break; + } + appendPQExpBuffer(query, ";\n"); + } + appendPQExpBuffer(labelq, "COMMAND TRIGGER %s ", + fmtId(ctginfo->dobj.name)); + + 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,246 ---- DECLARE_UNIQUE_INDEX(pg_trigger_oid_index, 2702, on pg_trigger using btree(oid oid_ops)); #define TriggerOidIndexId 2702 + DECLARE_UNIQUE_INDEX(pg_cmdtrigger_ctgname_index, 3467, on pg_cmdtrigger using btree(ctgname name_ops)); + #define CmdTriggerNameIndexId 3467 + DECLARE_INDEX(pg_cmdtrigger_ctgcommand_index, 3468, on pg_cmdtrigger using btree(ctgcommand name_ops)); + #define CmdTriggerCommandNameIndexId 3468 + DECLARE_UNIQUE_INDEX(pg_cmdtrigger_oid_index, 3469, on pg_cmdtrigger using btree(oid oid_ops)); + #define CmdTriggerOidIndexId 3469 + 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 ctgname; /* trigger's name */ + NameData ctgcommand; /* trigger's command */ + 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_ctgname 1 + #define Anum_pg_cmdtrigger_ctgcommand 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,55 ---- + /*------------------------------------------------------------------------- + * + * 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 */ + MemoryContext oldmctx; /* Memory Context to switch back to */ + MemoryContext cmdmctx; /* Memory Context for the command triggers */ + } 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, bool missing_ok); + extern void AlterCmdTrigger(AlterCmdTrigStmt *stmt); + extern void RenameCmdTrigger(List *name, const char *newname); + + extern void InitCommandContext(CommandContext cmd, const Node *stmt, bool list_triggers); + 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); *************** *** 99,156 **** extern void RemoveOpClassById(Oid opclassOid); extern void RemoveOpFamilyById(Oid opfamilyOid); extern void RemoveAmOpEntryById(Oid entryOid); extern void RemoveAmProcEntryById(Oid entryOid); ! extern void RenameOpClass(List *name, const char *access_method, const char *newname); ! 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); extern List *deserialize_deflist(Datum txt); /* commands/foreigncmds.c */ ! extern void RenameForeignServer(const char *oldname, const char *newname); ! extern void RenameForeignDataWrapper(const char *oldname, const char *newname); ! extern void AlterForeignServerOwner(const char *name, Oid newOwnerId); extern void AlterForeignServerOwner_oid(Oid , Oid newOwnerId); ! extern void AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId); extern void AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId); extern void CreateForeignDataWrapper(CreateFdwStmt *stmt); extern void AlterForeignDataWrapper(AlterFdwStmt *stmt); --- 103,166 ---- extern void RemoveOpFamilyById(Oid opfamilyOid); extern void RemoveAmOpEntryById(Oid entryOid); extern void RemoveAmProcEntryById(Oid entryOid); ! extern void RenameOpClass(List *name, const char *access_method, ! const char *newname, CommandContext cmd); ! extern void RenameOpFamily(List *name, const char *access_method, ! const char *newname, CommandContext cmd); ! extern void AlterOpClassOwner(List *name, const char *access_method, ! Oid newOwnerId, CommandContext cmd); 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, CommandContext cmd); 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); extern List *deserialize_deflist(Datum txt); /* commands/foreigncmds.c */ ! extern void RenameForeignServer(const char *oldname, const char *newname, CommandContext cmd); ! extern void RenameForeignDataWrapper(const char *oldname, const char *newname, CommandContext cmd); ! extern void AlterForeignServerOwner(const char *name, Oid newOwnerId, CommandContext cmd); extern void AlterForeignServerOwner_oid(Oid , Oid newOwnerId); ! extern void AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId, CommandContext cmd); extern void AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId); extern void CreateForeignDataWrapper(CreateFdwStmt *stmt); extern void AlterForeignDataWrapper(AlterFdwStmt *stmt); *** 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/proclang.h --- b/src/include/commands/proclang.h *************** *** 12,23 **** #ifndef PROCLANG_H #define PROCLANG_H #include "nodes/parsenodes.h" extern void CreateProceduralLanguage(CreatePLangStmt *stmt); extern void DropProceduralLanguageById(Oid langOid); ! extern void RenameLanguage(const char *oldname, const char *newname); ! extern void AlterLanguageOwner(const char *name, Oid newOwnerId); extern void AlterLanguageOwner_oid(Oid oid, Oid newOwnerId); extern bool PLTemplateExists(const char *languageName); extern Oid get_language_oid(const char *langname, bool missing_ok); --- 12,24 ---- #ifndef PROCLANG_H #define PROCLANG_H + #include "commands/cmdtrigger.h" #include "nodes/parsenodes.h" extern void CreateProceduralLanguage(CreatePLangStmt *stmt); extern void DropProceduralLanguageById(Oid langOid); ! extern void RenameLanguage(const char *oldname, const char *newname, CommandContext cmd); ! extern void AlterLanguageOwner(const char *name, Oid newOwnerId, CommandContext cmd); extern void AlterLanguageOwner_oid(Oid oid, Oid newOwnerId); extern bool PLTemplateExists(const char *languageName); extern Oid get_language_oid(const char *langname, bool missing_ok); *** 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,26 **** #define TABLECMDS_H #include "access/htup.h" #include "nodes/parsenodes.h" #include "storage/lock.h" #include "utils/relcache.h" ! extern Oid DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId); extern void RemoveRelations(DropStmt *drop); --- 15,28 ---- #define TABLECMDS_H #include "access/htup.h" + #include "commands/cmdtrigger.h" #include "nodes/parsenodes.h" #include "storage/lock.h" #include "utils/relcache.h" ! extern Oid DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, ! CommandContext cmd); extern void RemoveRelations(DropStmt *drop); *************** *** 46,57 **** extern void ExecuteTruncate(TruncateStmt *stmt); 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, --- 48,59 ---- extern void SetRelationHasSubclass(Oid relationId, bool relhassubclass); ! extern void renameatt(RenameStmt *stmt, CommandContext cmd); ! 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(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(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, *************** *** 1730,1735 **** typedef struct CreateTrigStmt --- 1731,1763 ---- } CreateTrigStmt; /* ---------------------- + * Create COMMAND TRIGGER Statement + * ---------------------- + */ + typedef struct CreateCmdTrigStmt + { + NodeTag type; + char *command; /* command's 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 *trigname; /* TRIGGER's name */ + char *tgenabled; /* trigger's firing configuration WRT + * session_replication_role */ + } AlterCmdTrigStmt; + + /* ---------------------- + * Create/Drop PROCEDURAL LANGUAGE Statements * Create PROCEDURAL LANGUAGE Statements * ---------------------- */ *************** *** 1912,1917 **** 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); *** a/src/include/utils/builtins.h --- b/src/include/utils/builtins.h *************** *** 1011,1016 **** extern Datum pg_encoding_max_length_sql(PG_FUNCTION_ARGS); --- 1011,1017 ---- /* format_type.c */ extern Datum format_type(PG_FUNCTION_ARGS); extern char *format_type_be(Oid type_oid); + extern char *format_type_be_without_namespace(Oid type_oid); extern char *format_type_with_typemod(Oid type_oid, int32 typemod); extern Datum oidvectortypes(PG_FUNCTION_ARGS); extern int32 type_maximum_size(Oid type_oid, int32 typemod); *** /dev/null --- b/src/test/regress/expected/cmdtriggers.out *************** *** 0 **** --- 1,597 ---- + -- + -- COMMAND TRIGGERS + -- + create or replace function any_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 + -- don't output objectname and schemaname, NULL in an ANY command trigger + raise notice 'snitch: % any %', tg_when, cmd_tag; + end; + $$; + 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 any_snitch(); + create command trigger snitch_after_ after any command execute procedure any_snitch(); + alter command trigger snitch_before set disable; + alter command trigger snitch_before set enable; + alter command trigger snitch_after_ rename to snitch_after; + create command trigger snitch_create_table after create table execute procedure snitch(); + create command trigger snitch_create_seq after create sequence execute procedure snitch(); + create command trigger snitch_create_view after create view execute procedure snitch(); + create command trigger snitch_alter_table after alter table execute procedure snitch(); + create command trigger snitch_alter_seq after alter sequence execute procedure snitch(); + create command trigger snitch_alter_view after alter view execute procedure snitch(); + create command trigger snitch_drop_table after drop table execute procedure snitch(); + create command trigger snitch_create_function after create function execute procedure snitch(); + create command trigger snitch_create_collation after create collation execute procedure snitch(); + create command trigger snitch_alter_operator after alter operator execute procedure snitch(); + create command trigger snitch_create_domain after create domain execute procedure snitch(); + create command trigger snitch_alter_schema after alter schema execute procedure snitch(); + create command trigger snitch_create_tsconfig after create text search configuration execute procedure snitch(); + create command trigger snitch_create_tsdict after create text search dictionary execute procedure snitch(); + create command trigger snitch_create_tsparser after create text search parser execute procedure snitch(); + create command trigger snitch_create_tstmpl after create text search template execute procedure snitch(); + create command trigger snitch_after_alter_function after alter function execute procedure snitch(); + create command trigger snitch_create_cast after create cast execute procedure snitch(); + create command trigger snitch_create_trigger before create trigger execute procedure snitch(); + create command trigger snitch_alter_trigger before alter trigger execute procedure snitch(); + create command trigger snitch_drop_trigger before drop trigger execute procedure snitch(); + create command trigger snitch_create_schema before create schema execute procedure snitch(); + create command trigger snitch_drop_schema before drop schema execute procedure snitch(); + create command trigger snitch_create_aggregate before create aggregate execute procedure snitch(); + create command trigger snitch_alter_collation before alter collation execute procedure snitch(); + create command trigger snitch_create_operator before create operator execute procedure snitch(); + create command trigger snitch_alter_domain before alter domain execute procedure snitch(); + create command trigger snitch_create_type before create type execute procedure snitch(); + create command trigger snitch_alter_type before alter type execute procedure snitch(); + create command trigger snitch_before_alter_function before alter function execute procedure snitch(); + create command trigger snitch_alter_conversion before alter conversion execute procedure snitch(); + create command trigger snitch_drop_agg before drop aggregate execute procedure snitch(); + create command trigger snitch_before_drop_tsconf before drop text search configuration execute procedure snitch(); + create command trigger snitch_before_drop_tsdict before drop text search dictionary execute procedure snitch(); + create command trigger snitch_before_drop_tsparser before drop text search parser execute procedure snitch(); + create command trigger snitch_before_drop_tstmpl before drop text search template execute procedure snitch(); + create command trigger snitch_vacuum before vacuum execute procedure snitch(); + create command trigger snitch_reindex before reindex execute procedure snitch(); + WARNING: REINDEX DATABASE is not supported + DETAIL: The command trigger will not fire on REINDEX DATABASE. + create schema cmd; + NOTICE: snitch: BEFORE any CREATE SCHEMA + NOTICE: snitch: BEFORE CREATE SCHEMA cmd + NOTICE: snitch: AFTER any CREATE SCHEMA + create schema cmd2; + NOTICE: snitch: BEFORE any CREATE SCHEMA + NOTICE: snitch: BEFORE CREATE SCHEMA cmd2 + NOTICE: snitch: AFTER any CREATE SCHEMA + create role regbob; + ERROR: role "regbob" already exists + create table cmd.foo(id bigserial primary key); + NOTICE: snitch: BEFORE any CREATE TABLE + NOTICE: CREATE TABLE will create implicit sequence "foo_id_seq" for serial column "foo.id" + NOTICE: snitch: BEFORE any CREATE SEQUENCE + NOTICE: snitch: AFTER CREATE SEQUENCE cmd foo_id_seq + NOTICE: snitch: AFTER any CREATE SEQUENCE + NOTICE: snitch: BEFORE any CREATE INDEX + NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo" + NOTICE: snitch: AFTER any CREATE INDEX + NOTICE: snitch: BEFORE any ALTER SEQUENCE + NOTICE: snitch: AFTER ALTER SEQUENCE cmd foo_id_seq + NOTICE: snitch: AFTER any ALTER SEQUENCE + NOTICE: snitch: AFTER CREATE TABLE cmd foo + NOTICE: snitch: AFTER any CREATE TABLE + create view cmd.v as select * from cmd.foo; + NOTICE: snitch: BEFORE any CREATE VIEW + NOTICE: snitch: AFTER CREATE VIEW cmd v + NOTICE: snitch: AFTER any CREATE VIEW + alter table cmd.foo add column t text; + NOTICE: snitch: BEFORE any ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE cmd foo + NOTICE: snitch: AFTER any ALTER TABLE + create table test9 (id int, stuff text); + NOTICE: snitch: BEFORE any CREATE TABLE + NOTICE: snitch: AFTER CREATE TABLE public test9 + NOTICE: snitch: AFTER any CREATE TABLE + alter table test9 rename to test; + NOTICE: snitch: BEFORE any ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE public test + NOTICE: snitch: AFTER any ALTER TABLE + alter table test set schema cmd; + NOTICE: snitch: BEFORE any ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE cmd test + NOTICE: snitch: AFTER any ALTER TABLE + alter table cmd.test rename column stuff to things; + NOTICE: snitch: BEFORE any ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE cmd test + NOTICE: snitch: AFTER any ALTER TABLE + alter table cmd.test add column alpha text; + NOTICE: snitch: BEFORE any ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE cmd test + NOTICE: snitch: AFTER any ALTER TABLE + alter table cmd.test alter column alpha set data type varchar(300); + NOTICE: snitch: BEFORE any ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE cmd test + NOTICE: snitch: AFTER any ALTER TABLE + alter table cmd.test alter column alpha set default 'test'; + NOTICE: snitch: BEFORE any ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE cmd test + NOTICE: snitch: AFTER any ALTER TABLE + alter table cmd.test alter column alpha drop default; + NOTICE: snitch: BEFORE any ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE cmd test + NOTICE: snitch: AFTER any ALTER TABLE + alter table cmd.test alter column alpha set statistics 78; + NOTICE: snitch: BEFORE any ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE cmd test + NOTICE: snitch: AFTER any ALTER TABLE + alter table cmd.test alter column alpha set storage plain; + NOTICE: snitch: BEFORE any ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE cmd test + NOTICE: snitch: AFTER any ALTER TABLE + alter table cmd.test alter column alpha set not null; + NOTICE: snitch: BEFORE any ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE cmd test + NOTICE: snitch: AFTER any ALTER TABLE + alter table cmd.test alter column alpha drop not null; + NOTICE: snitch: BEFORE any ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE cmd test + NOTICE: snitch: AFTER any ALTER TABLE + alter table cmd.test alter column alpha set (n_distinct = -0.78); + NOTICE: snitch: BEFORE any ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE cmd test + NOTICE: snitch: AFTER any ALTER TABLE + alter table cmd.test alter column alpha reset (n_distinct); + NOTICE: snitch: BEFORE any ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE cmd test + NOTICE: snitch: AFTER any ALTER TABLE + alter table cmd.test drop column alpha; + NOTICE: snitch: BEFORE any ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE cmd test + NOTICE: snitch: AFTER any ALTER TABLE + alter table cmd.test add check (id > 2) not valid; + NOTICE: snitch: BEFORE any ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE cmd test + NOTICE: snitch: AFTER any ALTER TABLE + alter table cmd.test add check (id < 800000); + NOTICE: snitch: BEFORE any ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE cmd test + NOTICE: snitch: AFTER any ALTER TABLE + alter table cmd.test set without cluster; + NOTICE: snitch: BEFORE any ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE cmd test + NOTICE: snitch: AFTER any ALTER TABLE + alter table cmd.test set with oids; + NOTICE: snitch: BEFORE any ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE cmd test + NOTICE: snitch: AFTER any ALTER TABLE + alter table cmd.test set without oids; + NOTICE: snitch: BEFORE any ALTER TABLE + NOTICE: snitch: AFTER ALTER TABLE cmd test + NOTICE: snitch: AFTER any ALTER TABLE + create sequence test_seq_; + NOTICE: snitch: BEFORE any CREATE SEQUENCE + NOTICE: snitch: AFTER CREATE SEQUENCE public test_seq_ + NOTICE: snitch: AFTER any CREATE SEQUENCE + alter sequence test_seq_ owner to regbob; + NOTICE: snitch: BEFORE any ALTER SEQUENCE + NOTICE: snitch: AFTER ALTER SEQUENCE public test_seq_ + NOTICE: snitch: AFTER any ALTER SEQUENCE + alter sequence test_seq_ rename to test_seq; + NOTICE: snitch: BEFORE any ALTER SEQUENCE + NOTICE: snitch: AFTER ALTER SEQUENCE public test_seq + NOTICE: snitch: AFTER any ALTER SEQUENCE + alter sequence test_seq set schema cmd; + NOTICE: snitch: BEFORE any ALTER SEQUENCE + NOTICE: snitch: AFTER ALTER SEQUENCE cmd test_seq + NOTICE: snitch: AFTER any ALTER SEQUENCE + alter sequence cmd.test_seq start with 3; + NOTICE: snitch: BEFORE any ALTER SEQUENCE + NOTICE: snitch: AFTER ALTER SEQUENCE cmd test_seq + NOTICE: snitch: AFTER any ALTER SEQUENCE + alter sequence cmd.test_seq restart with 4; + NOTICE: snitch: BEFORE any ALTER SEQUENCE + NOTICE: snitch: AFTER ALTER SEQUENCE cmd test_seq + NOTICE: snitch: AFTER any ALTER SEQUENCE + alter sequence cmd.test_seq minvalue 3; + NOTICE: snitch: BEFORE any ALTER SEQUENCE + NOTICE: snitch: AFTER ALTER SEQUENCE cmd test_seq + NOTICE: snitch: AFTER any ALTER SEQUENCE + alter sequence cmd.test_seq no minvalue; + NOTICE: snitch: BEFORE any ALTER SEQUENCE + NOTICE: snitch: AFTER ALTER SEQUENCE cmd test_seq + NOTICE: snitch: AFTER any ALTER SEQUENCE + alter sequence cmd.test_seq maxvalue 900000; + NOTICE: snitch: BEFORE any ALTER SEQUENCE + NOTICE: snitch: AFTER ALTER SEQUENCE cmd test_seq + NOTICE: snitch: AFTER any ALTER SEQUENCE + alter sequence cmd.test_seq no maxvalue; + NOTICE: snitch: BEFORE any ALTER SEQUENCE + NOTICE: snitch: AFTER ALTER SEQUENCE cmd test_seq + NOTICE: snitch: AFTER any ALTER SEQUENCE + alter sequence cmd.test_seq cache 876; + NOTICE: snitch: BEFORE any ALTER SEQUENCE + NOTICE: snitch: AFTER ALTER SEQUENCE cmd test_seq + NOTICE: snitch: AFTER any ALTER SEQUENCE + alter sequence cmd.test_seq cycle; + NOTICE: snitch: BEFORE any ALTER SEQUENCE + NOTICE: snitch: AFTER ALTER SEQUENCE cmd test_seq + NOTICE: snitch: AFTER any ALTER SEQUENCE + alter sequence cmd.test_seq no cycle; + NOTICE: snitch: BEFORE any ALTER SEQUENCE + NOTICE: snitch: AFTER ALTER SEQUENCE cmd test_seq + NOTICE: snitch: AFTER any ALTER SEQUENCE + create view view_test as select id, things from cmd.test; + NOTICE: snitch: BEFORE any CREATE VIEW + NOTICE: snitch: AFTER CREATE VIEW public view_test + NOTICE: snitch: AFTER any CREATE VIEW + alter view view_test owner to regbob; + NOTICE: snitch: BEFORE any ALTER VIEW + NOTICE: snitch: AFTER ALTER VIEW public view_test + NOTICE: snitch: AFTER any ALTER VIEW + alter view view_test rename to view_test2; + NOTICE: snitch: BEFORE any ALTER VIEW + NOTICE: snitch: AFTER ALTER VIEW public view_test2 + NOTICE: snitch: AFTER any ALTER VIEW + alter view view_test2 set schema cmd; + NOTICE: snitch: BEFORE any ALTER VIEW + NOTICE: snitch: AFTER ALTER VIEW cmd view_test2 + NOTICE: snitch: AFTER any ALTER VIEW + alter view cmd.view_test2 alter column id set default 9; + NOTICE: snitch: BEFORE any ALTER VIEW + NOTICE: snitch: AFTER ALTER VIEW cmd view_test2 + NOTICE: snitch: AFTER any ALTER VIEW + alter view cmd.view_test2 alter column id drop default; + NOTICE: snitch: BEFORE any ALTER VIEW + NOTICE: snitch: AFTER ALTER VIEW cmd view_test2 + NOTICE: snitch: AFTER any ALTER VIEW + cluster cmd.foo using foo_pkey; + NOTICE: snitch: BEFORE any CLUSTER + vacuum cmd.foo; + NOTICE: snitch: BEFORE any VACUUM + NOTICE: snitch: BEFORE VACUUM cmd foo + vacuum; + NOTICE: snitch: BEFORE any VACUUM + NOTICE: snitch: BEFORE VACUUM + reindex table cmd.foo; + NOTICE: snitch: BEFORE any REINDEX + NOTICE: snitch: BEFORE REINDEX cmd + NOTICE: snitch: AFTER any REINDEX + 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 any CREATE INDEX + NOTICE: snitch: AFTER any CREATE INDEX + drop index cmd.idx_foo; + NOTICE: snitch: BEFORE any DROP INDEX + NOTICE: snitch: AFTER any DROP INDEX + create function fun(int) returns text language sql + as $$ select t from cmd.foo where id = $1; $$; + NOTICE: snitch: BEFORE any CREATE FUNCTION + NOTICE: snitch: AFTER CREATE FUNCTION public fun + NOTICE: snitch: AFTER any CREATE FUNCTION + alter function fun(int) strict; + NOTICE: snitch: BEFORE any ALTER FUNCTION + NOTICE: snitch: BEFORE ALTER FUNCTION public fun + NOTICE: snitch: AFTER ALTER FUNCTION public fun + NOTICE: snitch: AFTER any ALTER FUNCTION + alter function fun(int) rename to notfun; + NOTICE: snitch: BEFORE any ALTER FUNCTION + NOTICE: snitch: BEFORE ALTER FUNCTION public fun + NOTICE: snitch: AFTER ALTER FUNCTION public notfun + NOTICE: snitch: AFTER any ALTER FUNCTION + alter function notfun(int) set schema cmd; + NOTICE: snitch: BEFORE any ALTER FUNCTION + NOTICE: snitch: BEFORE ALTER FUNCTION public notfun + NOTICE: snitch: AFTER ALTER FUNCTION cmd notfun + NOTICE: snitch: AFTER any ALTER FUNCTION + alter function cmd.notfun(int) owner to regbob; + NOTICE: snitch: BEFORE any ALTER FUNCTION + NOTICE: snitch: BEFORE ALTER FUNCTION cmd notfun + NOTICE: snitch: AFTER ALTER FUNCTION cmd notfun + NOTICE: snitch: AFTER any ALTER FUNCTION + alter function cmd.notfun(int) cost 77; + NOTICE: snitch: BEFORE any ALTER FUNCTION + NOTICE: snitch: BEFORE ALTER FUNCTION cmd notfun + NOTICE: snitch: AFTER ALTER FUNCTION cmd notfun + NOTICE: snitch: AFTER any ALTER FUNCTION + drop function cmd.notfun(int); + NOTICE: snitch: BEFORE any DROP FUNCTION + NOTICE: snitch: AFTER any DROP FUNCTION + create function cmd.plus1(int) returns bigint language sql + as $$ select $1::bigint + 1; $$; + NOTICE: snitch: BEFORE any CREATE FUNCTION + NOTICE: snitch: AFTER CREATE FUNCTION cmd plus1 + NOTICE: snitch: AFTER any CREATE FUNCTION + create operator cmd.+!(procedure = cmd.plus1, leftarg = int); + NOTICE: snitch: BEFORE any CREATE OPERATOR + NOTICE: snitch: BEFORE CREATE OPERATOR cmd +! + NOTICE: snitch: AFTER any CREATE OPERATOR + alter operator cmd.+!(int, NONE) set schema public; + NOTICE: snitch: BEFORE any ALTER OPERATOR + NOTICE: snitch: AFTER ALTER OPERATOR public +! + NOTICE: snitch: AFTER any ALTER OPERATOR + drop operator public.+!(int, NONE); + NOTICE: snitch: BEFORE any DROP OPERATOR + NOTICE: snitch: AFTER any DROP OPERATOR + create aggregate cmd.avg (float8) + ( + sfunc = float8_accum, + stype = float8[], + finalfunc = float8_avg, + initcond = '{0,0,0}' + ); + NOTICE: snitch: BEFORE any CREATE AGGREGATE + NOTICE: snitch: BEFORE CREATE AGGREGATE cmd avg + NOTICE: snitch: AFTER any CREATE AGGREGATE + alter aggregate cmd.avg(float8) set schema public; + NOTICE: snitch: BEFORE any ALTER AGGREGATE + NOTICE: snitch: AFTER any ALTER AGGREGATE + drop aggregate public.avg(float8); + NOTICE: snitch: BEFORE any DROP AGGREGATE + NOTICE: snitch: BEFORE DROP AGGREGATE public avg + NOTICE: snitch: AFTER any DROP AGGREGATE + create collation cmd.french (LOCALE = 'fr_FR'); + NOTICE: snitch: BEFORE any CREATE COLLATION + NOTICE: snitch: AFTER CREATE COLLATION cmd french + NOTICE: snitch: AFTER any CREATE COLLATION + alter collation cmd.french rename to francais; + NOTICE: snitch: BEFORE any ALTER COLLATION + NOTICE: snitch: BEFORE ALTER COLLATION cmd french + NOTICE: snitch: AFTER any ALTER COLLATION + create type cmd.compfoo AS (f1 int, f2 text); + NOTICE: snitch: BEFORE any CREATE TYPE + NOTICE: snitch: BEFORE CREATE TYPE cmd compfoo + NOTICE: snitch: AFTER any CREATE TYPE + alter type cmd.compfoo add attribute f3 text; + NOTICE: snitch: BEFORE any ALTER TYPE + NOTICE: snitch: BEFORE ALTER TYPE cmd compfoo + NOTICE: snitch: AFTER any ALTER TYPE + create type cmd.type_test AS (a integer, b integer, c text); + NOTICE: snitch: BEFORE any CREATE TYPE + NOTICE: snitch: BEFORE CREATE TYPE cmd type_test + NOTICE: snitch: AFTER any CREATE TYPE + alter type cmd.type_test owner to regbob; + NOTICE: snitch: BEFORE any ALTER TYPE + NOTICE: snitch: BEFORE ALTER TYPE cmd type_test + NOTICE: snitch: AFTER any ALTER TYPE + alter type cmd.type_test rename to type_test2; + NOTICE: snitch: BEFORE any ALTER TYPE + NOTICE: snitch: BEFORE ALTER TYPE cmd type_test + NOTICE: snitch: AFTER any ALTER TYPE + alter type cmd.type_test2 set schema public; + NOTICE: snitch: BEFORE any ALTER TYPE + NOTICE: snitch: BEFORE ALTER TYPE cmd type_test2 + NOTICE: snitch: AFTER any ALTER TYPE + alter type public.type_test2 rename attribute a to z; + NOTICE: snitch: BEFORE any ALTER TYPE + NOTICE: snitch: BEFORE ALTER TYPE public type_test2 + NOTICE: snitch: AFTER any ALTER TYPE + alter type public.type_test2 add attribute alpha text; + NOTICE: snitch: BEFORE any ALTER TYPE + NOTICE: snitch: BEFORE ALTER TYPE public type_test2 + NOTICE: snitch: AFTER any ALTER TYPE + alter type public.type_test2 alter attribute alpha set data type char(90); + NOTICE: snitch: BEFORE any ALTER TYPE + NOTICE: snitch: BEFORE ALTER TYPE public type_test2 + NOTICE: snitch: AFTER any ALTER TYPE + alter type public.type_test2 drop attribute alpha; + NOTICE: snitch: BEFORE any ALTER TYPE + NOTICE: snitch: BEFORE ALTER TYPE public type_test2 + NOTICE: snitch: AFTER any ALTER TYPE + drop type cmd.compfoo; + NOTICE: snitch: BEFORE any DROP TYPE + NOTICE: snitch: AFTER any DROP TYPE + drop type public.type_test2; + NOTICE: snitch: BEFORE any DROP TYPE + NOTICE: snitch: AFTER any DROP TYPE + create type cmd.bug_status as enum ('new', 'open', 'closed'); + NOTICE: snitch: BEFORE any CREATE TYPE + NOTICE: snitch: BEFORE CREATE TYPE cmd bug_status + NOTICE: snitch: AFTER any CREATE TYPE + alter type cmd.bug_status add value 'wontfix'; + NOTICE: snitch: BEFORE any ALTER TYPE + NOTICE: snitch: AFTER any ALTER TYPE + create domain cmd.us_postal_code as text check(value ~ '^\d{5}$' or value ~ '^\d{5}-\d{4}$'); + NOTICE: snitch: BEFORE any CREATE DOMAIN + NOTICE: snitch: AFTER CREATE DOMAIN cmd us_postal_code + NOTICE: snitch: AFTER any CREATE DOMAIN + alter domain cmd.us_postal_code set not null; + NOTICE: snitch: BEFORE any ALTER DOMAIN + NOTICE: snitch: BEFORE ALTER DOMAIN cmd us_postal_code + NOTICE: snitch: AFTER any ALTER DOMAIN + alter domain cmd.us_postal_code set default 90210; + NOTICE: snitch: BEFORE any ALTER DOMAIN + NOTICE: snitch: BEFORE ALTER DOMAIN cmd us_postal_code + NOTICE: snitch: AFTER any ALTER DOMAIN + alter domain cmd.us_postal_code drop default; + NOTICE: snitch: BEFORE any ALTER DOMAIN + NOTICE: snitch: BEFORE ALTER DOMAIN cmd us_postal_code + NOTICE: snitch: AFTER any ALTER DOMAIN + alter domain cmd.us_postal_code set not null; + NOTICE: snitch: BEFORE any ALTER DOMAIN + NOTICE: snitch: BEFORE ALTER DOMAIN cmd us_postal_code + NOTICE: snitch: AFTER any ALTER DOMAIN + alter domain cmd.us_postal_code drop not null; + NOTICE: snitch: BEFORE any ALTER DOMAIN + NOTICE: snitch: BEFORE ALTER DOMAIN cmd us_postal_code + NOTICE: snitch: AFTER any ALTER DOMAIN + alter domain cmd.us_postal_code add constraint dummy_constraint check (value ~ '^\d{8}$'); + NOTICE: snitch: BEFORE any ALTER DOMAIN + NOTICE: snitch: BEFORE ALTER DOMAIN cmd us_postal_code + NOTICE: snitch: AFTER any ALTER DOMAIN + alter domain cmd.us_postal_code drop constraint dummy_constraint; + NOTICE: snitch: BEFORE any ALTER DOMAIN + NOTICE: snitch: BEFORE ALTER DOMAIN cmd us_postal_code + NOTICE: snitch: AFTER any ALTER DOMAIN + alter domain cmd.us_postal_code owner to regbob; + NOTICE: snitch: BEFORE any ALTER DOMAIN + NOTICE: snitch: BEFORE ALTER DOMAIN cmd us_postal_code + NOTICE: snitch: AFTER any ALTER DOMAIN + alter domain cmd.us_postal_code set schema cmd2; + NOTICE: snitch: BEFORE any ALTER DOMAIN + NOTICE: snitch: BEFORE ALTER DOMAIN cmd us_postal_code + NOTICE: snitch: AFTER any ALTER DOMAIN + create function cmd.trigfunc() returns trigger language plpgsql as + $$ begin raise notice 'trigfunc'; end;$$; + NOTICE: snitch: BEFORE any CREATE FUNCTION + NOTICE: snitch: AFTER CREATE FUNCTION cmd trigfunc + NOTICE: snitch: AFTER any CREATE FUNCTION + create trigger footg before update on cmd.foo for each row execute procedure cmd.trigfunc(); + NOTICE: snitch: BEFORE any CREATE TRIGGER + NOTICE: snitch: BEFORE CREATE TRIGGER cmd footg + NOTICE: snitch: AFTER any CREATE TRIGGER + alter trigger footg on cmd.foo rename to foo_trigger; + NOTICE: snitch: BEFORE any ALTER TRIGGER + NOTICE: snitch: BEFORE ALTER TRIGGER cmd footg + NOTICE: snitch: AFTER any ALTER TRIGGER + drop trigger foo_trigger on cmd.foo; + NOTICE: snitch: BEFORE any DROP TRIGGER + ERROR: unexpected name list length (3) + create conversion test for 'utf8' to 'sjis' from utf8_to_sjis; + NOTICE: snitch: BEFORE any CREATE CONVERSION + NOTICE: snitch: AFTER any CREATE CONVERSION + create default conversion test2 for 'utf8' to 'sjis' from utf8_to_sjis; + NOTICE: snitch: BEFORE any CREATE CONVERSION + NOTICE: snitch: AFTER any CREATE CONVERSION + alter conversion test2 rename to test3; + NOTICE: snitch: BEFORE any ALTER CONVERSION + NOTICE: snitch: BEFORE ALTER CONVERSION public test2 + NOTICE: snitch: AFTER any ALTER CONVERSION + drop conversion test3; + NOTICE: snitch: BEFORE any DROP CONVERSION + NOTICE: snitch: AFTER any DROP CONVERSION + drop conversion test; + NOTICE: snitch: BEFORE any DROP CONVERSION + NOTICE: snitch: AFTER any DROP CONVERSION + create text search configuration test (parser = "default"); + NOTICE: snitch: BEFORE any CREATE TEXT SEARCH CONFIGURATION + NOTICE: snitch: AFTER CREATE TEXT SEARCH CONFIGURATION public test + NOTICE: snitch: AFTER any CREATE TEXT SEARCH CONFIGURATION + create text search dictionary test_stem ( + template = snowball, + language = 'english', stopwords = 'english' + ); + NOTICE: snitch: BEFORE any CREATE TEXT SEARCH DICTIONARY + NOTICE: snitch: AFTER CREATE TEXT SEARCH DICTIONARY public test_stem + NOTICE: snitch: AFTER any CREATE TEXT SEARCH DICTIONARY + create text search parser test_parser ( + start = prsd_start, + gettoken = prsd_nexttoken, + end = prsd_end, + lextypes = prsd_lextype, + headline = prsd_headline + ); + NOTICE: snitch: BEFORE any CREATE TEXT SEARCH PARSER + NOTICE: snitch: AFTER CREATE TEXT SEARCH PARSER public test_parser + NOTICE: snitch: AFTER any CREATE TEXT SEARCH PARSER + create text search template test_template ( + init = dsimple_init, + lexize = dsimple_lexize + ); + NOTICE: snitch: BEFORE any CREATE TEXT SEARCH TEMPLATE + NOTICE: snitch: AFTER CREATE TEXT SEARCH TEMPLATE public test_template + NOTICE: snitch: AFTER any CREATE TEXT SEARCH TEMPLATE + drop text search configuration test; + NOTICE: snitch: BEFORE any DROP TEXT SEARCH CONFIGURATION + NOTICE: snitch: BEFORE DROP TEXT SEARCH CONFIGURATION public test + NOTICE: snitch: AFTER any DROP TEXT SEARCH CONFIGURATION + drop text search dictionary test_stem; + NOTICE: snitch: BEFORE any DROP TEXT SEARCH DICTIONARY + NOTICE: snitch: BEFORE DROP TEXT SEARCH DICTIONARY public test_stem + NOTICE: snitch: AFTER any DROP TEXT SEARCH DICTIONARY + drop text search parser test_parser; + NOTICE: snitch: BEFORE any DROP TEXT SEARCH PARSER + NOTICE: snitch: BEFORE DROP TEXT SEARCH PARSER public test_parser + NOTICE: snitch: AFTER any DROP TEXT SEARCH PARSER + drop text search template test_template; + NOTICE: snitch: BEFORE any DROP TEXT SEARCH TEMPLATE + NOTICE: snitch: BEFORE DROP TEXT SEARCH TEMPLATE public test_template + NOTICE: snitch: AFTER any DROP TEXT SEARCH TEMPLATE + create function cmd.testcast(text) returns int4 language plpgsql as $$begin return 4::int4;end;$$; + NOTICE: snitch: BEFORE any CREATE FUNCTION + NOTICE: snitch: AFTER CREATE FUNCTION cmd testcast + NOTICE: snitch: AFTER any CREATE FUNCTION + create cast (text as int4) with function cmd.testcast(text) as assignment; + NOTICE: snitch: BEFORE any CREATE CAST + NOTICE: snitch: AFTER CREATE CAST + NOTICE: snitch: AFTER any CREATE CAST + alter schema cmd rename to cmd1; + NOTICE: snitch: BEFORE any ALTER SCHEMA + NOTICE: snitch: AFTER ALTER SCHEMA cmd1 + NOTICE: snitch: AFTER any ALTER SCHEMA + drop schema cmd1 cascade; + NOTICE: snitch: BEFORE any DROP SCHEMA + NOTICE: snitch: BEFORE DROP SCHEMA cmd1 + NOTICE: drop cascades to 12 other objects + DETAIL: drop cascades to table cmd1.foo + drop cascades to view cmd1.v + drop cascades to table cmd1.test + drop cascades to sequence cmd1.test_seq + drop cascades to view cmd1.view_test2 + 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 function cmd1.trigfunc() + drop cascades to function cmd1.testcast(text) + drop cascades to cast from text to integer + NOTICE: snitch: AFTER any DROP SCHEMA + drop schema cmd2 cascade; + NOTICE: snitch: BEFORE any DROP SCHEMA + NOTICE: snitch: BEFORE DROP SCHEMA cmd2 + NOTICE: drop cascades to type cmd2.us_postal_code + NOTICE: snitch: AFTER any DROP SCHEMA + drop role regbob; + NOTICE: snitch: BEFORE any DROP ROLE + ERROR: role "regbob" cannot be dropped because some objects depend on it + DETAIL: 3 objects in database foo + drop command trigger snitch_before; + drop command trigger snitch_after; + drop command trigger snitch_create_table; + drop command trigger snitch_create_seq; + drop command trigger snitch_create_view; + drop command trigger snitch_alter_table; + drop command trigger snitch_alter_seq; + drop command trigger snitch_alter_view; + drop command trigger snitch_drop_table; + drop command trigger snitch_create_function; + drop command trigger snitch_create_collation; + drop command trigger snitch_alter_operator; + drop command trigger snitch_create_domain; + drop command trigger snitch_alter_schema; + drop command trigger snitch_create_tsconfig; + drop command trigger snitch_create_tsdict; + drop command trigger snitch_create_tsparser; + drop command trigger snitch_create_tstmpl; + drop command trigger snitch_after_alter_function; + drop command trigger snitch_create_cast; + drop command trigger snitch_create_trigger; + drop command trigger snitch_alter_trigger; + drop command trigger snitch_drop_trigger; + drop command trigger snitch_create_schema; + drop command trigger snitch_drop_schema; + drop command trigger snitch_create_aggregate; + drop command trigger snitch_alter_collation; + drop command trigger snitch_create_operator; + drop command trigger snitch_alter_domain; + drop command trigger snitch_create_type; + drop command trigger snitch_alter_type; + drop command trigger snitch_before_alter_function; + drop command trigger snitch_alter_conversion; + drop command trigger snitch_drop_agg; + drop command trigger snitch_before_drop_tsconf; + drop command trigger snitch_before_drop_tsdict; + drop command trigger snitch_before_drop_tsparser; + drop command trigger snitch_before_drop_tstmpl; + drop command trigger snitch_vacuum; + drop command trigger snitch_reindex; *** 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,296 ---- + -- + -- COMMAND TRIGGERS + -- + create or replace function any_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 + -- don't output objectname and schemaname, NULL in an ANY command trigger + raise notice 'snitch: % any %', tg_when, cmd_tag; + end; + $$; + + 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 any_snitch(); + create command trigger snitch_after_ after any command execute procedure any_snitch(); + + alter command trigger snitch_before set disable; + alter command trigger snitch_before set enable; + alter command trigger snitch_after_ rename to snitch_after; + + create command trigger snitch_create_table after create table execute procedure snitch(); + create command trigger snitch_create_seq after create sequence execute procedure snitch(); + create command trigger snitch_create_view after create view execute procedure snitch(); + create command trigger snitch_alter_table after alter table execute procedure snitch(); + create command trigger snitch_alter_seq after alter sequence execute procedure snitch(); + create command trigger snitch_alter_view after alter view execute procedure snitch(); + create command trigger snitch_drop_table after drop table execute procedure snitch(); + create command trigger snitch_create_function after create function execute procedure snitch(); + create command trigger snitch_create_collation after create collation execute procedure snitch(); + create command trigger snitch_alter_operator after alter operator execute procedure snitch(); + create command trigger snitch_create_domain after create domain execute procedure snitch(); + create command trigger snitch_alter_schema after alter schema execute procedure snitch(); + create command trigger snitch_create_tsconfig after create text search configuration execute procedure snitch(); + create command trigger snitch_create_tsdict after create text search dictionary execute procedure snitch(); + create command trigger snitch_create_tsparser after create text search parser execute procedure snitch(); + create command trigger snitch_create_tstmpl after create text search template execute procedure snitch(); + create command trigger snitch_after_alter_function after alter function execute procedure snitch(); + create command trigger snitch_create_cast after create cast execute procedure snitch(); + create command trigger snitch_create_opclass after create operator class execute procedure snitch(); + + create command trigger snitch_create_trigger before create trigger execute procedure snitch(); + create command trigger snitch_alter_trigger before alter trigger execute procedure snitch(); + create command trigger snitch_drop_trigger before drop trigger execute procedure snitch(); + create command trigger snitch_create_schema before create schema execute procedure snitch(); + create command trigger snitch_drop_schema before drop schema execute procedure snitch(); + create command trigger snitch_create_aggregate before create aggregate execute procedure snitch(); + create command trigger snitch_alter_collation before alter collation execute procedure snitch(); + create command trigger snitch_create_operator before create operator execute procedure snitch(); + create command trigger snitch_alter_domain before alter domain execute procedure snitch(); + create command trigger snitch_create_type before create type execute procedure snitch(); + create command trigger snitch_alter_type before alter type execute procedure snitch(); + create command trigger snitch_before_alter_function before alter function execute procedure snitch(); + create command trigger snitch_alter_conversion before alter conversion execute procedure snitch(); + create command trigger snitch_drop_agg before drop aggregate execute procedure snitch(); + create command trigger snitch_drop_domain before drop domain execute procedure snitch(); + + create command trigger snitch_before_drop_tsconf before drop text search configuration execute procedure snitch(); + create command trigger snitch_before_drop_tsdict before drop text search dictionary execute procedure snitch(); + create command trigger snitch_before_drop_tsparser before drop text search parser execute procedure snitch(); + create command trigger snitch_before_drop_tstmpl before drop text search template execute procedure snitch(); + + create command trigger snitch_vacuum before vacuum execute procedure snitch(); + create command trigger snitch_reindex before reindex execute procedure snitch(); + + create schema cmd; + create schema cmd2; + create role regbob; + + 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; + + create table test9 (id int, stuff text); + alter table test9 rename to test; + alter table test set schema cmd; + alter table cmd.test rename column stuff to things; + alter table cmd.test add column alpha text; + alter table cmd.test alter column alpha set data type varchar(300); + alter table cmd.test alter column alpha set default 'test'; + alter table cmd.test alter column alpha drop default; + alter table cmd.test alter column alpha set statistics 78; + alter table cmd.test alter column alpha set storage plain; + alter table cmd.test alter column alpha set not null; + alter table cmd.test alter column alpha drop not null; + alter table cmd.test alter column alpha set (n_distinct = -0.78); + alter table cmd.test alter column alpha reset (n_distinct); + alter table cmd.test drop column alpha; + alter table cmd.test add check (id > 2) not valid; + alter table cmd.test add check (id < 800000); + alter table cmd.test set without cluster; + alter table cmd.test set with oids; + alter table cmd.test set without oids; + + create sequence test_seq_; + alter sequence test_seq_ owner to regbob; + alter sequence test_seq_ rename to test_seq; + alter sequence test_seq set schema cmd; + alter sequence cmd.test_seq start with 3; + alter sequence cmd.test_seq restart with 4; + alter sequence cmd.test_seq minvalue 3; + alter sequence cmd.test_seq no minvalue; + alter sequence cmd.test_seq maxvalue 900000; + alter sequence cmd.test_seq no maxvalue; + alter sequence cmd.test_seq cache 876; + alter sequence cmd.test_seq cycle; + alter sequence cmd.test_seq no cycle; + + create view view_test as select id, things from cmd.test; + alter view view_test owner to regbob; + alter view view_test rename to view_test2; + alter view view_test2 set schema cmd; + alter view cmd.view_test2 alter column id set default 9; + alter view cmd.view_test2 alter column id drop default; + + cluster cmd.foo using foo_pkey; + vacuum cmd.foo; + vacuum; + reindex table cmd.foo; + + set session_replication_role to replica; + create table cmd.bar(); + reset session_replication_role; + + create index idx_foo on cmd.foo(t); + reindex index cmd.idx_foo; + drop index cmd.idx_foo; + + create function fun(int) returns text language sql + as $$ select t from cmd.foo where id = $1; $$; + + alter function fun(int) strict; + alter function fun(int) rename to notfun; + alter function notfun(int) set schema cmd; + alter function cmd.notfun(int) owner to regbob; + alter function cmd.notfun(int) cost 77; + 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; + + create type cmd.type_test AS (a integer, b integer, c text); + alter type cmd.type_test owner to regbob; + alter type cmd.type_test rename to type_test2; + alter type cmd.type_test2 set schema public; + alter type public.type_test2 rename attribute a to z; + alter type public.type_test2 add attribute alpha text; + alter type public.type_test2 alter attribute alpha set data type char(90); + alter type public.type_test2 drop attribute alpha; + + drop type cmd.compfoo; + drop type public.type_test2; + + 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; + alter domain cmd.us_postal_code set default 90210; + alter domain cmd.us_postal_code drop default; + alter domain cmd.us_postal_code set not null; + alter domain cmd.us_postal_code drop not null; + alter domain cmd.us_postal_code add constraint dummy_constraint check (value ~ '^\d{8}$'); + alter domain cmd.us_postal_code drop constraint dummy_constraint; + alter domain cmd.us_postal_code owner to regbob; + alter domain cmd.us_postal_code set schema cmd2; + drop domain cmd2.us_postal_code; + + 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; + + create conversion test for 'utf8' to 'sjis' from utf8_to_sjis; + create default conversion test2 for 'utf8' to 'sjis' from utf8_to_sjis; + alter conversion test2 rename to test3; + drop conversion test3; + drop conversion test; + + create operator class test_op_class + for type anyenum using hash as + operator 1 =, + function 1 hashenum(anyenum); + + create text search configuration test (parser = "default"); + + create text search dictionary test_stem ( + template = snowball, + language = 'english', stopwords = 'english' + ); + + create text search parser test_parser ( + start = prsd_start, + gettoken = prsd_nexttoken, + end = prsd_end, + lextypes = prsd_lextype, + headline = prsd_headline + ); + + create text search template test_template ( + init = dsimple_init, + lexize = dsimple_lexize + ); + + drop text search configuration test; + drop text search dictionary test_stem; + drop text search parser test_parser; + drop text search template test_template; + + create function cmd.testcast(text) returns int4 language plpgsql as $$begin return 4::int4;end;$$; + create cast (text as int4) with function cmd.testcast(text) as assignment; + + alter schema cmd rename to cmd1; + + drop schema cmd1 cascade; + drop schema cmd2 cascade; + drop role regbob; + + drop command trigger snitch_before; + drop command trigger snitch_after; + + drop command trigger snitch_create_table; + drop command trigger snitch_create_seq; + drop command trigger snitch_create_view; + drop command trigger snitch_alter_table; + drop command trigger snitch_alter_seq; + drop command trigger snitch_alter_view; + drop command trigger snitch_drop_table; + drop command trigger snitch_create_function; + drop command trigger snitch_create_collation; + drop command trigger snitch_alter_operator; + drop command trigger snitch_create_domain; + drop command trigger snitch_alter_schema; + drop command trigger snitch_create_tsconfig; + drop command trigger snitch_create_tsdict; + drop command trigger snitch_create_tsparser; + drop command trigger snitch_create_tstmpl; + drop command trigger snitch_after_alter_function; + drop command trigger snitch_create_cast; + drop command trigger snitch_create_opclass; + + drop command trigger snitch_create_trigger; + drop command trigger snitch_alter_trigger; + drop command trigger snitch_drop_trigger; + drop command trigger snitch_create_schema; + drop command trigger snitch_drop_schema; + drop command trigger snitch_create_aggregate; + drop command trigger snitch_alter_collation; + drop command trigger snitch_create_operator; + drop command trigger snitch_alter_domain; + drop command trigger snitch_create_type; + drop command trigger snitch_alter_type; + drop command trigger snitch_before_alter_function; + drop command trigger snitch_alter_conversion; + drop command trigger snitch_drop_agg; + drop command trigger snitch_drop_domain; + + drop command trigger snitch_before_drop_tsconf; + drop command trigger snitch_before_drop_tsdict; + drop command trigger snitch_before_drop_tsparser; + drop command trigger snitch_before_drop_tstmpl; + + drop command trigger snitch_vacuum; + drop command trigger snitch_reindex; *** 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 ----