*** 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,102 ----
+
+
+
+
+ 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
+
+ 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.
+
+
+
+
+
+ command
+
+
+ The command tag on which this trigger acts.
+
+
+
+
+
+ enabled
+
+
+ When to enable this trigger. See
+ the session_replication_role parameter.
+
+
+
+
+
+
+
+ Compatibility
+
+
+ ALTER COMMAND TRIGGER is a PostgreSQL>
+ extension of the SQL standard.
+
+
+
+
+ See Also
+
+
+
+
+
+
+
*** /dev/null
--- b/doc/src/sgml/ref/create_command_trigger.sgml
***************
*** 0 ****
--- 1,323 ----
+
+
+
+
+ CREATE COMMAND TRIGGER
+ 7
+ SQL - Language Statements
+
+
+
+ CREATE COMMAND TRIGGER
+ define a new trigger
+
+
+
+ CREATE COMMAND TRIGGER
+
+
+
+
+ CREATE COMMAND TRIGGER name { BEFORE | AFTER } ANY COMMAND
+ EXECUTE PROCEDURE function_name ()
+
+ CREATE COMMAND TRIGGER name { BEFORE | AFTER } command
+ EXECUTE PROCEDURE function_name ()
+
+ where command can be one of:
+
+ ALTER AGGREGATE
+ ALTER COLLATION
+ 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 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
+ CLUSTER
+ CREATE AGGREGATE
+ CREATE CAST
+ CREATE COLLATION
+ CREATE CONVERSION
+ CREATE DOMAIN
+ CREATE EXTENSION
+ CREATE FOREIGN DATA WRAPPER
+ CREATE FOREIGN TABLE
+ CREATE FUNCTION
+ CREATE INDEX
+ CREATE LANGUAGE
+ CREATE OPERATOR
+ CREATE OPERATOR CLASS
+ CREATE OPERATOR FAMILY
+ CREATE RULE
+ CREATE SCHEMA
+ CREATE SEQUENCE
+ CREATE SERVER
+ CREATE TABLE
+ CREATE 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, the only it can aborts the execution of
+ the command is by raising an exception.
+
+
+
+ Refer to for more information about triggers.
+
+
+
+
+ Parameters
+
+
+
+ name
+
+
+ The name to give the new trigger. This must be distinct from
+ the name of any other trigger for the same table.
+ The name cannot be schema-qualified — the trigger inherits the
+ schema of its table. For a constraint trigger, this is also the name to
+ use when modifying the trigger's behavior using
+ SET CONSTRAINTS>.
+
+
+
+
+
+ BEFORE
+ AFTER
+
+
+ Determines whether the function is called before or after the command
+ is executed.
+
+
+
+
+
+ command
+
+
+ The tag of the command the trigger is for. Supported commands are
+ mainly those acting on database objects, plus some more facilities.
+ That leaves out the following list of non supported commands.
+
+
+ Commands that refers to global objects, such as databases, tablespaces
+ and roles are not supported. As command triggers are per-database, it
+ would be weird to affect e.g. a tablespace depending on which database
+ you are connected to.
+
+
+ Commands that exercize their own transaction control are only
+ supported in BEFORE command triggers, that's the
+ case for VACUUM, CLUSTER
+ CREATE INDEX CONCURRENTLY, and REINDEX
+ DATABASE.
+
+
+ Commands that are related to transaction control (such
+ as BEGIN or COMMIT), related to
+ prepared plans
+ (e.g. PREPARE, DEALLOCATE),
+ cursors management
+ (e.g. DECLARE, FETCH), setting
+ variables (SET), the LISTEN
+ feature, and security are not supported either.
+
+
+ Command triggers on CREATE COMMAND
+ TRIGGER, ALTER COMMAND TRIGGER
+ and DROP COMMAND TRIGGER are not supported so as
+ not to be able to take over control from a superuser.
+
+
+ Triggers on ANY command support more commands than
+ just this list, and will only provide the command
+ tag argument as NOT NULL. Supporting more
+ commands is made so that you can actually block
+ commands in one go.
+
+
+
+
+
+ function_name
+
+
+ A user-supplied function that is declared as taking 5 arguments of
+ type text, text, oid, text, text and returning void.
+
+
+ If your command trigger is implemented in C then it
+ will be called with yet another argument, of
+ type internal, which is a pointer to
+ the Node * parse tree.
+
+
+ The command trigger function is called with the
+ parameters tg_when (which is set to either 'BEFORE'
+ or 'AFTER'), command
+ tag, objectid (can be null in case of a
+ BEFORE CREATE or an AFTER DROP command trigger
+ timing), schemaname (can be null for objects not
+ living in a schema, and for sequences due to an implementation limit)
+ and object name (can be null for any command
+ triggers).
+
+
+ The command CREATE SEQUENCE lacks support for
+ the schemaname command trigger argument, it
+ provides NULL in all cases.
+
+
+
+
+
+
+
+
+ Notes
+
+
+ To create a trigger on a command, the user must be superuser.
+
+
+
+ Use to remove a command trigger.
+
+
+
+
+ Examples
+
+
+ Forbids the execution of any DDL command:
+
+
+ CREATE OR REPLACE FUNCTION abort_any_command
+ (tg_when text, cmd_tag text, objectid oid, schemaname text, objectname text)
+ RETURNS void LANGUAGE plpgsql AS $$
+ BEGIN
+ RAISE EXCEPTION 'command % is disabled' % cmd_tag;
+ END;
+ $$;
+
+ CREATE COMMAND TRIGGER abort_ddl
+ BEFORE ANY COMMAND
+ EXECUTE PROCEDURE abort_any_command();
+
+
+ Execute the function enforce_local_style> each time
+ a CREATE TABLE command is run:
+
+
+ CREATE OR REPLACE FUNCTION enforce_local_style
+ (tg_when text, cmd_tag text, objectid oid, schemaname text, objectname text)
+ RETURNS void LANGUAGE plpgsql AS $$
+ BEGIN
+ IF substring(objectname, 0, 4) NOT IN ('ab_', 'cz_', 'fr_')
+ THEN
+ RAISE EXCEPTION 'invalid relation name: %', objectname;
+ END IF;
+ END;
+ $$;
+
+ CREATE COMMAND TRIGGER check_style
+ BEFORE CREATE TABLE
+ EXECUTE PROCEDURE enforce_local_style();
+
+
+
+
+
+ Compatibility
+
+
+ CREATE COMMAND TRIGGER is a
+ PostgreSQL extension of the SQL>
+ standard.
+
+
+
+
+
+ See Also
+
+
+
+
+
+
+
+
*** a/doc/src/sgml/ref/create_trigger.sgml
--- b/doc/src/sgml/ref/create_trigger.sgml
***************
*** 35,40 **** CREATE [ CONSTRAINT ] TRIGGER name
--- 35,41 ----
UPDATE [ OF column_name [, ... ] ]
DELETE
TRUNCATE
+
*** /dev/null
--- b/doc/src/sgml/ref/drop_command_trigger.sgml
***************
*** 0 ****
--- 1,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 command triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = indexId;
+ cmd->objectname = RelationGetRelationName(heapRelation);
+ cmd->schemaname = get_namespace_name(RelationGetNamespace(heapRelation));
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/*
* All predicate locks on the index are about to be made invalid. Promote
* them to relation locks on the heap.
***************
*** 2923,2928 **** reindex_index(Oid indexId, bool skip_constraint_checks)
--- 2933,2942 ----
/* Close rels, but keep locks */
index_close(iRel, NoLock);
heap_close(heapRelation, NoLock);
+
+ /* Call AFTER REINDEX command triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
/*
***************
*** 2955,2961 **** reindex_index(Oid indexId, bool skip_constraint_checks)
* index rebuild.
*/
bool
! reindex_relation(Oid relid, int flags)
{
Relation rel;
Oid toast_relid;
--- 2969,2975 ----
* index rebuild.
*/
bool
! reindex_relation(Oid relid, int flags, CommandContext cmd)
{
Relation rel;
Oid toast_relid;
***************
*** 2972,2977 **** reindex_relation(Oid relid, int flags)
--- 2986,3002 ----
toast_relid = rel->rd_rel->reltoastrelid;
+ /* Call BEFORE REINDEX command triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = relid;
+ cmd->objectname = RelationGetRelationName(rel);
+ cmd->objectname = get_namespace_name(RelationGetNamespace(rel));
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
+
/*
* Get the list of index OIDs for this relation. (We trust to the
* relcache to get this with a sequential scan if ignoring system
***************
*** 3033,3039 **** reindex_relation(Oid relid, int flags)
if (is_pg_class)
RelationSetIndexList(rel, doneIndexes, InvalidOid);
! reindex_index(indexOid, !(flags & REINDEX_REL_CHECK_CONSTRAINTS));
CommandCounterIncrement();
--- 3058,3064 ----
if (is_pg_class)
RelationSetIndexList(rel, doneIndexes, InvalidOid);
! reindex_index(indexOid, !(flags & REINDEX_REL_CHECK_CONSTRAINTS), NULL);
CommandCounterIncrement();
***************
*** 3068,3074 **** reindex_relation(Oid relid, int flags)
* still hold the lock on the master table.
*/
if ((flags & REINDEX_REL_PROCESS_TOAST) && OidIsValid(toast_relid))
! result |= reindex_relation(toast_relid, flags);
return result;
}
--- 3093,3103 ----
* still hold the lock on the master table.
*/
if ((flags & REINDEX_REL_PROCESS_TOAST) && OidIsValid(toast_relid))
! result |= reindex_relation(toast_relid, flags, NULL);
!
! /* Call AFTER REINDEX command triggers */
! if (CommandFiresAfterTriggers(cmd))
! ExecAfterCommandTriggers(cmd);
return result;
}
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
***************
*** 21,26 ****
--- 21,27 ----
#include "catalog/objectaddress.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
+ #include "catalog/pg_cmdtrigger.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
***************
*** 44,49 ****
--- 45,51 ----
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
+ #include "commands/cmdtrigger.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/extension.h"
***************
*** 204,209 **** static ObjectPropertyType ObjectProperty[] =
--- 206,217 ----
InvalidAttrNumber
},
{
+ CmdTriggerRelationId,
+ CmdTriggerOidIndexId,
+ -1,
+ InvalidAttrNumber
+ },
+ {
TSConfigRelationId,
TSConfigOidIndexId,
TSCONFIGOID,
***************
*** 249,254 **** static ObjectAddress get_object_address_type(ObjectType objtype,
--- 257,264 ----
List *objname, bool missing_ok);
static ObjectAddress get_object_address_opcf(ObjectType objtype, List *objname,
List *objargs, bool missing_ok);
+ static ObjectAddress get_object_address_cmdtrigger(ObjectType objtype,
+ List *objname, 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->subname, 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,112 **** ExecRenameStmt(RenameStmt *stmt)
case OBJECT_VIEW:
case OBJECT_INDEX:
case OBJECT_FOREIGN_TABLE:
! RenameRelation(stmt);
break;
case OBJECT_COLUMN:
--- 114,120 ----
case OBJECT_VIEW:
case OBJECT_INDEX:
case OBJECT_FOREIGN_TABLE:
! RenameRelation(stmt, &cmd);
break;
case OBJECT_COLUMN:
***************
*** 115,142 **** ExecRenameStmt(RenameStmt *stmt)
break;
case OBJECT_TRIGGER:
! renametrig(stmt);
break;
case OBJECT_TSPARSER:
! RenameTSParser(stmt->object, stmt->newname);
break;
case OBJECT_TSDICTIONARY:
! RenameTSDictionary(stmt->object, stmt->newname);
break;
case OBJECT_TSTEMPLATE:
! RenameTSTemplate(stmt->object, stmt->newname);
break;
case OBJECT_TSCONFIGURATION:
! RenameTSConfiguration(stmt->object, stmt->newname);
break;
case OBJECT_DOMAIN:
case OBJECT_TYPE:
! RenameType(stmt);
break;
default:
--- 123,150 ----
break;
case OBJECT_TRIGGER:
! renametrig(stmt, &cmd);
break;
case OBJECT_TSPARSER:
! RenameTSParser(stmt->object, stmt->newname, &cmd);
break;
case OBJECT_TSDICTIONARY:
! RenameTSDictionary(stmt->object, stmt->newname, &cmd);
break;
case OBJECT_TSTEMPLATE:
! RenameTSTemplate(stmt->object, stmt->newname, &cmd);
break;
case OBJECT_TSCONFIGURATION:
! RenameTSConfiguration(stmt->object, stmt->newname, &cmd);
break;
case OBJECT_DOMAIN:
case OBJECT_TYPE:
! RenameType(stmt, &cmd);
break;
default:
***************
*** 152,191 **** ExecRenameStmt(RenameStmt *stmt)
void
ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
{
switch (stmt->objectType)
{
case OBJECT_AGGREGATE:
AlterFunctionNamespace(stmt->object, stmt->objarg, true,
! stmt->newschema);
break;
case OBJECT_COLLATION:
! AlterCollationNamespace(stmt->object, stmt->newschema);
break;
case OBJECT_CONVERSION:
! AlterConversionNamespace(stmt->object, stmt->newschema);
break;
case OBJECT_EXTENSION:
! AlterExtensionNamespace(stmt->object, stmt->newschema);
break;
case OBJECT_FUNCTION:
AlterFunctionNamespace(stmt->object, stmt->objarg, false,
! stmt->newschema);
break;
case OBJECT_OPERATOR:
! AlterOperatorNamespace(stmt->object, stmt->objarg, stmt->newschema);
break;
case OBJECT_OPCLASS:
! AlterOpClassNamespace(stmt->object, stmt->addname, stmt->newschema);
break;
case OBJECT_OPFAMILY:
! AlterOpFamilyNamespace(stmt->object, stmt->addname, stmt->newschema);
break;
case OBJECT_SEQUENCE:
--- 160,202 ----
void
ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
{
+ CommandContextData cmd;
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
switch (stmt->objectType)
{
case OBJECT_AGGREGATE:
AlterFunctionNamespace(stmt->object, stmt->objarg, true,
! stmt->newschema, &cmd);
break;
case OBJECT_COLLATION:
! AlterCollationNamespace(stmt->object, stmt->newschema, &cmd);
break;
case OBJECT_CONVERSION:
! AlterConversionNamespace(stmt->object, stmt->newschema, &cmd);
break;
case OBJECT_EXTENSION:
! AlterExtensionNamespace(stmt->object, stmt->newschema, &cmd);
break;
case OBJECT_FUNCTION:
AlterFunctionNamespace(stmt->object, stmt->objarg, false,
! stmt->newschema, &cmd);
break;
case OBJECT_OPERATOR:
! AlterOperatorNamespace(stmt->object, stmt->objarg, stmt->newschema, &cmd);
break;
case OBJECT_OPCLASS:
! AlterOpClassNamespace(stmt->object, stmt->addname, stmt->newschema, &cmd);
break;
case OBJECT_OPFAMILY:
! AlterOpFamilyNamespace(stmt->object, stmt->addname, stmt->newschema, &cmd);
break;
case OBJECT_SEQUENCE:
***************
*** 196,219 **** ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
break;
case OBJECT_TSPARSER:
! AlterTSParserNamespace(stmt->object, stmt->newschema);
break;
case OBJECT_TSDICTIONARY:
! AlterTSDictionaryNamespace(stmt->object, stmt->newschema);
break;
case OBJECT_TSTEMPLATE:
! AlterTSTemplateNamespace(stmt->object, stmt->newschema);
break;
case OBJECT_TSCONFIGURATION:
! AlterTSConfigurationNamespace(stmt->object, stmt->newschema);
break;
case OBJECT_TYPE:
case OBJECT_DOMAIN:
! AlterTypeNamespace(stmt->object, stmt->newschema, stmt->objectType);
break;
default:
--- 207,230 ----
break;
case OBJECT_TSPARSER:
! AlterTSParserNamespace(stmt->object, stmt->newschema, &cmd);
break;
case OBJECT_TSDICTIONARY:
! AlterTSDictionaryNamespace(stmt->object, stmt->newschema, &cmd);
break;
case OBJECT_TSTEMPLATE:
! AlterTSTemplateNamespace(stmt->object, stmt->newschema, &cmd);
break;
case OBJECT_TSCONFIGURATION:
! AlterTSConfigurationNamespace(stmt->object, stmt->newschema, &cmd);
break;
case OBJECT_TYPE:
case OBJECT_DOMAIN:
! AlterTypeNamespace(stmt->object, stmt->newschema, stmt->objectType, &cmd);
break;
default:
***************
*** 235,240 **** ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
--- 246,254 ----
*
* Returns the OID of the object's previous namespace, or InvalidOid if
* object doesn't have a schema.
+ *
+ * Doesn't run any command trigger for those sub-commands, so just pass a NULL
+ * CommandContext to functions implementing the ALTER.
*/
Oid
AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid)
***************
*** 271,285 **** AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid)
}
case OCLASS_PROC:
! oldNspOid = AlterFunctionNamespace_oid(objid, nspOid);
break;
case OCLASS_TYPE:
! oldNspOid = AlterTypeNamespace_oid(objid, nspOid);
break;
case OCLASS_COLLATION:
! oldNspOid = AlterCollationNamespace_oid(objid, nspOid);
break;
case OCLASS_CONVERSION:
--- 285,299 ----
}
case OCLASS_PROC:
! oldNspOid = AlterFunctionNamespace_oid(objid, nspOid, NULL);
break;
case OCLASS_TYPE:
! oldNspOid = AlterTypeNamespace_oid(objid, nspOid, NULL);
break;
case OCLASS_COLLATION:
! oldNspOid = AlterCollationNamespace_oid(objid, nspOid, NULL);
break;
case OCLASS_CONVERSION:
***************
*** 349,355 **** Oid
AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId,
Oid objid, Oid nspOid,
int Anum_name, int Anum_namespace, int Anum_owner,
! AclObjectKind acl_kind)
{
Oid classId = RelationGetRelid(rel);
Oid oldNspOid;
--- 363,369 ----
AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId,
Oid objid, Oid nspOid,
int Anum_name, int Anum_namespace, int Anum_owner,
! AclObjectKind acl_kind, CommandContext cmd)
{
Oid classId = RelationGetRelid(rel);
Oid oldNspOid;
***************
*** 419,424 **** AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId,
--- 433,448 ----
getObjectDescriptionOids(classId, objid),
get_namespace_name(nspOid))));
+ /* Call BEFORE ALTER OBJECT triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = objid;
+ cmd->objectname = 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,703 ----
+ /*-------------------------------------------------------------------------
+ *
+ * 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("CREATE INDEX CONCURRENTLY is not supported"),
+ errdetail("The command trigger will not get fired.")));
+
+ if (strcmp(stmt->command, "REINDEX") == 0)
+ ereport(WARNING,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("REINDEX DATABASE is not supported"),
+ errdetail("The command trigger will not get fired.")));
+
+ 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(const char *trigname, const char *newname)
+ {
+ SysScanDesc tgscan;
+ ScanKeyData skey[1];
+ HeapTuple tup;
+ Relation rel;
+ Form_pg_cmdtrigger cmdForm;
+
+ 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.
+ */
+ 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,325 ----
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(objectId);
+ break;
+ case OBJECT_CAST:
+ cmd->objectname = NULL;
+ 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_TRIGGER:
+ case OBJECT_CMDTRIGGER:
+ case OBJECT_RULE:
+ case OBJECT_FDW:
+ case OBJECT_FOREIGN_SERVER:
+ case OBJECT_OPCLASS:
+ case OBJECT_OPFAMILY:
+ cmd->objectname = strVal(linitial(objname));
+ 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,1200 **** CreateExtension(CreateExtensionStmt *stmt)
--- 1191,1215 ----
List *requiredSchemas;
Oid extensionOid;
ListCell *lc;
+ CommandContextData cmd;
/* Check extension name validity before any filesystem access */
check_valid_extension_name(stmt->extname);
/*
+ * Call BEFORE CREATE EXTENSION triggers
+ */
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = InvalidOid;
+ cmd.objectname = stmt->extname;
+ cmd.schemaname = NULL;
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+ /*
* Check for duplicate extension name. The unique index on
* pg_extension.extname would catch this anyway, and serves as a backstop
* in case of race conditions; but this is a friendlier error message, and
***************
*** 1467,1472 **** CreateExtension(CreateExtensionStmt *stmt)
--- 1482,1494 ----
*/
ApplyExtensionUpdates(extensionOid, pcontrol,
versionName, updateVersions);
+
+ /* Call AFTER CREATE EXTENSION triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = extensionOid;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
/*
***************
*** 2186,2192 **** pg_extension_config_dump(PG_FUNCTION_ARGS)
* Execute ALTER EXTENSION SET SCHEMA
*/
void
! AlterExtensionNamespace(List *names, const char *newschema)
{
char *extensionName;
Oid extensionOid;
--- 2208,2214 ----
* Execute ALTER EXTENSION SET SCHEMA
*/
void
! AlterExtensionNamespace(List *names, const char *newschema, CommandContext cmd)
{
char *extensionName;
Oid extensionOid;
***************
*** 2247,2252 **** AlterExtensionNamespace(List *names, const char *newschema)
--- 2269,2284 ----
systable_endscan(extScan);
+ /* Call BEFORE ALTER EXTENSION triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = extensionOid;
+ cmd->objectname = extensionName;
+ cmd->schemaname = NULL;
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/*
* If the extension is already in the target schema, just silently do
* nothing.
***************
*** 2342,2347 **** AlterExtensionNamespace(List *names, const char *newschema)
--- 2374,2383 ----
/* update dependencies to point to the new schema */
changeDependencyFor(ExtensionRelationId, extensionOid,
NamespaceRelationId, oldNspOid, nspOid);
+
+ /* Call AFTER ALTER EXTENSION triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
/*
***************
*** 2363,2368 **** ExecAlterExtensionStmt(AlterExtensionStmt *stmt)
--- 2399,2405 ----
Datum datum;
bool isnull;
ListCell *lc;
+ CommandContextData cmd;
/*
* We use global variables to track the extension being created, so we can
***************
*** 2415,2420 **** ExecAlterExtensionStmt(AlterExtensionStmt *stmt)
--- 2452,2471 ----
stmt->extname);
/*
+ * Call BEFORE ALTER EXTENSION triggers
+ */
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = extensionOid;
+ cmd.objectname = stmt->extname;
+ cmd.schemaname = NULL;
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
+ /*
* Read the primary control file. Note we assume that it does not contain
* any non-ASCII data, so there is no need to worry about encoding at this
* point.
***************
*** 2480,2485 **** ExecAlterExtensionStmt(AlterExtensionStmt *stmt)
--- 2531,2540 ----
*/
ApplyExtensionUpdates(extensionOid, control,
oldVersionName, updateVersions);
+
+ /* Call AFTER ALTER EXTENSION triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ ExecAfterCommandTriggers(&cmd);
}
/*
***************
*** 2649,2654 **** ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt)
--- 2704,2710 ----
ObjectAddress object;
Relation relation;
Oid oldExtension;
+ CommandContextData cmd;
extension.classId = ExtensionRelationId;
extension.objectId = get_extension_oid(stmt->extname, false);
***************
*** 2677,2682 **** ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt)
--- 2733,2745 ----
*/
oldExtension = getExtensionOfObject(object.classId, object.objectId);
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
+ /* Init the command context no matter what, that's cheap here */
+ cmd.objectId = extension.objectId;
+ cmd.objectname = stmt->extname;
+ cmd.schemaname = NULL;
+
if (stmt->action > 0)
{
/*
***************
*** 2689,2694 **** ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt)
--- 2752,2761 ----
getObjectDescription(&object),
get_extension_name(oldExtension))));
+ /* Call BEFORE ALTER EXTENSION command triggers */
+ if (CommandFiresTriggers(&cmd))
+ ExecBeforeCommandTriggers(&cmd);
+
/*
* OK, add the dependency.
*/
***************
*** 2706,2711 **** ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt)
--- 2773,2782 ----
getObjectDescription(&object),
stmt->extname)));
+ /* Call BEFORE ALTER EXTENSION command triggers */
+ if (CommandFiresTriggers(&cmd))
+ ExecBeforeCommandTriggers(&cmd);
+
/*
* OK, drop the dependency.
*/
***************
*** 2723,2726 **** ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt)
--- 2794,2801 ----
*/
if (relation != NULL)
relation_close(relation, NoLock);
+
+ /* Call AFTER ALTER EXTENSION command triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ ExecAfterCommandTriggers(&cmd);
}
*** a/src/backend/commands/foreigncmds.c
--- b/src/backend/commands/foreigncmds.c
***************
*** 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);
***************
*** 1479,1484 **** CreateCast(CreateCastStmt *stmt)
--- 1535,1541 ----
ObjectAddress myself,
referenced;
AclResult aclresult;
+ CommandContextData cmd;
sourcetypeid = typenameTypeId(NULL, stmt->sourcetype);
targettypeid = typenameTypeId(NULL, stmt->targettype);
***************
*** 1697,1702 **** CreateCast(CreateCastStmt *stmt)
--- 1754,1771 ----
break;
}
+ /* Call BEFORE CREATE CAST command triggers */
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = InvalidOid;
+ cmd.objectname = NULL; /* composite name, not supported */
+ cmd.schemaname = NULL; /* casts don't live in a namespace */
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
relation = heap_open(CastRelationId, RowExclusiveLock);
/*
***************
*** 1764,1769 **** CreateCast(CreateCastStmt *stmt)
--- 1833,1844 ----
heap_freetuple(tuple);
heap_close(relation, RowExclusiveLock);
+
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = castid;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
/*
***************
*** 1822,1828 **** DropCastById(Oid castOid)
*/
void
AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
! const char *newschema)
{
Oid procOid;
Oid nspOid;
--- 1897,1903 ----
*/
void
AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
! const char *newschema, CommandContext cmd)
{
Oid procOid;
Oid nspOid;
***************
*** 1836,1846 **** AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
/* get schema OID and check its permissions */
nspOid = LookupCreationNamespace(newschema);
! AlterFunctionNamespace_oid(procOid, nspOid);
}
Oid
! AlterFunctionNamespace_oid(Oid procOid, Oid nspOid)
{
Oid oldNspOid;
HeapTuple tup;
--- 1911,1921 ----
/* get schema OID and check its permissions */
nspOid = LookupCreationNamespace(newschema);
! AlterFunctionNamespace_oid(procOid, nspOid, cmd);
}
Oid
! AlterFunctionNamespace_oid(Oid procOid, Oid nspOid, CommandContext cmd)
{
Oid oldNspOid;
HeapTuple tup;
***************
*** 1875,1880 **** AlterFunctionNamespace_oid(Oid procOid, Oid nspOid)
--- 1950,1965 ----
NameStr(proc->proname),
get_namespace_name(nspOid))));
+ /* Call BEFORE ALTER FUNCTION triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = procOid;
+ cmd->objectname = 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)
--- 1978,1988 ----
heap_close(procRel, RowExclusiveLock);
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->schemaname = get_namespace_name(nspOid);
+ ExecAfterCommandTriggers(cmd);
+ }
return oldNspOid;
}
*** a/src/backend/commands/indexcmds.c
--- b/src/backend/commands/indexcmds.c
***************
*** 318,324 **** DefineIndex(RangeVar *heapRelation,
bool check_rights,
bool skip_build,
bool quiet,
! bool concurrent)
{
Oid *typeObjectId;
Oid *collationObjectId;
--- 318,325 ----
bool check_rights,
bool skip_build,
bool quiet,
! bool concurrent,
! CommandContext cmd)
{
Oid *typeObjectId;
Oid *collationObjectId;
***************
*** 574,579 **** DefineIndex(RangeVar *heapRelation,
--- 575,597 ----
index_check_primary_key(rel, indexInfo, is_alter_table);
/*
+ * Call BEFORE CREATE INDEX triggers
+ *
+ * cmd.tag and cmd.parseetree must have been prepared for us by the caller.
+ *
+ * Bootstrap code must be able to skip command triggers, it's passing NULL
+ * as the CommandContext pointer.
+ */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = InvalidOid;
+ cmd->objectname = indexRelationName;
+ cmd->schemaname = get_namespace_name(namespaceId);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
+ /*
* Report index creation if appropriate (delay this till after most of the
* error checks)
*/
***************
*** 625,630 **** DefineIndex(RangeVar *heapRelation,
--- 643,655 ----
{
/* Close the heap and we're done, in the non-concurrent case */
heap_close(rel, NoLock);
+
+ /* Call AFTER CREATE INDEX triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectId = indexRelationId;
+ ExecAfterCommandTriggers(cmd);
+ }
return indexRelationId;
}
***************
*** 914,919 **** DefineIndex(RangeVar *heapRelation,
--- 939,949 ----
*/
UnlockRelationIdForSession(&heaprelid, ShareUpdateExclusiveLock);
+ /*
+ * Don't Call AFTER CREATE INDEX triggers here, because the transaction
+ * that did the work is already commited, RAISE EXCEPTION in the trigger
+ * can no longer undo what we did.
+ */
return indexRelationId;
}
***************
*** 1735,1741 **** ChooseIndexColumnNames(List *indexElems)
* Recreate a specific index.
*/
void
! ReindexIndex(RangeVar *indexRelation)
{
Oid indOid;
Oid heapOid = InvalidOid;
--- 1765,1771 ----
* Recreate a specific index.
*/
void
! ReindexIndex(RangeVar *indexRelation, CommandContext cmd)
{
Oid indOid;
Oid heapOid = InvalidOid;
***************
*** 1746,1752 **** ReindexIndex(RangeVar *indexRelation)
RangeVarCallbackForReindexIndex,
(void *) &heapOid);
! reindex_index(indOid, false);
}
/*
--- 1776,1782 ----
RangeVarCallbackForReindexIndex,
(void *) &heapOid);
! reindex_index(indOid, false, cmd);
}
/*
***************
*** 1813,1819 **** RangeVarCallbackForReindexIndex(const RangeVar *relation,
* Recreate all indexes of a table (and of its toast table, if any)
*/
void
! ReindexTable(RangeVar *relation)
{
Oid heapOid;
--- 1843,1849 ----
* Recreate all indexes of a table (and of its toast table, if any)
*/
void
! ReindexTable(RangeVar *relation, CommandContext cmd)
{
Oid heapOid;
***************
*** 1821,1827 **** ReindexTable(RangeVar *relation)
heapOid = RangeVarGetRelidExtended(relation, ShareLock, false, false,
RangeVarCallbackOwnsTable, NULL);
! if (!reindex_relation(heapOid, REINDEX_REL_PROCESS_TOAST))
ereport(NOTICE,
(errmsg("table \"%s\" has no indexes",
relation->relname)));
--- 1851,1857 ----
heapOid = RangeVarGetRelidExtended(relation, ShareLock, false, false,
RangeVarCallbackOwnsTable, NULL);
! if (!reindex_relation(heapOid, REINDEX_REL_PROCESS_TOAST, cmd))
ereport(NOTICE,
(errmsg("table \"%s\" has no indexes",
relation->relname)));
***************
*** 1934,1940 **** ReindexDatabase(const char *databaseName, bool do_system, bool do_user)
StartTransactionCommand();
/* functions in indexes may want a snapshot set */
PushActiveSnapshot(GetTransactionSnapshot());
! if (reindex_relation(relid, REINDEX_REL_PROCESS_TOAST))
ereport(NOTICE,
(errmsg("table \"%s.%s\" was reindexed",
get_namespace_name(get_rel_namespace(relid)),
--- 1964,1970 ----
StartTransactionCommand();
/* functions in indexes may want a snapshot set */
PushActiveSnapshot(GetTransactionSnapshot());
! if (reindex_relation(relid, REINDEX_REL_PROCESS_TOAST, NULL))
ereport(NOTICE,
(errmsg("table \"%s.%s\" was reindexed",
get_namespace_name(get_rel_namespace(relid)),
*** a/src/backend/commands/opclasscmds.c
--- b/src/backend/commands/opclasscmds.c
***************
*** 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 = stmt->amname;
+ cmd.schemaname = get_namespace_name(namespaceoid);
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
/*
***************
*** 720,725 **** DefineOpClass(CreateOpClassStmt *stmt)
--- 733,745 ----
OperatorClassRelationId, opclassoid, 0);
heap_close(rel, RowExclusiveLock);
+
+ /* Call AFTER CREATE OPERATOR CLASS command triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = opclassoid;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
***************
*** 731,739 **** void
DefineOpFamily(CreateOpFamilyStmt *stmt)
{
char *opfname; /* name of opfamily we're creating */
! Oid amoid, /* our AM's oid */
namespaceoid; /* namespace to create opfamily in */
AclResult aclresult;
/* Convert list of names to a name and namespace */
namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname,
--- 751,761 ----
DefineOpFamily(CreateOpFamilyStmt *stmt)
{
char *opfname; /* name of opfamily we're creating */
! Oid opfOid,
! amoid, /* our AM's oid */
namespaceoid; /* namespace to create opfamily in */
AclResult aclresult;
+ CommandContextData cmd;
/* Convert list of names to a name and namespace */
namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname,
***************
*** 759,766 **** DefineOpFamily(CreateOpFamilyStmt *stmt)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to create an operator family")));
/* Insert pg_opfamily catalog entry */
! (void) CreateOpFamily(stmt->amname, opfname, namespaceoid, amoid);
}
--- 781,807 ----
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to create an operator family")));
+ /* Call BEFORE CREATE OPERATOR FAMILY command triggers */
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = InvalidOid;
+ cmd.objectname = opfname;
+ cmd.schemaname = get_namespace_name(namespaceoid);
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
/* Insert pg_opfamily catalog entry */
! opfOid = CreateOpFamily(stmt->amname, opfname, namespaceoid, amoid);
!
! /* Call AFTER CREATE OPERATOR FAMILY command triggers */
! if (CommandFiresAfterTriggers(&cmd))
! {
! cmd.objectId = opfOid;
! ExecAfterCommandTriggers(&cmd);
! }
}
***************
*** 781,786 **** AlterOpFamily(AlterOpFamilyStmt *stmt)
--- 822,828 ----
maxProcNumber; /* amsupport value */
HeapTuple tup;
Form_pg_am pg_am;
+ CommandContextData cmd;
/* Get necessary info about access method */
tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
***************
*** 815,820 **** AlterOpFamily(AlterOpFamilyStmt *stmt)
--- 857,882 ----
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to alter an operator family")));
+ /* Call BEFORE ALTER OPERATOR FAMILY command triggers */
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ HeapTuple htup;
+ Form_pg_opfamily opfForm;
+
+ htup = OpFamilyCacheLookup(amoid, stmt->opfamilyname, false);
+ opfForm = (Form_pg_opfamily) GETSTRUCT(htup);
+
+ cmd.objectId = opfamilyoid;
+ cmd.objectname = NameStr(opfForm->opfname);
+ cmd.schemaname = get_namespace_name(opfForm->opfnamespace);
+
+ ReleaseSysCache(htup);
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
/*
* ADD and DROP cases need separate code from here on down.
*/
***************
*** 826,831 **** AlterOpFamily(AlterOpFamilyStmt *stmt)
--- 888,897 ----
AlterOpFamilyAdd(stmt->opfamilyname, amoid, opfamilyoid,
maxOpNumber, maxProcNumber,
stmt->items);
+
+ /* Call AFTER ALTER OPERATOR FAMILY command triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ ExecAfterCommandTriggers(&cmd);
}
/*
***************
*** 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 = NameListToString(name);
+ 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,216 **** DefineSequence(CreateSeqStmt *seq)
--- 213,232 ----
stmt->tablespacename = NULL;
stmt->if_not_exists = false;
+ /*
+ * Call BEFORE CREATE SEQUENCE triggers
+ */
+ InitCommandContext(&cmd, (Node *)seq, false);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = InvalidOid;
+ cmd.objectname = NameStr(name);
+ cmd.schemaname = NULL; /* can't publish it easily enough here */
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
seqoid = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId);
Assert(seqoid != InvalidOid);
***************
*** 226,231 **** DefineSequence(CreateSeqStmt *seq)
--- 242,254 ----
process_owned_by(rel, owned_by);
heap_close(rel, NoLock);
+
+ /* Call AFTER CREATE SEQUENCE triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = seqoid;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
/*
***************
*** 423,428 **** AlterSequence(AlterSeqStmt *stmt)
--- 446,452 ----
Form_pg_sequence seq;
FormData_pg_sequence new;
List *owned_by;
+ CommandContextData cmd;
/* Open and lock sequence. */
relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
***************
*** 458,463 **** AlterSequence(AlterSeqStmt *stmt)
--- 482,501 ----
/* Now okay to update the on-disk tuple */
memcpy(seq, &new, sizeof(FormData_pg_sequence));
+ /*
+ * Call BEFORE ALTER SEQUENCE triggers
+ */
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
+ if (ListCommandTriggers(&cmd))
+ {
+ cmd.objectId = relid;
+ cmd.objectname = stmt->sequence->relname;
+ cmd.schemaname = stmt->sequence->schemaname;
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
START_CRIT_SECTION();
MarkBufferDirty(buf);
***************
*** 496,501 **** AlterSequence(AlterSeqStmt *stmt)
--- 534,543 ----
process_owned_by(seqrel, owned_by);
relation_close(seqrel, NoLock);
+
+ /* Call AFTER ALTER SEQUENCE triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ ExecAfterCommandTriggers(&cmd);
}
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 41,46 ****
--- 41,47 ----
#include "catalog/pg_type_fn.h"
#include "catalog/storage.h"
#include "catalog/toasting.h"
+ #include "commands/cmdtrigger.h"
#include "commands/cluster.h"
#include "commands/comment.h"
#include "commands/defrem.h"
***************
*** 73,78 ****
--- 74,80 ----
#include "storage/lock.h"
#include "storage/predicate.h"
#include "storage/smgr.h"
+ #include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
***************
*** 738,743 **** RemoveRelations(DropStmt *drop)
--- 740,747 ----
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)
--- 788,794 ----
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)
--- 814,838 ----
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)
--- 843,858 ----
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);
}
}
--- 1170,1176 ----
/*
* Reconstruct the indexes to match, and we're done.
*/
! reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST, NULL);
}
}
***************
*** 2331,2337 **** renameatt(RenameStmt *stmt)
* Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE RENAME
*/
void
! RenameRelation(RenameStmt *stmt)
{
Oid relid;
--- 2362,2368 ----
* 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);
}
/*
--- 2387,2393 ----
}
/* 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 */
--- 2400,2406 ----
* 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)
--- 2431,2446 ----
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.
--- 2460,2466 ----
*/
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)
--- 2477,2489 ----
* 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
--- 2582,2588 ----
* 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
--- 5382,5388 ----
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 */
--- 5441,5447 ----
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);
--- 6919,6925 ----
/* 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)
--- 9703,9709 ----
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)
--- 9744,9768 ----
/* 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)
--- 9777,9789 ----
/* 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);
--- 9931,9937 ----
* 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(NameStr(dname));
+ cmd->schemaname = get_namespace_name(namespaceoid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
+ /*
* Looks good, insert
*/
memset(values, 0, sizeof(values));
***************
*** 570,582 **** DefineTSDictionary(List *names, List *parameters)
heap_freetuple(tup);
heap_close(dictRel, RowExclusiveLock);
}
/*
* ALTER TEXT SEARCH DICTIONARY RENAME
*/
void
! RenameTSDictionary(List *oldname, const char *newname)
{
HeapTuple tup;
Relation rel;
--- 619,638 ----
heap_freetuple(tup);
heap_close(dictRel, RowExclusiveLock);
+
+ /* Call AFTER CREATE TEXT SEARCH DICTIONARY triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectId = dictOid;
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
* ALTER TEXT SEARCH DICTIONARY RENAME
*/
void
! RenameTSDictionary(List *oldname, const char *newname, CommandContext cmd)
{
HeapTuple tup;
Relation rel;
***************
*** 615,633 **** RenameTSDictionary(List *oldname, const char *newname)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
namestrcpy(&(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname), newname);
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
heap_close(rel, NoLock);
heap_freetuple(tup);
}
/*
* ALTER TEXT SEARCH DICTIONARY any_name SET SCHEMA name
*/
void
! AlterTSDictionaryNamespace(List *name, const char *newschema)
{
Oid dictId,
nspOid;
--- 671,706 ----
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
+ /* Call BEFORE ALTER TEXT SEARCH DICTIONARY triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = dictId;
+ cmd->objectname = 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;
***************
*** 2022,2031 **** DefineCompositeType(RangeVar *typevar, List *coldeflist)
--- 2110,2138 ----
}
/*
+ * Call BEFORE CREATE (composite) TYPE triggers
+ */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = InvalidOid;
+ cmd->objectname = createStmt->relation->relname;
+ cmd->schemaname = get_namespace_name(typeNamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
+ /*
* Finally create the relation. This also creates the type.
*/
relid = DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid);
Assert(relid != InvalidOid);
+
+ /* Call AFTER CREATE (composite) TYPE triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectId = relid;
+ ExecAfterCommandTriggers(cmd);
+ }
return relid;
}
***************
*** 2035,2041 **** DefineCompositeType(RangeVar *typevar, List *coldeflist)
* Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements.
*/
void
! AlterDomainDefault(List *names, Node *defaultRaw)
{
TypeName *typename;
Oid domainoid;
--- 2142,2148 ----
* Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements.
*/
void
! AlterDomainDefault(List *names, Node *defaultRaw, CommandContext cmd)
{
TypeName *typename;
Oid domainoid;
***************
*** 2065,2070 **** AlterDomainDefault(List *names, Node *defaultRaw)
--- 2172,2187 ----
/* Check it's a domain and check user has permission for ALTER DOMAIN */
checkDomainOwner(tup);
+ /* Call BEFORE ALTER DOMAIN triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = 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)
--- 2278,2287 ----
/* Clean up */
heap_close(rel, NoLock);
heap_freetuple(newtuple);
+
+ /* Call AFTER ALTER DOMAIN triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
/*
***************
*** 2169,2175 **** AlterDomainDefault(List *names, Node *defaultRaw)
* Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements.
*/
void
! AlterDomainNotNull(List *names, bool notNull)
{
TypeName *typename;
Oid domainoid;
--- 2290,2296 ----
* Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements.
*/
void
! AlterDomainNotNull(List *names, bool notNull, CommandContext cmd)
{
TypeName *typename;
Oid domainoid;
***************
*** 2192,2197 **** AlterDomainNotNull(List *names, bool notNull)
--- 2313,2328 ----
/* Check it's a domain and check user has permission for ALTER DOMAIN */
checkDomainOwner(tup);
+ /* Call BEFORE ALTER DOMAIN triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = 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)
--- 2388,2397 ----
/* Clean up */
heap_freetuple(tup);
heap_close(typrel, RowExclusiveLock);
+
+ /* Call AFTER ALTER DOMAIN triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
/*
***************
*** 2266,2272 **** AlterDomainNotNull(List *names, bool notNull)
*/
void
AlterDomainDropConstraint(List *names, const char *constrName,
! DropBehavior behavior, bool missing_ok)
{
TypeName *typename;
Oid domainoid;
--- 2401,2407 ----
*/
void
AlterDomainDropConstraint(List *names, const char *constrName,
! DropBehavior behavior, bool missing_ok, CommandContext cmd)
{
TypeName *typename;
Oid domainoid;
***************
*** 2292,2297 **** AlterDomainDropConstraint(List *names, const char *constrName,
--- 2427,2442 ----
/* Check it's a domain and check user has permission for ALTER DOMAIN */
checkDomainOwner(tup);
+ /* Call BEFORE ALTER DOMAIN triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = pstrdup(NameStr(((Form_pg_type)tup)->typname));
+ cmd->schemaname = get_namespace_name(((Form_pg_type)tup)->typnamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* Grab an appropriate lock on the pg_constraint relation */
conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
***************
*** 2341,2346 **** AlterDomainDropConstraint(List *names, const char *constrName,
--- 2486,2495 ----
(errmsg("constraint \"%s\" of domain \"%s\" does not exist, skipping",
constrName, TypeNameToString(typename))));
}
+
+ /* Call AFTER ALTER DOMAIN triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
/*
***************
*** 2349,2355 **** AlterDomainDropConstraint(List *names, const char *constrName,
* Implements the ALTER DOMAIN .. ADD CONSTRAINT statement.
*/
void
! AlterDomainAddConstraint(List *names, Node *newConstraint)
{
TypeName *typename;
Oid domainoid;
--- 2498,2504 ----
* Implements the ALTER DOMAIN .. ADD CONSTRAINT statement.
*/
void
! AlterDomainAddConstraint(List *names, Node *newConstraint, CommandContext cmd)
{
TypeName *typename;
Oid domainoid;
***************
*** 2378,2383 **** AlterDomainAddConstraint(List *names, Node *newConstraint)
--- 2527,2542 ----
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(newConstraint));
+ /* Call BEFORE ALTER DOMAIN triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = 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)
--- 2603,2612 ----
/* Clean up */
heap_close(typrel, RowExclusiveLock);
+
+ /* Call AFTER ALTER DOMAIN triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
/*
***************
*** 2452,2458 **** AlterDomainAddConstraint(List *names, Node *newConstraint)
* Implements the ALTER DOMAIN .. VALIDATE CONSTRAINT statement.
*/
void
! AlterDomainValidateConstraint(List *names, char *constrName)
{
TypeName *typename;
Oid domainoid;
--- 2615,2621 ----
* Implements the ALTER DOMAIN .. VALIDATE CONSTRAINT statement.
*/
void
! AlterDomainValidateConstraint(List *names, char *constrName, CommandContext cmd)
{
TypeName *typename;
Oid domainoid;
***************
*** 2525,2530 **** AlterDomainValidateConstraint(List *names, char *constrName)
--- 2688,2703 ----
HeapTupleGetOid(tuple));
conbin = TextDatumGetCString(val);
+ /* Call BEFORE ALTER DOMAIN triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = pstrdup(NameStr(((Form_pg_type)tup)->typname));
+ cmd->schemaname = get_namespace_name(((Form_pg_type)tup)->typnamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
validateDomainConstraint(domainoid, conbin);
/*
***************
*** 2543,2548 **** AlterDomainValidateConstraint(List *names, char *constrName)
--- 2716,2725 ----
heap_close(conrel, RowExclusiveLock);
ReleaseSysCache(tup);
+
+ /* Call AFTER ALTER DOMAIN triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
static void
***************
*** 3091,3097 **** GetDomainConstraints(Oid typeOid)
* Execute ALTER TYPE RENAME
*/
void
! RenameType(RenameStmt *stmt)
{
List *names = stmt->object;
const char *newTypeName = stmt->newname;
--- 3268,3274 ----
* Execute ALTER TYPE RENAME
*/
void
! RenameType(RenameStmt *stmt, CommandContext cmd)
{
List *names = stmt->object;
const char *newTypeName = stmt->newname;
***************
*** 3153,3162 **** RenameType(RenameStmt *stmt)
* RenameRelationInternal will call RenameTypeInternal automatically.
*/
if (typTup->typtype == TYPTYPE_COMPOSITE)
! RenameRelationInternal(typTup->typrelid, newTypeName);
else
RenameTypeInternal(typeOid, newTypeName,
! typTup->typnamespace);
/* Clean up */
heap_close(rel, RowExclusiveLock);
--- 3330,3339 ----
* RenameRelationInternal will call RenameTypeInternal automatically.
*/
if (typTup->typtype == TYPTYPE_COMPOSITE)
! RenameRelationInternal(typTup->typrelid, newTypeName, cmd);
else
RenameTypeInternal(typeOid, newTypeName,
! typTup->typnamespace, cmd);
/* Clean up */
heap_close(rel, RowExclusiveLock);
***************
*** 3166,3172 **** RenameType(RenameStmt *stmt)
* Change the owner of a type.
*/
void
! AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype)
{
TypeName *typename;
Oid typeOid;
--- 3343,3350 ----
* Change the owner of a type.
*/
void
! AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype,
! CommandContext cmd)
{
TypeName *typename;
Oid typeOid;
***************
*** 3252,3257 **** AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype)
--- 3430,3445 ----
get_namespace_name(typTup->typnamespace));
}
+ /* Call BEFORE ALTER TYPE triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = typeOid;
+ cmd->objectname = (char *)typename;
+ cmd->schemaname = get_namespace_name(typTup->typnamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/*
* If it's a composite type, invoke ATExecChangeOwner so that we fix
* up the pg_class entry properly. That will call back to
***************
*** 3279,3284 **** AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype)
--- 3467,3476 ----
if (OidIsValid(typTup->typarray))
AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false);
}
+
+ /* Call AFTER ALTER TYPE triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
/* Clean up */
***************
*** 3336,3342 **** AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId,
* Execute ALTER TYPE SET SCHEMA
*/
void
! AlterTypeNamespace(List *names, const char *newschema, ObjectType objecttype)
{
TypeName *typename;
Oid typeOid;
--- 3528,3535 ----
* Execute ALTER TYPE SET SCHEMA
*/
void
! AlterTypeNamespace(List *names, const char *newschema, ObjectType objecttype,
! CommandContext cmd)
{
TypeName *typename;
Oid typeOid;
***************
*** 3356,3366 **** AlterTypeNamespace(List *names, const char *newschema, ObjectType objecttype)
/* get schema OID and check its permissions */
nspOid = LookupCreationNamespace(newschema);
! AlterTypeNamespace_oid(typeOid, nspOid);
}
Oid
! AlterTypeNamespace_oid(Oid typeOid, Oid nspOid)
{
Oid elemOid;
--- 3549,3559 ----
/* get schema OID and check its permissions */
nspOid = LookupCreationNamespace(newschema);
! AlterTypeNamespace_oid(typeOid, nspOid, cmd);
}
Oid
! AlterTypeNamespace_oid(Oid typeOid, Oid nspOid, CommandContext cmd)
{
Oid elemOid;
***************
*** 3380,3386 **** AlterTypeNamespace_oid(Oid typeOid, Oid nspOid)
format_type_be(elemOid))));
/* and do the work */
! return AlterTypeNamespaceInternal(typeOid, nspOid, false, true);
}
/*
--- 3573,3579 ----
format_type_be(elemOid))));
/* and do the work */
! return AlterTypeNamespaceInternal(typeOid, nspOid, false, true, cmd);
}
/*
***************
*** 3401,3407 **** AlterTypeNamespace_oid(Oid typeOid, Oid nspOid)
Oid
AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
bool isImplicitArray,
! bool errorOnTableType)
{
Relation rel;
HeapTuple tup;
--- 3594,3601 ----
Oid
AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
bool isImplicitArray,
! bool errorOnTableType,
! CommandContext cmd)
{
Relation rel;
HeapTuple tup;
***************
*** 3447,3452 **** AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
--- 3641,3656 ----
format_type_be(typeOid)),
errhint("Use ALTER TABLE instead.")));
+ /* Call BEFORE ALTER TYPE triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = typeOid;
+ cmd->objectname = 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;
}
--- 3708,3719 ----
/* Recursively alter the associated array type, if any */
if (OidIsValid(arrayOid))
! AlterTypeNamespaceInternal(arrayOid, nspOid, true, true, NULL);
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->schemaname = get_namespace_name(nspOid);
+ ExecAfterCommandTriggers(cmd);
+ }
return oldNspOid;
}
*** a/src/backend/commands/vacuum.c
--- b/src/backend/commands/vacuum.c
***************
*** 30,35 ****
--- 30,36 ----
#include "catalog/namespace.h"
#include "catalog/pg_database.h"
#include "catalog/pg_namespace.h"
+ #include "commands/cmdtrigger.h"
#include "commands/cluster.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
***************
*** 122,127 **** vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
--- 123,144 ----
else
in_outer_xact = IsInTransactionChain(isTopLevel);
+ /* CAll BEFORE VACUUM command triggers */
+ if (!IsAutoVacuumWorkerProcess() && vacstmt->relation != NULL)
+ {
+ CommandContextData cmd;
+ InitCommandContext(&cmd, (Node *)vacstmt, false);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = relid;
+ cmd.objectname = vacstmt->relation->relname;
+ cmd.schemaname = vacstmt->relation->schemaname;
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+ }
+
/*
* Send info about dead objects to the statistics collector, unless we are
* in autovacuum --- autovacuum.c does this for itself.
***************
*** 1054,1060 **** vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound)
/* VACUUM FULL is now a variant of CLUSTER; see cluster.c */
cluster_rel(relid, InvalidOid, false,
(vacstmt->options & VACOPT_VERBOSE) != 0,
! vacstmt->freeze_min_age, vacstmt->freeze_table_age);
}
else
lazy_vacuum_rel(onerel, vacstmt, vac_strategy);
--- 1071,1077 ----
/* VACUUM FULL is now a variant of CLUSTER; see cluster.c */
cluster_rel(relid, InvalidOid, false,
(vacstmt->options & VACOPT_VERBOSE) != 0,
! vacstmt->freeze_min_age, vacstmt->freeze_table_age, NULL);
}
else
lazy_vacuum_rel(onerel, vacstmt, vac_strategy);
*** a/src/backend/commands/view.c
--- b/src/backend/commands/view.c
***************
*** 28,33 ****
--- 28,34 ----
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteManip.h"
#include "rewrite/rewriteSupport.h"
+ #include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
***************
*** 99,105 **** isViewOnTempTable_walker(Node *node, void *context)
*/
static Oid
DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
! List *options)
{
Oid viewOid;
LOCKMODE lockmode;
--- 100,106 ----
*/
static Oid
DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
! List *options, CommandContext cmd)
{
Oid viewOid;
LOCKMODE lockmode;
***************
*** 168,173 **** DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
--- 169,186 ----
lockmode = replace ? AccessExclusiveLock : NoLock;
(void) RangeVarGetAndCheckCreationNamespace(relation, lockmode, &viewOid);
+ /*
+ * Call BEFORE CREATE VIEW triggers
+ */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = InvalidOid;
+ cmd->objectname = relation->relname;
+ cmd->schemaname = relation->schemaname;
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
if (OidIsValid(viewOid) && replace)
{
Relation rel;
***************
*** 343,349 **** DefineViewRules(Oid viewOid, Query *viewParse, bool replace)
CMD_SELECT,
true,
replace,
! list_make1(viewParse));
/*
* Someday: automatic ON INSERT, etc
--- 356,363 ----
CMD_SELECT,
true,
replace,
! list_make1(viewParse),
! NULL);
/*
* Someday: automatic ON INSERT, etc
***************
*** 426,431 **** DefineView(ViewStmt *stmt, const char *queryString)
--- 440,446 ----
Query *viewParse;
Oid viewOid;
RangeVar *view;
+ CommandContextData cmd;
/*
* Run parse analysis to convert the raw parse tree to a Query. Note this
***************
*** 511,523 **** DefineView(ViewStmt *stmt, const char *queryString)
}
/*
* Create the view relation
*
* NOTE: if it already exists and replace is false, the xact will be
* aborted.
*/
viewOid = DefineVirtualRelation(view, viewParse->targetList,
! stmt->replace, stmt->options);
/*
* The relation we have just created is not visible to any other commands
--- 526,546 ----
}
/*
+ * Prepare BEFORE CREATE VIEW triggers
+ */
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
+ /*
* Create the view relation
*
* NOTE: if it already exists and replace is false, the xact will be
* aborted.
*/
viewOid = DefineVirtualRelation(view, viewParse->targetList,
! stmt->replace, stmt->options, &cmd);
!
! if (!OidIsValid(viewOid))
! return;
/*
* The relation we have just created is not visible to any other commands
***************
*** 536,539 **** DefineView(ViewStmt *stmt, const char *queryString)
--- 559,569 ----
* Now create the rules associated with the view.
*/
DefineViewRules(viewOid, viewParse, stmt->replace);
+
+ /* Call AFTER CREATE VIEW triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = viewOid;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 3458,3463 **** _copyCreateTrigStmt(const CreateTrigStmt *from)
--- 3458,3488 ----
return newnode;
}
+ static CreateCmdTrigStmt *
+ _copyCreateCmdTrigStmt(const CreateCmdTrigStmt *from)
+ {
+ CreateCmdTrigStmt *newnode = makeNode(CreateCmdTrigStmt);
+
+ COPY_NODE_FIELD(command);
+ COPY_STRING_FIELD(trigname);
+ COPY_SCALAR_FIELD(timing);
+ COPY_NODE_FIELD(funcname);
+
+ return newnode;
+ }
+
+ static AlterCmdTrigStmt *
+ _copyAlterCmdTrigStmt(const AlterCmdTrigStmt *from)
+ {
+ AlterCmdTrigStmt *newnode = makeNode(AlterCmdTrigStmt);
+
+ COPY_STRING_FIELD(command);
+ COPY_STRING_FIELD(trigname);
+ COPY_STRING_FIELD(tgenabled);
+
+ return newnode;
+ }
+
static CreatePLangStmt *
_copyCreatePLangStmt(const CreatePLangStmt *from)
{
***************
*** 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)); \
--- 3712,3718 ----
/*
* 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)
--- 4335,4346 ----
case T_CreateTrigStmt:
retval = _copyCreateTrigStmt(from);
break;
+ case T_CreateCmdTrigStmt:
+ retval = _copyCreateCmdTrigStmt(from);
+ break;
+ case T_AlterCmdTrigStmt:
+ retval = _copyAlterCmdTrigStmt(from);
+ break;
case T_CreatePLangStmt:
retval = _copyCreatePLangStmt(from);
break;
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 1778,1783 **** _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
--- 1778,1804 ----
}
static bool
+ _equalCreateCmdTrigStmt(const CreateCmdTrigStmt *a, const CreateCmdTrigStmt *b)
+ {
+ COMPARE_NODE_FIELD(command);
+ COMPARE_STRING_FIELD(trigname);
+ COMPARE_SCALAR_FIELD(timing);
+ COMPARE_NODE_FIELD(funcname);
+
+ return true;
+ }
+
+ static bool
+ _equalAlterCmdTrigStmt(const AlterCmdTrigStmt *a, const AlterCmdTrigStmt *b)
+ {
+ COMPARE_STRING_FIELD(command);
+ COMPARE_STRING_FIELD(trigname);
+ COMPARE_STRING_FIELD(tgenabled);
+
+ return true;
+ }
+
+ static bool
_equalCreatePLangStmt(const CreatePLangStmt *a, const CreatePLangStmt *b)
{
COMPARE_SCALAR_FIELD(replace);
***************
*** 2853,2858 **** equal(const void *a, const void *b)
--- 2874,2885 ----
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,4433 ----
/*****************************************************************************
*
* 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 AGGREGATE { $$ = "ALTER AGGREGATE"; }
+ | ALTER OPERATOR { $$ = "ALTER OPERATOR"; }
+ | ALTER OPERATOR CLASS { $$ = "ALTER OPERATOR CLASS"; }
+ | ALTER OPERATOR FAMILY { $$ = "ALTER OPERATOR FAMILY"; }
+ | ALTER COLLATION { $$ = "ALTER COLLATION"; }
+ | ALTER TEXT_P SEARCH PARSER { $$ = "ALTER TEXT SEARCH PARSER"; }
+ | ALTER TEXT_P SEARCH DICTIONARY { $$ = "ALTER TEXT SEARCH DICTIONARY"; }
+ | ALTER TEXT_P SEARCH TEMPLATE { $$ = "ALTER TEXT SEARCH TEMPLATE"; }
+ | ALTER TEXT_P SEARCH CONFIGURATION { $$ = "ALTER TEXT SEARCH CONFIGURATION"; }
+ | ALTER TYPE_P { $$ = "ALTER TYPE"; }
+ | ALTER DOMAIN_P { $$ = "ALTER DOMAIN"; }
+ | ALTER TRIGGER { $$ = "ALTER TRIGGER"; }
+ | DROP TABLE { $$ = "DROP TABLE"; }
+ | DROP SEQUENCE { $$ = "DROP SEQUENCE"; }
+ | DROP VIEW { $$ = "DROP VIEW"; }
+ | DROP INDEX { $$ = "DROP INDEX"; }
+ | DROP TYPE_P { $$ = "DROP TYPE"; }
+ | DROP DOMAIN_P { $$ = "DROP DOMAIN"; }
+ | DROP COLLATION { $$ = "DROP COLLATION"; }
+ | DROP CONVERSION_P { $$ = "DROP CONVERSION"; }
+ | 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
--- 6931,6945 ----
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER TRIGGER name ON COMMAND trigger_command RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_CMDTRIGGER;
+ n->object = list_make1(makeString($6));
+ n->subname = $3;
+ n->newname = $9;
+ $$ = (Node *)n;
+ }
| ALTER ROLE RoleId RENAME TO RoleId
{
RenameStmt *n = makeNode(RenameStmt);
*** a/src/backend/rewrite/rewriteDefine.c
--- b/src/backend/rewrite/rewriteDefine.c
***************
*** 195,204 **** DefineRule(RuleStmt *stmt, const char *queryString)
--- 195,208 ----
List *actions;
Node *whereClause;
Oid relId;
+ CommandContextData cmd;
/* Parse analysis. */
transformRuleStmt(stmt, queryString, &actions, &whereClause);
+ /* Prepare command context */
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
/*
* Find and lock the relation. Lock level should match
* DefineQueryRewrite.
***************
*** 212,218 **** DefineRule(RuleStmt *stmt, const char *queryString)
stmt->event,
stmt->instead,
stmt->replace,
! actions);
}
--- 216,223 ----
stmt->event,
stmt->instead,
stmt->replace,
! actions,
! &cmd);
}
***************
*** 230,236 **** DefineQueryRewrite(char *rulename,
CmdType event_type,
bool is_instead,
bool replace,
! List *action)
{
Relation event_relation;
int event_attno;
--- 235,242 ----
CmdType event_type,
bool is_instead,
bool replace,
! List *action,
! CommandContext cmd)
{
Relation event_relation;
int event_attno;
***************
*** 480,485 **** DefineQueryRewrite(char *rulename,
--- 486,502 ----
}
}
+ /* Call BEFORE CREATE RULE triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = InvalidOid;
+ cmd->objectname = rulename;
+ cmd->schemaname =
+ get_namespace_name(RelationGetNamespace(event_relation));
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/*
* This rule is allowed - prepare to install it.
*/
***************
*** 520,525 **** DefineQueryRewrite(char *rulename,
--- 537,546 ----
/* Close rel, but keep lock till commit... */
heap_close(event_relation, NoLock);
+
+ /* Call AFTER CREATE RULE triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
/*
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 25,30 ****
--- 25,31 ----
#include "commands/alter.h"
#include "commands/async.h"
#include "commands/cluster.h"
+ #include "commands/cmdtrigger.h"
#include "commands/comment.h"
#include "commands/collationcmds.h"
#include "commands/conversioncmds.h"
***************
*** 58,66 ****
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/guc.h"
#include "utils/syscache.h"
-
/* Hook for plugins to get control in ProcessUtility() */
ProcessUtility_hook_type ProcessUtility_hook = NULL;
--- 59,67 ----
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/guc.h"
+ #include "utils/lsyscache.h"
#include "utils/syscache.h"
/* Hook for plugins to get control in ProcessUtility() */
ProcessUtility_hook_type ProcessUtility_hook = NULL;
***************
*** 150,155 **** CommandIsReadOnly(Node *parsetree)
--- 151,328 ----
}
/*
+ * Support function for calling the command triggers.
+ */
+ static void
+ call_before_cmdtriggers(Node *parsetree, CommandContext cmd)
+ {
+ switch (nodeTag(parsetree))
+ {
+ case T_AlterDatabaseStmt:
+ case T_AlterDatabaseSetStmt:
+ case T_AlterDomainStmt:
+ case T_AlterFunctionStmt:
+ case T_AlterRoleStmt:
+ case T_AlterRoleSetStmt:
+ case T_AlterObjectSchemaStmt:
+ case T_AlterOwnerStmt:
+ case T_AlterSeqStmt:
+ case T_AlterTableStmt:
+ case T_RenameStmt:
+ case T_CommentStmt:
+ case T_DefineStmt:
+ case T_CreateCastStmt:
+ case T_CreateConversionStmt:
+ case T_CreatedbStmt:
+ case T_CreateDomainStmt:
+ case T_CreateFunctionStmt:
+ case T_CreateRoleStmt:
+ case T_IndexStmt:
+ case T_CreatePLangStmt:
+ case T_CreateOpClassStmt:
+ case T_CreateOpFamilyStmt:
+ case T_AlterOpFamilyStmt:
+ case T_RuleStmt:
+ case T_CreateSchemaStmt:
+ case T_CreateSeqStmt:
+ case T_CreateStmt:
+ case T_CreateTableSpaceStmt:
+ case T_CreateTrigStmt:
+ case T_CompositeTypeStmt:
+ case T_CreateEnumStmt:
+ case T_CreateRangeStmt:
+ case T_AlterEnumStmt:
+ case T_ViewStmt:
+ case T_DropdbStmt:
+ case T_DropTableSpaceStmt:
+ case T_DropRoleStmt:
+ case T_GrantStmt:
+ case T_GrantRoleStmt:
+ case T_AlterDefaultPrivilegesStmt:
+ case T_TruncateStmt:
+ case T_DropOwnedStmt:
+ case T_ReassignOwnedStmt:
+ case T_AlterTSDictionaryStmt:
+ case T_AlterTSConfigurationStmt:
+ case T_CreateExtensionStmt:
+ case T_AlterExtensionStmt:
+ case T_AlterExtensionContentsStmt:
+ case T_CreateFdwStmt:
+ case T_AlterFdwStmt:
+ case T_CreateForeignServerStmt:
+ case T_AlterForeignServerStmt:
+ case T_CreateUserMappingStmt:
+ case T_AlterUserMappingStmt:
+ case T_DropUserMappingStmt:
+ case T_AlterTableSpaceOptionsStmt:
+ case T_CreateForeignTableStmt:
+ case T_ClusterStmt:
+ case T_VacuumStmt:
+ case T_LoadStmt:
+ case T_ReindexStmt:
+ if (CommandFiresTriggers(cmd))
+ ExecBeforeCommandTriggers(cmd);
+ return;
+
+ case T_DropStmt:
+ if (((DropStmt *) cmd->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_AlterDatabaseStmt:
+ case T_AlterDatabaseSetStmt:
+ case T_AlterDomainStmt:
+ case T_AlterFunctionStmt:
+ case T_AlterRoleStmt:
+ case T_AlterRoleSetStmt:
+ case T_AlterObjectSchemaStmt:
+ case T_AlterOwnerStmt:
+ case T_AlterSeqStmt:
+ case T_AlterTableStmt:
+ case T_RenameStmt:
+ case T_CommentStmt:
+ case T_DefineStmt:
+ case T_CreateCastStmt:
+ case T_CreateConversionStmt:
+ case T_CreatedbStmt:
+ case T_CreateDomainStmt:
+ case T_CreateFunctionStmt:
+ case T_CreateRoleStmt:
+ case T_CreatePLangStmt:
+ case T_CreateOpClassStmt:
+ case T_CreateOpFamilyStmt:
+ case T_AlterOpFamilyStmt:
+ case T_RuleStmt:
+ case T_CreateSchemaStmt:
+ case T_CreateSeqStmt:
+ case T_CreateStmt:
+ case T_CreateTableSpaceStmt:
+ case T_CreateTrigStmt:
+ case T_CompositeTypeStmt:
+ case T_CreateEnumStmt:
+ case T_CreateRangeStmt:
+ case T_AlterEnumStmt:
+ case T_ViewStmt:
+ case T_DropdbStmt:
+ case T_DropTableSpaceStmt:
+ case T_DropRoleStmt:
+ case T_GrantStmt:
+ case T_GrantRoleStmt:
+ case T_AlterDefaultPrivilegesStmt:
+ case T_TruncateStmt:
+ case T_DropOwnedStmt:
+ case T_ReassignOwnedStmt:
+ case T_AlterTSDictionaryStmt:
+ case T_AlterTSConfigurationStmt:
+ case T_CreateExtensionStmt:
+ case T_AlterExtensionStmt:
+ case T_AlterExtensionContentsStmt:
+ case T_CreateFdwStmt:
+ case T_AlterFdwStmt:
+ case T_CreateForeignServerStmt:
+ case T_AlterForeignServerStmt:
+ case T_CreateUserMappingStmt:
+ case T_AlterUserMappingStmt:
+ case T_DropUserMappingStmt:
+ case T_AlterTableSpaceOptionsStmt:
+ case T_CreateForeignTableStmt:
+ case T_LoadStmt:
+ case T_ReindexStmt:
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
+ return;
+
+ case T_IndexStmt:
+ if (!((IndexStmt *)cmd->parsetree)->concurrent)
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
+ return;
+
+ case T_DropStmt:
+ if (((DropStmt *) cmd->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)
--- 357,364 ----
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,
--- 519,535 ----
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)
--- 690,716 ----
{
List *stmts;
ListCell *l;
! Oid relOid = InvalidOid;
! CreateStmt *stmt = (CreateStmt *) parsetree;
! CommandContextData cmd;
!
! /*
! * Call BEFORE CREATE TABLE triggers
! */
! InitCommandContext(&cmd, parsetree, false);
!
! if (CommandFiresTriggers(&cmd))
! {
! cmd.objectId = InvalidOid;
! cmd.objectname = stmt->relation->relname;
! cmd.schemaname = get_namespace_name(
! RangeVarGetCreationNamespace(stmt->relation));
!
! ExecBeforeCommandTriggers(&cmd);
! }
/* Run parse analysis ... */
! stmts = transformCreateStmt(stmt, queryString);
/* ... and do it */
foreach(l, stmts)
***************
*** 571,576 **** standard_ProcessUtility(Node *parsetree,
--- 768,780 ----
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,
--- 908,914 ----
List *stmts;
ListCell *l;
LOCKMODE lockmode;
+ CommandContextData cmd;
/*
* Figure out lock mode, and acquire lock. This also does
***************
*** 716,721 **** standard_ProcessUtility(Node *parsetree,
--- 921,941 ----
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,
--- 964,972 ----
if (lnext(l) != NULL)
CommandCounterIncrement();
}
+ /* Call AFTER ALTER TABLE triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ ExecAfterCommandTriggers(&cmd);
}
else
ereport(NOTICE,
***************
*** 755,760 **** standard_ProcessUtility(Node *parsetree,
--- 978,989 ----
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",
--- 998,1027 ----
* 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",
--- 1049,1091 ----
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;
--- 1098,1107 ----
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,
--- 1139,1147 ----
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;
--- 1172,1179 ----
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;
--- 1211,1217 ----
DropdbStmt *stmt = (DropdbStmt *) parsetree;
PreventTransactionChain(isTopLevel, "DROP DATABASE");
! dropdb(stmt);
}
break;
***************
*** 1012,1021 **** standard_ProcessUtility(Node *parsetree,
--- 1251,1274 ----
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,
--- 1312,1325 ----
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:
--- 1392,1410 ----
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,
--- 1461,1469 ----
(int) nodeTag(parsetree));
break;
}
+
+ /* call the AFTER ANY COMMAND triggers */
+ call_after_cmdtriggers(parsetree, &cmd);
}
/*
***************
*** 1704,1709 **** CreateCommandTag(Node *parsetree)
--- 1971,1979 ----
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)
--- 2220,2233 ----
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)
--- 2429,2441 ----
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)
--- 2730,2747 ----
lev = LOGSTMT_DDL;
break;
+ case T_DropPropertyStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
+ case T_CreateCmdTrigStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
+ case T_AlterCmdTrigStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_CreatePLangStmt:
lev = LOGSTMT_DDL;
break;
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
***************
*** 39,47 ****
--- 39,49 ----
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/tlist.h"
+ #include "parser/analyze.h"
#include "parser/keywords.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
+ #include "parser/parse_type.h"
#include "parser/parser.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h"
***************
*** 259,265 **** static char *flatten_reloptions(Oid relid);
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
-
/* ----------
* get_ruledef - Do it all and return a text
* that could be used as a statement
--- 261,266 ----
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 48,53 ****
--- 48,54 ----
#include "access/transam.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_class.h"
+ #include "catalog/pg_cmdtrigger.h"
#include "catalog/pg_default_acl.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
***************
*** 191,196 **** static void dumpConversion(Archive *fout, ConvInfo *convinfo);
--- 192,198 ----
static void dumpRule(Archive *fout, RuleInfo *rinfo);
static void dumpAgg(Archive *fout, AggInfo *agginfo);
static void dumpTrigger(Archive *fout, TriggerInfo *tginfo);
+ static void dumpCmdTrigger(Archive *fout, CmdTriggerInfo *ctginfo);
static void dumpTable(Archive *fout, TableInfo *tbinfo);
static void dumpTableSchema(Archive *fout, TableInfo *tbinfo);
static void dumpAttrDef(Archive *fout, AttrDefInfo *adinfo);
***************
*** 5283,5288 **** getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
--- 5285,5359 ----
}
/*
+ * getCmdTriggers
+ * get information about every command trigger on a dumpable table
+ */
+ CmdTriggerInfo *
+ getCmdTriggers(Archive *fout, int *numCmdTriggers)
+ {
+ int i;
+ PQExpBuffer query = createPQExpBuffer();
+ PGresult *res;
+ CmdTriggerInfo *ctginfo;
+ int i_tableoid,
+ i_oid,
+ i_ctgcommand,
+ i_ctgname,
+ i_ctgfname,
+ i_ctgtype,
+ i_ctgenabled;
+ int ntups;
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(fout, "pg_catalog");
+
+ if (fout->remoteVersion >= 90200)
+ {
+ appendPQExpBuffer(query,
+ "SELECT c.tableoid, c.oid, "
+ "ctgname, ctgtype, ctgcommand, proname as ctgfname, ctgenabled "
+ "FROM pg_cmdtrigger c JOIN pg_proc p on c.ctgfoid = p.oid "
+ "ORDER BY c.oid");
+ }
+
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ *numCmdTriggers = ntups;
+
+ ctginfo = (CmdTriggerInfo *) pg_malloc(ntups * sizeof(CmdTriggerInfo));
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_ctgname = PQfnumber(res, "ctgname");
+ i_ctgtype = PQfnumber(res, "ctgtype");
+ i_ctgcommand = PQfnumber(res, "ctgcommand");
+ i_ctgfname = PQfnumber(res, "ctgfname");
+ i_ctgenabled = PQfnumber(res, "ctgenabled");
+
+ for (i = 0; i < ntups; i++)
+ {
+ ctginfo[i].dobj.objType = DO_CMDTRIGGER;
+ ctginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ ctginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&ctginfo[i].dobj);
+ ctginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_ctgname));
+ ctginfo[i].ctgname = pg_strdup(PQgetvalue(res, i, i_ctgname));
+ ctginfo[i].ctgtype = *(PQgetvalue(res, i, i_ctgtype));
+ ctginfo[i].ctgcommand = pg_strdup(PQgetvalue(res, i, i_ctgcommand));
+ ctginfo[i].ctgfname = pg_strdup(PQgetvalue(res, i, i_ctgfname));
+ ctginfo[i].ctgenabled = *(PQgetvalue(res, i, i_ctgenabled));
+ }
+
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+
+ return ctginfo;
+ }
+
+ /*
* getProcLangs
* get basic information about every procedural language in the system
*
***************
*** 7173,7178 **** dumpDumpableObject(Archive *fout, DumpableObject *dobj)
--- 7244,7252 ----
case DO_TRIGGER:
dumpTrigger(fout, (TriggerInfo *) dobj);
break;
+ case DO_CMDTRIGGER:
+ dumpCmdTrigger(fout, (CmdTriggerInfo *) dobj);
+ break;
case DO_CONSTRAINT:
dumpConstraint(fout, (ConstraintInfo *) dobj);
break;
***************
*** 13645,13650 **** dumpTrigger(Archive *fout, TriggerInfo *tginfo)
--- 13719,13776 ----
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, fmtId(ctginfo->ctgcommand));
+ }
+
+ appendPQExpBuffer(query, " EXECUTE PROCEDURE ");
+ appendPQExpBufferStr(query, fmtId(ctginfo->ctgfname));
+ appendPQExpBuffer(query, " ();\n");
+
+ appendPQExpBuffer(labelq, "TRIGGER %s ",
+ fmtId(ctginfo->dobj.name));
+ appendPQExpBuffer(labelq, "ON COMMAND %s",
+ fmtId(ctginfo->ctgcommand));
+
+ ArchiveEntry(fout, ctginfo->dobj.catId, ctginfo->dobj.dumpId,
+ ctginfo->dobj.name, NULL, NULL, "", false,
+ "COMMAND TRIGGER", SECTION_POST_DATA,
+ query->data, "", NULL, NULL, 0, NULL, NULL);
+
+ dumpComment(fout, labelq->data,
+ NULL, NULL,
+ ctginfo->dobj.catId, 0, ctginfo->dobj.dumpId);
+
+ destroyPQExpBuffer(query);
+ destroyPQExpBuffer(labelq);
+ }
+
/*
* dumpRule
* Dump a rule
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
***************
*** 118,124 **** typedef enum
DO_DEFAULT_ACL,
DO_BLOB,
DO_BLOB_DATA,
! DO_COLLATION
} DumpableObjectType;
typedef struct _dumpableObject
--- 118,125 ----
DO_DEFAULT_ACL,
DO_BLOB,
DO_BLOB_DATA,
! DO_COLLATION,
! DO_CMDTRIGGER
} DumpableObjectType;
typedef struct _dumpableObject
***************
*** 350,355 **** typedef struct _triggerInfo
--- 351,366 ----
char *tgdef;
} TriggerInfo;
+ typedef struct _cmdtriggerInfo
+ {
+ DumpableObject dobj;
+ char *ctgcommand;
+ char *ctgname;
+ char *ctgfname;
+ char ctgtype;
+ char ctgenabled;
+ } CmdTriggerInfo;
+
/*
* struct ConstraintInfo is used for all constraint types. However we
* use a different objType for foreign key constraints, to make it easier
***************
*** 558,562 **** extern ForeignServerInfo *getForeignServers(Archive *fout,
--- 569,574 ----
extern DefaultACLInfo *getDefaultACLs(Archive *fout, int *numDefaultACLs);
extern void getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
int numExtensions);
+ extern CmdTriggerInfo *getCmdTriggers(Archive *fout, int *numCmdTriggers);
#endif /* PG_DUMP_H */
*** a/src/bin/pg_dump/pg_dump_sort.c
--- b/src/bin/pg_dump/pg_dump_sort.c
***************
*** 59,65 **** static const int oldObjectTypePriority[] =
17, /* DO_DEFAULT_ACL */
9, /* DO_BLOB */
11, /* DO_BLOB_DATA */
! 2 /* DO_COLLATION */
};
/*
--- 59,66 ----
17, /* DO_DEFAULT_ACL */
9, /* DO_BLOB */
11, /* DO_BLOB_DATA */
! 2, /* DO_COLLATION */
! 18 /* DO_CMDTRIGGER */
};
/*
***************
*** 98,104 **** static const int newObjectTypePriority[] =
29, /* DO_DEFAULT_ACL */
21, /* DO_BLOB */
23, /* DO_BLOB_DATA */
! 3 /* DO_COLLATION */
};
--- 99,106 ----
29, /* DO_DEFAULT_ACL */
21, /* DO_BLOB */
23, /* DO_BLOB_DATA */
! 3, /* DO_COLLATION */
! 30 /* DO_CMDTRIGGER */
};
***************
*** 1114,1119 **** describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
--- 1116,1126 ----
"TRIGGER %s (ID %d OID %u)",
obj->name, obj->dumpId, obj->catId.oid);
return;
+ case DO_CMDTRIGGER:
+ snprintf(buf, bufsize,
+ "TRIGGER %s ON COMMAND %s (ID %d OID %u)",
+ obj->name, ((CmdTriggerInfo *)obj)->ctgcommand, obj->dumpId, obj->catId.oid);
+ return;
case DO_CONSTRAINT:
snprintf(buf, bufsize,
"CONSTRAINT %s (ID %d OID %u)",
*** a/src/bin/psql/command.c
--- b/src/bin/psql/command.c
***************
*** 363,369 **** exec_command(const char *cmd,
success = describeTablespaces(pattern, show_verbose);
break;
case 'c':
! success = listConversions(pattern, show_verbose, show_system);
break;
case 'C':
success = listCasts(pattern, show_verbose);
--- 363,380 ----
success = describeTablespaces(pattern, show_verbose);
break;
case 'c':
! switch (cmd[2])
! {
! case '\0':
! success = listConversions(pattern, show_verbose, show_system);
! break;
! case 't':
! success = listCmdTriggers(pattern, show_verbose);
! break;
! default:
! status = PSQL_CMD_UNKNOWN;
! break;
! }
break;
case 'C':
success = listCasts(pattern, show_verbose);
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
***************
*** 2941,2946 **** listConversions(const char *pattern, bool verbose, bool showSystem)
--- 2941,3003 ----
}
/*
+ * \dct
+ *
+ * Describes command triggers.
+ */
+ bool
+ listCmdTriggers(const char *pattern, bool verbose)
+ {
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+ static const bool translate_columns[] = {true, true};
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT ctgname as \"%s\", "
+ "'CREATE TRIGGER ' || ctgname || ' ' || "
+ "case ctgtype when 'A' then 'AFTER' "
+ " when 'B' then 'BEFORE' "
+ " when 'I' then 'INSTEAD OF' "
+ "end || "
+ "case ctgcommand when 'ANY' then ' ANY COMMAND '"
+ " else ' COMMAND ' || ctgcommand || ' '"
+ "end ||"
+ " 'EXECUTE PROCEDURE ' || proname || '();' as \"%s\" "
+ "FROM pg_cmdtrigger c "
+ "JOIN pg_proc p on c.ctgfoid = p.oid ",
+ gettext_noop("Name"),
+ gettext_noop("Definition"));
+
+ if (pattern)
+ {
+ processSQLNamePattern(pset.db, &buf, pattern, false, false,
+ NULL, "ctgcommand", NULL, NULL);
+
+ appendPQExpBuffer(&buf, " OR ctgcommand = 'ANY' ");
+ }
+
+ appendPQExpBuffer(&buf, "ORDER BY c.oid");
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of command triggers");
+ myopt.translate_header = true;
+ myopt.translate_columns = translate_columns;
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ PQclear(res);
+ return true;
+ }
+
+ /*
* \dC
*
* Describes casts.
*** a/src/bin/psql/describe.h
--- b/src/bin/psql/describe.h
***************
*** 66,71 **** extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
--- 66,74 ----
/* \dc */
extern bool listConversions(const char *pattern, bool verbose, bool showSystem);
+ /* \dcT */
+ extern bool listCmdTriggers(const char *pattern, bool verbose);
+
/* \dC */
extern bool listCasts(const char *pattern, bool verbose);
*** a/src/bin/psql/help.c
--- b/src/bin/psql/help.c
***************
*** 199,204 **** slashUsage(unsigned short int pager)
--- 199,205 ----
fprintf(output, _(" \\d[S+] NAME describe table, view, sequence, or index\n"));
fprintf(output, _(" \\da[S] [PATTERN] list aggregates\n"));
fprintf(output, _(" \\db[+] [PATTERN] list tablespaces\n"));
+ fprintf(output, _(" \\dct [PATTERN] list command triggers\n"));
fprintf(output, _(" \\dc[S+] [PATTERN] list conversions\n"));
fprintf(output, _(" \\dC[+] [PATTERN] list casts\n"));
fprintf(output, _(" \\dd[S] [PATTERN] show object descriptions not displayed elsewhere\n"));
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
***************
*** 146,151 **** typedef enum ObjectClass
--- 146,152 ----
OCLASS_USER_MAPPING, /* pg_user_mapping */
OCLASS_DEFACL, /* pg_default_acl */
OCLASS_EXTENSION, /* pg_extension */
+ OCLASS_CMDTRIGGER, /* pg_cmdtrigger */
MAX_OCLASS /* MUST BE LAST */
} ObjectClass;
*** a/src/include/catalog/index.h
--- b/src/include/catalog/index.h
***************
*** 14,19 ****
--- 14,20 ----
#ifndef INDEX_H
#define INDEX_H
+ #include "commands/cmdtrigger.h"
#include "nodes/execnodes.h"
***************
*** 88,101 **** extern double IndexBuildHeapScan(Relation heapRelation,
extern void validate_index(Oid heapId, Oid indexId, Snapshot snapshot);
! extern void reindex_index(Oid indexId, bool skip_constraint_checks);
/* Flag bits for reindex_relation(): */
#define REINDEX_REL_PROCESS_TOAST 0x01
#define REINDEX_REL_SUPPRESS_INDEX_USE 0x02
#define REINDEX_REL_CHECK_CONSTRAINTS 0x04
! extern bool reindex_relation(Oid relid, int flags);
extern bool ReindexIsProcessingHeap(Oid heapOid);
extern bool ReindexIsProcessingIndex(Oid indexOid);
--- 89,103 ----
extern void validate_index(Oid heapId, Oid indexId, Snapshot snapshot);
! extern void reindex_index(Oid indexId, bool skip_constraint_checks,
! CommandContext cmd);
/* Flag bits for reindex_relation(): */
#define REINDEX_REL_PROCESS_TOAST 0x01
#define REINDEX_REL_SUPPRESS_INDEX_USE 0x02
#define REINDEX_REL_CHECK_CONSTRAINTS 0x04
! extern bool reindex_relation(Oid relid, int flags, CommandContext cmd);
extern bool ReindexIsProcessingHeap(Oid heapOid);
extern bool ReindexIsProcessingIndex(Oid indexOid);
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
***************
*** 234,239 **** DECLARE_UNIQUE_INDEX(pg_trigger_tgrelid_tgname_index, 2701, on pg_trigger using
--- 234,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,56 ----
+ /*-------------------------------------------------------------------------
+ *
+ * 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(const char *trigname, const char *newname);
+
+ extern void InitCommandContext(CommandContext cmd, const Node *stmt, bool list_triggers);
+ extern bool ListCommandTriggers(CommandContext cmd);
+ extern bool CommandFiresTriggers(CommandContext cmd);
+ extern bool CommandFiresAfterTriggers(CommandContext cmd);
+ extern void ExecBeforeCommandTriggers(CommandContext cmd);
+ extern void ExecBeforeAnyCommandTriggers(CommandContext cmd);
+ extern void ExecAfterCommandTriggers(CommandContext cmd);
+ extern void ExecAfterAnyCommandTriggers(CommandContext cmd);
+
+ #endif /* CMD_TRIGGER_H */
*** a/src/include/commands/collationcmds.h
--- b/src/include/commands/collationcmds.h
***************
*** 15,27 ****
#ifndef COLLATIONCMDS_H
#define COLLATIONCMDS_H
#include "nodes/parsenodes.h"
! extern void DefineCollation(List *names, List *parameters);
! extern void RenameCollation(List *name, const char *newname);
! extern void AlterCollationOwner(List *name, Oid newOwnerId);
! extern void AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId);
! extern void AlterCollationNamespace(List *name, const char *newschema);
! extern Oid AlterCollationNamespace_oid(Oid collOid, Oid newNspOid);
#endif /* COLLATIONCMDS_H */
--- 15,30 ----
#ifndef COLLATIONCMDS_H
#define COLLATIONCMDS_H
+ #include "commands/cmdtrigger.h"
#include "nodes/parsenodes.h"
! extern void DefineCollation(List *names, List *parameters, CommandContext cmd);
! extern void RenameCollation(List *name, const char *newname, CommandContext cmd);
! extern void AlterCollationOwner(List *name, Oid newOwnerId, CommandContext cmd);
! extern void AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId, CommandContext cmd);
! extern void AlterCollationNamespace(List *name, const char *newschema,
! CommandContext cmd);
! extern Oid AlterCollationNamespace_oid(Oid collOid, Oid newNspOid,
! CommandContext cmd);
#endif /* COLLATIONCMDS_H */
*** a/src/include/commands/conversioncmds.h
--- b/src/include/commands/conversioncmds.h
***************
*** 18,27 ****
#include "nodes/parsenodes.h"
extern void CreateConversionCommand(CreateConversionStmt *parsetree);
! extern void RenameConversion(List *name, const char *newname);
! extern void AlterConversionOwner(List *name, Oid newOwnerId);
! extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId);
! extern void AlterConversionNamespace(List *name, const char *newschema);
extern Oid AlterConversionNamespace_oid(Oid convOid, Oid newNspOid);
#endif /* CONVERSIONCMDS_H */
--- 18,28 ----
#include "nodes/parsenodes.h"
extern void CreateConversionCommand(CreateConversionStmt *parsetree);
! extern void RenameConversion(List *name, const char *newname, CommandContext cmd);
! extern void AlterConversionOwner(List *name, Oid newOwnerId, CommandContext cmd);
! extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId, CommandContext cmd);
! extern void AlterConversionNamespace(List *name, const char *newschema,
! CommandContext cmd);
extern Oid AlterConversionNamespace_oid(Oid convOid, Oid newNspOid);
#endif /* CONVERSIONCMDS_H */
*** a/src/include/commands/dbcommands.h
--- b/src/include/commands/dbcommands.h
***************
*** 53,59 **** typedef struct xl_dbase_drop_rec
} xl_dbase_drop_rec;
extern void createdb(const CreatedbStmt *stmt);
! extern void dropdb(const char *dbname, bool missing_ok);
extern void RenameDatabase(const char *oldname, const char *newname);
extern void AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel);
extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt);
--- 53,59 ----
} xl_dbase_drop_rec;
extern void createdb(const CreatedbStmt *stmt);
! extern void dropdb(const DropdbStmt *stmt);
extern void RenameDatabase(const char *oldname, const char *newname);
extern void AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel);
extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt);
*** a/src/include/commands/defrem.h
--- b/src/include/commands/defrem.h
***************
*** 14,19 ****
--- 14,20 ----
#ifndef DEFREM_H
#define DEFREM_H
+ #include "commands/cmdtrigger.h"
#include "nodes/parsenodes.h"
/* commands/dropcmds.c */
***************
*** 39,47 **** extern Oid DefineIndex(RangeVar *heapRelation,
bool check_rights,
bool skip_build,
bool quiet,
! bool concurrent);
! extern void ReindexIndex(RangeVar *indexRelation);
! extern void ReindexTable(RangeVar *relation);
extern void ReindexDatabase(const char *databaseName,
bool do_system, bool do_user);
extern char *makeObjectName(const char *name1, const char *name2,
--- 40,49 ----
bool check_rights,
bool skip_build,
bool quiet,
! bool concurrent,
! CommandContext cmd);
! extern void ReindexIndex(RangeVar *indexRelation, CommandContext cmd);
! extern void ReindexTable(RangeVar *relation, CommandContext cmd);
extern void ReindexDatabase(const char *databaseName,
bool do_system, bool do_user);
extern char *makeObjectName(const char *name1, const char *name2,
***************
*** 64,95 **** extern void CreateFunction(CreateFunctionStmt *stmt, const char *queryString);
extern void RemoveFunctionById(Oid funcOid);
extern void SetFunctionReturnType(Oid funcOid, Oid newRetType);
extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType);
! extern void RenameFunction(List *name, List *argtypes, const char *newname);
! extern void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId);
! extern void AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId);
extern void AlterFunction(AlterFunctionStmt *stmt);
extern void CreateCast(CreateCastStmt *stmt);
extern void DropCastById(Oid castOid);
extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
! const char *newschema);
! extern Oid AlterFunctionNamespace_oid(Oid procOid, Oid nspOid);
extern void ExecuteDoStmt(DoStmt *stmt);
extern Oid get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok);
/* commands/operatorcmds.c */
! extern void DefineOperator(List *names, List *parameters);
extern void RemoveOperatorById(Oid operOid);
extern void AlterOperatorOwner(List *name, TypeName *typeName1,
! TypeName *typename2, Oid newOwnerId);
extern void AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId);
! extern void AlterOperatorNamespace(List *names, List *argtypes, const char *newschema);
extern Oid AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid);
/* commands/aggregatecmds.c */
extern void DefineAggregate(List *name, List *args, bool oldstyle,
! List *parameters);
! extern void RenameAggregate(List *name, List *args, const char *newname);
! extern void AlterAggregateOwner(List *name, List *args, Oid newOwnerId);
/* commands/opclasscmds.c */
extern void DefineOpClass(CreateOpClassStmt *stmt);
--- 66,99 ----
extern void RemoveFunctionById(Oid funcOid);
extern void SetFunctionReturnType(Oid funcOid, Oid newRetType);
extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType);
! extern void RenameFunction(List *name, List *argtypes, const char *newname, CommandContext cmd);
! extern void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId, CommandContext cmd);
! extern void AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId, CommandContext cmd);
extern void AlterFunction(AlterFunctionStmt *stmt);
extern void CreateCast(CreateCastStmt *stmt);
extern void DropCastById(Oid castOid);
extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
! const char *newschema, CommandContext cmd);
! extern Oid AlterFunctionNamespace_oid(Oid procOid, Oid nspOid,
! CommandContext cmd);
extern void ExecuteDoStmt(DoStmt *stmt);
extern Oid get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok);
/* commands/operatorcmds.c */
! extern void DefineOperator(List *names, List *parameters, CommandContext cmd);
extern void RemoveOperatorById(Oid operOid);
extern void AlterOperatorOwner(List *name, TypeName *typeName1,
! TypeName *typename2, Oid newOwnerId, CommandContext cmd);
extern void AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId);
! extern void AlterOperatorNamespace(List *names, List *argtypes,
! const char *newschema, CommandContext cmd);
extern Oid AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid);
/* commands/aggregatecmds.c */
extern void DefineAggregate(List *name, List *args, bool oldstyle,
! List *parameters, CommandContext cmd);
! extern void RenameAggregate(List *name, List *args, const char *newname, CommandContext cmd);
! extern void AlterAggregateOwner(List *name, List *args, Oid newOwnerId, CommandContext cmd);
/* commands/opclasscmds.c */
extern void DefineOpClass(CreateOpClassStmt *stmt);
***************
*** 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,20 ****
--- 15,21 ----
#define TABLECMDS_H
#include "access/htup.h"
+ #include "commands/cmdtrigger.h"
#include "nodes/parsenodes.h"
#include "storage/lock.h"
#include "utils/relcache.h"
***************
*** 48,57 **** extern void SetRelationHasSubclass(Oid relationId, bool relhassubclass);
extern void renameatt(RenameStmt *stmt);
! extern void RenameRelation(RenameStmt *stmt);
extern void RenameRelationInternal(Oid myrelid,
! const char *newrelname);
extern void find_composite_type_dependencies(Oid typeOid,
Relation origRelation,
--- 49,58 ----
extern void renameatt(RenameStmt *stmt);
! extern void RenameRelation(RenameStmt *stmt, CommandContext cmd);
extern void RenameRelationInternal(Oid myrelid,
! const char *newrelname, CommandContext cmd);
extern void find_composite_type_dependencies(Oid typeOid,
Relation origRelation,
*** a/src/include/commands/trigger.h
--- b/src/include/commands/trigger.h
***************
*** 13,18 ****
--- 13,19 ----
#ifndef TRIGGER_H
#define TRIGGER_H
+ #include "commands/cmdtrigger.h"
#include "nodes/execnodes.h"
#include "nodes/parsenodes.h"
***************
*** 115,121 **** extern Oid CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
extern void RemoveTriggerById(Oid trigOid);
extern Oid get_trigger_oid(Oid relid, const char *name, bool missing_ok);
! extern void renametrig(RenameStmt *stmt);
extern void EnableDisableTrigger(Relation rel, const char *tgname,
char fires_when, bool skip_system);
--- 116,122 ----
extern void RemoveTriggerById(Oid trigOid);
extern Oid get_trigger_oid(Oid relid, const char *name, bool missing_ok);
! extern void renametrig(RenameStmt *stmt, CommandContext cmd);
extern void EnableDisableTrigger(Relation rel, const char *tgname,
char fires_when, bool skip_system);
*** a/src/include/commands/typecmds.h
--- b/src/include/commands/typecmds.h
***************
*** 14,50 ****
#ifndef TYPECMDS_H
#define TYPECMDS_H
#include "nodes/parsenodes.h"
#define DEFAULT_TYPDELIM ','
! extern void DefineType(List *names, List *parameters);
extern void RemoveTypeById(Oid typeOid);
extern void DefineDomain(CreateDomainStmt *stmt);
extern void DefineEnum(CreateEnumStmt *stmt);
extern void DefineRange(CreateRangeStmt *stmt);
extern void AlterEnum(AlterEnumStmt *stmt);
! extern Oid DefineCompositeType(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);
*** /dev/null
--- b/src/test/regress/expected/cmdtriggers.out
***************
*** 0 ****
--- 1,246 ----
+ --
+ -- 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;
+ create command trigger snitch_create_table after create table execute procedure snitch();
+ create command trigger snitch_alter_table after alter table 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_after_alter_function after alter function 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 schema cmd;
+ NOTICE: snitch: BEFORE any CREATE SCHEMA
+ NOTICE: snitch: BEFORE CREATE SCHEMA cmd
+ NOTICE: snitch: AFTER any CREATE SCHEMA
+ 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 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 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 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
+ cluster cmd.foo using foo_pkey;
+ NOTICE: snitch: BEFORE any CLUSTER
+ vacuum cmd.foo;
+ NOTICE: snitch: BEFORE any VACUUM
+ vacuum;
+ NOTICE: snitch: BEFORE any VACUUM
+ set session_replication_role to replica;
+ create table cmd.bar();
+ reset session_replication_role;
+ create index idx_foo on cmd.foo(t);
+ NOTICE: snitch: BEFORE 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 cmd.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 cmd fun
+ NOTICE: snitch: AFTER any CREATE FUNCTION
+ alter function cmd.fun(int) strict;
+ NOTICE: snitch: BEFORE any ALTER FUNCTION
+ NOTICE: snitch: AFTER any ALTER FUNCTION
+ alter function cmd.fun(int) rename to notfun;
+ NOTICE: snitch: BEFORE any ALTER FUNCTION
+ NOTICE: snitch: BEFORE ALTER FUNCTION cmd fun
+ NOTICE: snitch: AFTER ALTER FUNCTION cmd notfun
+ NOTICE: snitch: AFTER any ALTER FUNCTION
+ alter function cmd.notfun(int) set schema public;
+ NOTICE: snitch: BEFORE any ALTER FUNCTION
+ NOTICE: snitch: BEFORE ALTER FUNCTION cmd notfun
+ NOTICE: snitch: AFTER ALTER FUNCTION public notfun
+ NOTICE: snitch: AFTER any ALTER FUNCTION
+ drop function public.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: 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
+ drop type cmd.compfoo;
+ 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
+ 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
+ NOTICE: snitch: BEFORE DROP TRIGGER cmd
+ NOTICE: snitch: AFTER any DROP TRIGGER
+ 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 function cmd.bigint_to_int4(bigint) returns integer language sql as 'select $1::int4';
+ NOTICE: snitch: BEFORE any CREATE FUNCTION
+ NOTICE: snitch: AFTER CREATE FUNCTION cmd bigint_to_int4
+ NOTICE: snitch: AFTER any CREATE FUNCTION
+ create cast (bigint as int4) with function cmd.bigint_to_int4(bigint);
+ NOTICE: snitch: BEFORE any CREATE CAST
+ ERROR: cast from type bigint to type integer already exists
+ drop cast (bigint as int4);
+ NOTICE: snitch: BEFORE any DROP CAST
+ ERROR: cannot drop cast from bigint to integer because it is required by the database system
+ 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 9 other objects
+ DETAIL: drop cascades to table cmd1.foo
+ drop cascades to view cmd1.v
+ drop cascades to table cmd1.bar
+ drop cascades to function cmd1.plus1(integer)
+ drop cascades to collation francais
+ drop cascades to type cmd1.bug_status
+ drop cascades to type cmd1.us_postal_code
+ drop cascades to function cmd1.trigfunc()
+ drop cascades to function cmd1.bigint_to_int4(bigint)
+ NOTICE: snitch: AFTER any DROP SCHEMA
+ drop command trigger snitch_before;
+ drop command trigger snitch_after;
+ drop command trigger snitch_create_table;
+ drop command trigger snitch_alter_table;
+ 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_after_alter_function;
+ 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;
*** 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,151 ----
+ --
+ -- 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;
+
+ create command trigger snitch_create_table after create table execute procedure snitch();
+ create command trigger snitch_alter_table after alter table 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_after_alter_function after alter function 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 schema cmd;
+ create table cmd.foo(id bigserial primary key);
+ create view cmd.v as select * from cmd.foo;
+ alter table cmd.foo add column t text;
+
+ cluster cmd.foo using foo_pkey;
+ vacuum cmd.foo;
+ vacuum;
+
+ set session_replication_role to replica;
+ create table cmd.bar();
+ reset session_replication_role;
+
+ create index idx_foo on cmd.foo(t);
+ drop index cmd.idx_foo;
+
+ create function cmd.fun(int) returns text language sql
+ as $$ select t from cmd.foo where id = $1; $$;
+
+ alter function cmd.fun(int) strict;
+ alter function cmd.fun(int) rename to notfun;
+ alter function cmd.notfun(int) set schema public;
+ drop function public.notfun(int);
+
+ create function cmd.plus1(int) returns bigint language sql
+ as $$ select $1::bigint + 1; $$;
+
+ create operator cmd.+!(procedure = cmd.plus1, leftarg = int);
+ alter operator cmd.+!(int, NONE) set schema public;
+ drop operator public.+!(int, NONE);
+
+ create aggregate cmd.avg (float8)
+ (
+ sfunc = float8_accum,
+ stype = float8[],
+ finalfunc = float8_avg,
+ initcond = '{0,0,0}'
+ );
+ alter aggregate cmd.avg(float8) set schema public;
+ drop aggregate public.avg(float8);
+
+ create collation cmd.french (LOCALE = 'fr_FR');
+ alter collation cmd.french rename to francais;
+
+ create type cmd.compfoo AS (f1 int, f2 text);
+ alter type cmd.compfoo add attribute f3 text;
+ drop type cmd.compfoo;
+
+ create type cmd.bug_status as enum ('new', 'open', 'closed');
+ alter type cmd.bug_status add value 'wontfix';
+
+ create domain cmd.us_postal_code as text check(value ~ '^\d{5}$' or value ~ '^\d{5}-\d{4}$');
+ alter domain cmd.us_postal_code set not null;
+
+ create function cmd.trigfunc() returns trigger language plpgsql as
+ $$ begin raise notice 'trigfunc'; end;$$;
+
+ create trigger footg before update on cmd.foo for each row execute procedure cmd.trigfunc();
+ alter trigger footg on cmd.foo rename to foo_trigger;
+ drop trigger foo_trigger on cmd.foo;
+
+ create text search configuration test (parser = "default");
+
+ create function cmd.bigint_to_int4(bigint) returns integer language sql as 'select $1::int4';
+ create cast (bigint as int4) with function cmd.bigint_to_int4(bigint);
+ drop cast (bigint as int4);
+
+ alter schema cmd rename to cmd1;
+
+ drop schema cmd1 cascade;
+
+ drop command trigger snitch_before;
+ drop command trigger snitch_after;
+
+ drop command trigger snitch_create_table;
+ drop command trigger snitch_alter_table;
+ 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_after_alter_function;
+
+ 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;
*** 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 ----