+ pg_cmdtrigger> Columns
+
+
+
+
+ Name
+ Type
+ References
+ Description
+
+
+
+
+
+ ctgname
+ name
+
+ Trigger's name (unique)
+
+
+
+ ctgcommand
+ name
+
+ Command TAG of the command this trigger fires for.
+
+
+
+ ctgfoid
+ oid
+ pg_proc.oid
+
+ The OID of the function that is called by this command trigger.
+
+
+
+
+ contype
+ char
+
+
+ B> = BEFORE
+ A> = AFTER
+
+
+
+
+ ctgenabled
+ char
+
+
+ Controls in which modes
+ the command trigger fires.
+ O> = trigger fires in origin> and local> modes,
+ D> = trigger is disabled,
+ R> = trigger fires in replica> mode,
+ A> = trigger fires always.
+
+
+
+
+
+
+
pg_constraint
*** a/doc/src/sgml/plperl.sgml
--- b/doc/src/sgml/plperl.sgml
***************
*** 634,640 **** SELECT init_hosts_query();
SELECT query_hosts('192.168.1.0/30');
SELECT release_hosts_query();
! query_hosts
-----------------
(1,192.168.1.1)
(2,192.168.1.2)
--- 634,640 ----
SELECT query_hosts('192.168.1.0/30');
SELECT release_hosts_query();
! query_hosts
-----------------
(1,192.168.1.1)
(2,192.168.1.2)
***************
*** 1026,1031 **** $$ LANGUAGE plperl;
--- 1026,1033 ----
PL/Perl Triggers
+
+
PL/Perl can be used to write trigger functions. In a trigger function,
the hash reference $_TD contains information about the
***************
*** 1209,1214 **** CREATE TRIGGER test_valid_id_trig
--- 1211,1284 ----
FOR EACH ROW EXECUTE PROCEDURE valid_id();
+
+
+
+
+ Command trigger procedures can be written in PL/Perl.
+ PostgreSQL requires that a procedure that is to be called
+ as a trigger must be declared as a function with no arguments
+ and a return type of command_trigger>.
+
+
+
+ The information from the trigger manager is passed to the procedure body
+ in the following variables:
+
+
+
+
+ $_TD->{when}
+
+
+ The timing of the event that fires the trigger, either the
+ string BEFORE or AFTER.
+
+
+
+
+
+ $_TD->{tag}
+
+
+ The command tag for which the trigger is fired.
+
+
+
+
+
+ $_TD->{objectid}
+
+
+ The object ID of the object that caused the trigger procedure
+ to be invoked.
+
+
+
+
+
+ $_TD->{objectname}
+
+
+ The name of the objectthat caused the trigger procedure
+ to be invoked.
+
+
+
+
+
+ $_TD->{schemaname}
+
+
+ The schema of the object that caused the trigger procedure to be
+ invoked, or NULL if the object is not qualified.
+
+
+
+
+
+
+
*** a/doc/src/sgml/plpgsql.sgml
--- b/doc/src/sgml/plpgsql.sgml
***************
*** 3377,3383 **** RAISE unique_violation USING MESSAGE = 'Duplicate user ID: ' || user_id;
in PL/pgSQL
! PL/pgSQL can be used to define trigger
procedures. A trigger procedure is created with the
CREATE FUNCTION> command, declaring it as a function with
--- 3377,3386 ----
in PL/pgSQL
!
! Triggers on data change
!
! PL/pgSQL can be used to define trigger
procedures. A trigger procedure is created with the
CREATE FUNCTION> command, declaring it as a function with
***************
*** 3924,3929 **** UPDATE sales_fact SET units_sold = units_sold * 2;
--- 3927,4035 ----
SELECT * FROM sales_summary_bytime;
+
+
+
+ Triggers on commands
+
+
+ PL/pgSQL can be used to define command
+ trigger procedures. A command trigger procedure is created with the
+ CREATE FUNCTION> command, declaring it as a function with
+ no arguments and a return type of command trigger.
+
+
+
+ When a PL/pgSQL function is called as a
+ command trigger, several special variables are created automatically
+ in the top-level block. They are:
+
+
+
+ TG_TAG
+
+
+ Data type text; variable that contains the command tag
+ for which the trigger is fired.
+
+
+
+
+
+ TG_WHEN
+
+
+ Data type text; a string of
+ BEFORE or AFTER depending on
+ the trigger's definition.
+
+
+
+
+
+ TG_OBJECTID
+
+
+ Data type oid; the object ID of the object that caused
+ the trigger invocation. NULL when the function is
+ called AFTER a drop command.
+
+
+
+
+
+ TG_SCHEMANAME
+
+
+ Data type name; the name of the schema of the object
+ that caused the trigger invocation. Can be NULL
+ for objects not located in a schema.
+
+
+
+
+
+ TG_OBJECTNAME
+
+
+ Data type name; the name of the object that caused the trigger
+ invocation. Can be NULL.
+
+
+
+
+
+
+
+ The command trigger function's return value is not used.
+
+
+
+ shows an example of a
+ command trigger procedure in PL/pgSQL.
+
+
+
+ A PL/pgSQL Command Trigger Procedure
+
+
+ This example trigger simply raises a NOTICE message
+ each time a supported command is executed.
+
+
+
+ CREATE OR REPLACE FUNCTION snitch() RETURNS command trigger AS $$
+ BEGIN
+ RAISE NOTICE 'snitch: % % %.% [%]',
+ tg_when, tg_tag, tg_schemaname, tg_objectname, tg_objectid;
+ END;
+ $$ LANGUAGE plpgsql;
+
+ CREATE COMMAND TRIGGER snitch_before BEFORE ANY COMMAND EXECUTE PROCEDURE snitch();
+ CREATE COMMAND TRIGGER snitch_after AFTER ANY COMMAND EXECUTE PROCEDURE snitch();
+
+
+
*** a/doc/src/sgml/plpython.sgml
--- b/doc/src/sgml/plpython.sgml
***************
*** 441,447 **** return (1, 2, 3, 4, 5)
$$ LANGUAGE plpythonu;
SELECT return_arr();
! return_arr
-------------
{1,2,3,4,5}
(1 row)
--- 441,447 ----
$$ LANGUAGE plpythonu;
SELECT return_arr();
! return_arr
-------------
{1,2,3,4,5}
(1 row)
***************
*** 756,761 **** $$ LANGUAGE plpythonu;
--- 756,763 ----
in PL/Python
+
+
When a function is used as a trigger, the dictionary
TD contains trigger-related values:
***************
*** 861,866 **** $$ LANGUAGE plpythonu;
--- 863,935 ----
"MODIFY"> to indicate you've modified the new row.
Otherwise the return value is ignored.
+
+
+
+
+ Command trigger procedures can be written in PL/Python.
+ PostgreSQL requires that a procedure that is to be called
+ as a trigger must be declared as a function with no arguments
+ and a return type of command_trigger>.
+
+
+
+ The information from the trigger manager is passed to the procedure body
+ in the following variables:
+
+
+
+
+ TD["when"]
+
+
+ The timing of the event that fires the trigger, either the
+ string BEFORE or AFTER.
+
+
+
+
+
+ TD["tag"]
+
+
+ The command tag for which the trigger is fired.
+
+
+
+
+
+ TD["objectid"]
+
+
+ The object ID of the object that caused the trigger procedure
+ to be invoked.
+
+
+
+
+
+ TD["objectname"]
+
+
+ The name of the objectthat caused the trigger procedure
+ to be invoked.
+
+
+
+
+
+ TD["schemaname"]
+
+
+ The schema of the object that caused the trigger procedure to be
+ invoked, or NULL if the object is not qualified.
+
+
+
+
+
+
*** a/doc/src/sgml/pltcl.sgml
--- b/doc/src/sgml/pltcl.sgml
***************
*** 521,526 **** SELECT 'doesn''t' AS ret
--- 521,528 ----
in PL/Tcl
+
+
Trigger procedures can be written in PL/Tcl.
PostgreSQL requires that a procedure that is to be called
***************
*** 709,714 **** CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab
--- 711,783 ----
name; that's supplied from the trigger arguments. This lets the
trigger procedure be reused with different tables.
+
+
+
+
+ Command trigger procedures can be written in PL/Tcl.
+ PostgreSQL requires that a procedure that is to be called
+ as a trigger must be declared as a function with no arguments
+ and a return type of command_trigger>.
+
+
+
+ The information from the trigger manager is passed to the procedure body
+ in the following variables:
+
+
+
+
+ $TG_when
+
+
+ The timing of the event that fires the trigger, either the
+ string BEFORE or AFTER.
+
+
+
+
+
+ $TG_tag
+
+
+ The command tag for which the trigger is fired.
+
+
+
+
+
+ $TG_objectid
+
+
+ The object ID of the object that caused the trigger procedure
+ to be invoked.
+
+
+
+
+
+ $TG_objectname
+
+
+ The name of the objectthat caused the trigger procedure
+ to be invoked.
+
+
+
+
+
+ $TG_schemaname
+
+
+ The schema of the object that caused the trigger procedure to be
+ invoked, or NULL if the object is not qualified.
+
+
+
+
+
+
*** a/doc/src/sgml/ref/allfiles.sgml
--- b/doc/src/sgml/ref/allfiles.sgml
***************
*** 8,13 **** Complete list of usable sgml source files in this directory.
--- 8,14 ----
+
***************
*** 50,55 **** Complete list of usable sgml source files in this directory.
--- 51,57 ----
+
***************
*** 88,93 **** Complete list of usable sgml source files in this directory.
--- 90,96 ----
+
*** /dev/null
--- b/doc/src/sgml/ref/alter_command_trigger.sgml
***************
*** 0 ****
--- 1,103 ----
+
+
+
+
+ ALTER COMMAND TRIGGER
+ 7
+ SQL - Language Statements
+
+
+
+ ALTER COMMAND TRIGGER
+ change the definition of a trigger
+
+
+
+ ALTER COMMAND TRIGGER
+
+
+
+
+ ALTER COMMAND TRIGGER name SET enabled
+ ALTER COMMAND TRIGGER name RENAME TO newname
+
+ where enabled can be one of:
+
+ ENABLE
+ ENABLE ALWAYS
+ ENABLE REPLICA
+ DISABLE
+
+ and where command can be one of the same list as in .
+
+
+
+
+
+ Description
+
+
+ ALTER COMMAND TRIGGER changes properties of an
+ existing command trigger.
+
+
+
+ You must be superuser to alter a command trigger.
+
+
+
+
+ Parameters
+
+
+
+ name
+
+
+ The name of an existing trigger to alter.
+
+
+
+
+
+ newname
+
+
+ The new name of the command trigger.
+
+
+
+
+
+ enabled
+
+
+ When to enable this trigger. See
+ the session_replication_role parameter.
+
+
+
+
+
+
+
+ Compatibility
+
+
+ ALTER COMMAND TRIGGER is a PostgreSQL>
+ extension of the SQL standard.
+
+
+
+
+ See Also
+
+
+
+
+
+
+
*** /dev/null
--- b/doc/src/sgml/ref/create_command_trigger.sgml
***************
*** 0 ****
--- 1,321 ----
+
+
+
+
+ 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 CONVERSION
+ ALTER DOMAIN
+ ALTER EXTENSION
+ ALTER FOREIGN DATA WRAPPER
+ ALTER FOREIGN TABLE
+ ALTER FUNCTION
+ ALTER LANGUAGE
+ ALTER OPERATOR
+ ALTER OPERATOR CLASS
+ ALTER OPERATOR FAMILY
+ ALTER SCHEMA
+ ALTER SEQUENCE
+ ALTER SERVER
+ ALTER TABLE
+ ALTER TEXT SEARCH CONFIGURATION
+ ALTER TEXT SEARCH DICTIONARY
+ ALTER TEXT SEARCH PARSER
+ ALTER TEXT SEARCH TEMPLATE
+ ALTER TRIGGER
+ ALTER TYPE
+ ALTER USER MAPPING
+ ALTER VIEW
+ CLUSTER
+ CREATE AGGREGATE
+ CREATE CAST
+ CREATE COLLATION
+ CREATE CONVERSION
+ CREATE DOMAIN
+ CREATE EXTENSION
+ CREATE FOREIGN DATA WRAPPER
+ CREATE FOREIGN TABLE
+ CREATE FUNCTION
+ CREATE INDEX
+ CREATE LANGUAGE
+ CREATE OPERATOR
+ CREATE OPERATOR CLASS
+ CREATE OPERATOR FAMILY
+ CREATE RULE
+ CREATE SCHEMA
+ CREATE SEQUENCE
+ CREATE SERVER
+ CREATE TABLE
+ CREATE 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 command
+ trigger. It can then only abort the execution of the command by
+ raising an exception.
+
+
+
+ If multiple triggers of the same kind are defined for the same event,
+ they will be fired in alphabetical order by name.
+
+
+
+ Note that objects dropped by the effect of DROP
+ CASCADE will not result in a command trigger firing, only the
+ top-level command for the main object will fire a command trigger. That
+ also applies to other dependencies following, as in DROP OWNED
+ BY.
+
+
+
+ Refer to for more information about triggers.
+
+
+
+
+ Parameters
+
+
+
+ name
+
+
+ The name to give the new trigger. This must be distinct from the name
+ of any other trigger for the same table. The name cannot be
+ schema-qualified.
+
+
+
+
+
+ BEFORE
+ AFTER
+
+
+ Determines whether the function is called before or after the command
+ is executed.
+
+
+
+
+
+ command
+
+
+ The tag of the command the trigger is for. Supported commands are
+ mainly those acting on database objects, plus some more facilities.
+ That leaves out the following list of non supported commands.
+
+
+ Commands that refer to global objects, such as databases, tablespaces
+ and roles, are not supported.
+
+
+ Commands that exercise their own transaction control are only
+ supported in BEFORE command triggers. This is the
+ case for VACUUM, CLUSTER,
+ CREATE INDEX CONCURRENTLY, and REINDEX
+ DATABASE.
+
+
+ The command ALTER TYPE ... ADD VALUE ... prevents
+ transaction control entirely, thus no command trigger will get fired
+ when it's used.
+
+
+ Commands that are related to transaction control (such
+ as BEGIN or COMMIT), related to
+ prepared plans
+ (e.g. PREPARE, DEALLOCATE),
+ cursors management
+ (e.g. DECLARE, FETCH), setting
+ variables (SET), the LISTEN
+ feature, and security are not supported either.
+
+
+ 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 no argument and
+ returning type command trigger.
+
+
+ If your command trigger is implemented in C then it
+ will be called with an argument, of
+ type internal, which is a pointer to
+ the Node * parse tree.
+
+
+
+
+
+
+
+
+ Notes
+
+
+ To create a trigger on a 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()
+ RETURNS command trigger LANGUAGE plpgsql AS $$
+ BEGIN
+ RAISE EXCEPTION 'command % is disabled', tg_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()
+ RETURNS command trigger LANGUAGE plpgsql AS $$
+ BEGIN
+ IF substring(tg_objectname, 0, 4) NOT IN ('ab_', 'cz_', 'fr_')
+ THEN
+ RAISE EXCEPTION 'invalid relation name: %', tg_objectname;
+ END IF;
+ END;
+ $$;
+
+ CREATE 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
***************
*** 26,31 ****
--- 26,32 ----
#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"
***************
*** 53,58 ****
--- 54,60 ----
#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"
***************
*** 158,164 **** static const Oid object_classes[MAX_OCLASS] = {
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
UserMappingRelationId, /* OCLASS_USER_MAPPING */
DefaultAclRelationId, /* OCLASS_DEFACL */
! ExtensionRelationId /* OCLASS_EXTENSION */
};
--- 160,167 ----
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
UserMappingRelationId, /* OCLASS_USER_MAPPING */
DefaultAclRelationId, /* OCLASS_DEFACL */
! ExtensionRelationId, /* OCLASS_EXTENSION */
! CmdTriggerRelationId /* OCLASS_CMDTRIGGER */
};
***************
*** 1096,1101 **** doDeletion(const ObjectAddress *object)
--- 1099,1108 ----
break;
}
+ case OCLASS_CMDTRIGGER:
+ RemoveCmdTriggerById(object->objectId);
+ break;
+
case OCLASS_PROC:
RemoveFunctionById(object->objectId);
break;
***************
*** 2248,2253 **** getObjectClass(const ObjectAddress *object)
--- 2255,2263 ----
case ExtensionRelationId:
return OCLASS_EXTENSION;
+
+ case CmdTriggerRelationId:
+ return OCLASS_CMDTRIGGER;
}
/* shouldn't get here */
***************
*** 2882,2887 **** getObjectDescription(const ObjectAddress *object)
--- 2892,2936 ----
break;
}
+ case OCLASS_CMDTRIGGER:
+ {
+ Relation trigDesc;
+ ScanKeyData skey[1];
+ SysScanDesc tgscan;
+ HeapTuple tup;
+ Form_pg_cmdtrigger trig;
+
+ trigDesc = heap_open(CmdTriggerRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ tgscan = systable_beginscan(trigDesc, CmdTriggerOidIndexId, true,
+ SnapshotNow, 1, skey);
+
+ tup = systable_getnext(tgscan);
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "could not find tuple for command trigger %u",
+ object->objectId);
+
+ trig = (Form_pg_cmdtrigger) GETSTRUCT(tup);
+
+ if (strcmp(NameStr(trig->ctgcommand), "ANY") == 0)
+ appendStringInfo(&buffer, _("trigger %s on any command"),
+ NameStr(trig->ctgname));
+ else
+ appendStringInfo(&buffer, _("trigger %s on command %s"),
+ NameStr(trig->ctgname),
+ NameStr(trig->ctgcommand));
+
+ systable_endscan(tgscan);
+ heap_close(trigDesc, AccessShareLock);
+ break;
+ }
+
default:
appendStringInfo(&buffer, "unrecognized object %u %u %d",
object->classId,
*** a/src/backend/catalog/index.c
--- b/src/backend/catalog/index.c
***************
*** 2789,2795 **** IndexGetRelation(Oid indexId, bool missing_ok)
* reindex_index - This routine is used to recreate a single index
*/
void
! reindex_index(Oid indexId, bool skip_constraint_checks)
{
Relation iRel,
heapRelation,
--- 2789,2795 ----
* reindex_index - This routine is used to recreate a single index
*/
void
! reindex_index(Oid indexId, bool skip_constraint_checks, CommandContext cmd)
{
Relation iRel,
heapRelation,
***************
*** 2828,2833 **** reindex_index(Oid indexId, bool skip_constraint_checks)
--- 2828,2843 ----
*/
CheckTableNotInUse(iRel, "REINDEX INDEX");
+ /* Call BEFORE REINDEX INDEX command triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = indexId;
+ cmd->objectname = RelationGetRelationName(iRel);
+ cmd->schemaname = get_namespace_name(RelationGetNamespace(iRel));
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/*
* All predicate locks on the index are about to be made invalid. Promote
* them to relation locks on the heap.
***************
*** 2923,2928 **** reindex_index(Oid indexId, bool skip_constraint_checks)
--- 2933,2942 ----
/* Close rels, but keep locks */
index_close(iRel, NoLock);
heap_close(heapRelation, NoLock);
+
+ /* Call AFTER REINDEX INDEX command triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
/*
***************
*** 2955,2961 **** reindex_index(Oid indexId, bool skip_constraint_checks)
* index rebuild.
*/
bool
! reindex_relation(Oid relid, int flags)
{
Relation rel;
Oid toast_relid;
--- 2969,2975 ----
* index rebuild.
*/
bool
! reindex_relation(Oid relid, int flags, CommandContext cmd)
{
Relation rel;
Oid toast_relid;
***************
*** 2972,2977 **** reindex_relation(Oid relid, int flags)
--- 2986,3002 ----
toast_relid = rel->rd_rel->reltoastrelid;
+ /* Call BEFORE REINDEX command triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = relid;
+ cmd->objectname = RelationGetRelationName(rel);
+ cmd->schemaname = get_namespace_name(RelationGetNamespace(rel));
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
+
/*
* Get the list of index OIDs for this relation. (We trust to the
* relcache to get this with a sequential scan if ignoring system
***************
*** 3033,3039 **** reindex_relation(Oid relid, int flags)
if (is_pg_class)
RelationSetIndexList(rel, doneIndexes, InvalidOid);
! reindex_index(indexOid, !(flags & REINDEX_REL_CHECK_CONSTRAINTS));
CommandCounterIncrement();
--- 3058,3064 ----
if (is_pg_class)
RelationSetIndexList(rel, doneIndexes, InvalidOid);
! reindex_index(indexOid, !(flags & REINDEX_REL_CHECK_CONSTRAINTS), NULL);
CommandCounterIncrement();
***************
*** 3068,3074 **** reindex_relation(Oid relid, int flags)
* still hold the lock on the master table.
*/
if ((flags & REINDEX_REL_PROCESS_TOAST) && OidIsValid(toast_relid))
! result |= reindex_relation(toast_relid, flags);
return result;
}
--- 3093,3103 ----
* still hold the lock on the master table.
*/
if ((flags & REINDEX_REL_PROCESS_TOAST) && OidIsValid(toast_relid))
! result |= reindex_relation(toast_relid, flags, NULL);
!
! /* Call AFTER REINDEX command triggers */
! if (CommandFiresAfterTriggers(cmd))
! ExecAfterCommandTriggers(cmd);
return result;
}
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
***************
*** 21,26 ****
--- 21,27 ----
#include "catalog/objectaddress.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
+ #include "catalog/pg_cmdtrigger.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
***************
*** 44,49 ****
--- 45,51 ----
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
+ #include "commands/cmdtrigger.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/extension.h"
***************
*** 204,209 **** static ObjectPropertyType ObjectProperty[] =
--- 206,217 ----
InvalidAttrNumber
},
{
+ CmdTriggerRelationId,
+ CmdTriggerOidIndexId,
+ -1,
+ InvalidAttrNumber
+ },
+ {
TSConfigRelationId,
TSConfigOidIndexId,
TSCONFIGOID,
***************
*** 249,254 **** static ObjectAddress get_object_address_type(ObjectType objtype,
--- 257,264 ----
List *objname, bool missing_ok);
static ObjectAddress get_object_address_opcf(ObjectType objtype, List *objname,
List *objargs, bool missing_ok);
+ static ObjectAddress get_object_address_cmdtrigger(ObjectType objtype,
+ List *objname, bool missing_ok);
static ObjectPropertyType *get_object_property_data(Oid class_id);
/*
***************
*** 292,298 **** get_object_address(ObjectType objtype, List *objname, List *objargs,
*/
inval_count = SharedInvalidMessageCounter;
! /* Look up object address. */
switch (objtype)
{
case OBJECT_INDEX:
--- 302,308 ----
*/
inval_count = SharedInvalidMessageCounter;
! /* Look up object address. */
switch (objtype)
{
case OBJECT_INDEX:
***************
*** 317,322 **** get_object_address(ObjectType objtype, List *objname, List *objargs,
--- 327,336 ----
address = get_object_address_relobject(objtype, objname,
&relation, missing_ok);
break;
+ case OBJECT_CMDTRIGGER:
+ address = get_object_address_cmdtrigger(objtype, objname,
+ missing_ok);
+ break;
case OBJECT_DATABASE:
case OBJECT_EXTENSION:
case OBJECT_TABLESPACE:
***************
*** 902,907 **** get_object_address_opcf(ObjectType objtype,
--- 916,941 ----
}
/*
+ * Find the ObjectAddress for a command trigger.
+ */
+ static ObjectAddress
+ get_object_address_cmdtrigger(ObjectType objtype, List *objname, bool missing_ok)
+ {
+ char *name;
+ ObjectAddress address;
+
+ Assert(list_length(objname) == 1); /* command triggers are not schema qualified */
+
+ name = strVal(linitial(objname));
+
+ address.classId = CmdTriggerRelationId;
+ address.objectId = get_cmdtrigger_oid(name, missing_ok);
+ address.objectSubId = 0;
+
+ return address;
+ }
+
+ /*
* Check ownership of an object previously identified by get_object_address.
*/
void
***************
*** 1054,1059 **** check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
--- 1088,1094 ----
break;
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
+ case OBJECT_CMDTRIGGER:
/* We treat these object types as being owned by superusers */
if (!superuser_arg(roleid))
ereport(ERROR,
*** a/src/backend/catalog/pg_aggregate.c
--- b/src/backend/catalog/pg_aggregate.c
***************
*** 23,28 ****
--- 23,29 ----
#include "catalog/pg_proc.h"
#include "catalog/pg_proc_fn.h"
#include "catalog/pg_type.h"
+ #include "commands/cmdtrigger.h"
#include "miscadmin.h"
#include "parser/parse_coerce.h"
#include "parser/parse_func.h"
***************
*** 50,56 **** AggregateCreate(const char *aggName,
List *aggfinalfnName,
List *aggsortopName,
Oid aggTransType,
! const char *agginitval)
{
Relation aggdesc;
HeapTuple tup;
--- 51,58 ----
List *aggfinalfnName,
List *aggsortopName,
Oid aggTransType,
! const char *agginitval,
! CommandContext cmd)
{
Relation aggdesc;
HeapTuple tup;
***************
*** 222,227 **** AggregateCreate(const char *aggName,
--- 224,240 ----
aclcheck_error(aclresult, ACL_KIND_TYPE,
format_type_be(finaltype));
+ /*
+ * Call BEFORE CREATE AGGREGATE triggers
+ */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = InvalidOid;
+ cmd->objectname = (char *)aggName;
+ cmd->schemaname = get_namespace_name(aggNamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
/*
* Everything looks okay. Try to create the pg_proc entry for the
***************
*** 317,322 **** AggregateCreate(const char *aggName,
--- 330,342 ----
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+
+ /* Call AFTER CREATE AGGREGATE triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectId = procOid;
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
*** a/src/backend/catalog/pg_collation.c
--- b/src/backend/catalog/pg_collation.c
***************
*** 23,32 ****
--- 23,34 ----
#include "catalog/pg_collation.h"
#include "catalog/pg_collation_fn.h"
#include "catalog/pg_namespace.h"
+ #include "commands/cmdtrigger.h"
#include "mb/pg_wchar.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/rel.h"
+ #include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
***************
*** 40,46 **** Oid
CollationCreate(const char *collname, Oid collnamespace,
Oid collowner,
int32 collencoding,
! const char *collcollate, const char *collctype)
{
Relation rel;
TupleDesc tupDesc;
--- 42,49 ----
CollationCreate(const char *collname, Oid collnamespace,
Oid collowner,
int32 collencoding,
! const char *collcollate, const char *collctype,
! CommandContext cmd)
{
Relation rel;
TupleDesc tupDesc;
***************
*** 90,95 **** CollationCreate(const char *collname, Oid collnamespace,
--- 93,110 ----
errmsg("collation \"%s\" already exists",
collname)));
+ /*
+ * Call BEFORE CREATE COLLATION triggers
+ */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = InvalidOid;
+ cmd->objectname = (char *)collname;
+ cmd->schemaname = get_namespace_name(collnamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* open pg_collation */
rel = heap_open(CollationRelationId, RowExclusiveLock);
tupDesc = RelationGetDescr(rel);
***************
*** 141,146 **** CollationCreate(const char *collname, Oid collnamespace,
--- 156,167 ----
heap_freetuple(tup);
heap_close(rel, RowExclusiveLock);
+ /* Call AFTER CREATE COLLATION triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectId = oid;
+ ExecAfterCommandTriggers(cmd);
+ }
return oid;
}
*** a/src/backend/catalog/pg_operator.c
--- b/src/backend/catalog/pg_operator.c
***************
*** 336,342 **** OperatorCreate(const char *operatorName,
Oid restrictionId,
Oid joinId,
bool canMerge,
! bool canHash)
{
Relation pg_operator_desc;
HeapTuple tup;
--- 336,343 ----
Oid restrictionId,
Oid joinId,
bool canMerge,
! bool canHash,
! CommandContext cmd)
{
Relation pg_operator_desc;
HeapTuple tup;
***************
*** 433,438 **** OperatorCreate(const char *operatorName,
--- 434,451 ----
operatorName);
/*
+ * Call BEFORE CREATE AGGREGARE triggers
+ */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = InvalidOid;
+ cmd->objectname = (char *)operatorName;
+ cmd->schemaname = get_namespace_name(operatorNamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
+ /*
* Set up the other operators. If they do not currently exist, create
* shells in order to get ObjectId's.
*/
***************
*** 564,569 **** OperatorCreate(const char *operatorName,
--- 577,589 ----
if (OidIsValid(commutatorId) || OidIsValid(negatorId))
OperatorUpd(operatorObjectId, commutatorId, negatorId);
+
+ /* Call AFTER CREATE OPERATOR triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectId = operatorObjectId;
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
*** a/src/backend/catalog/pg_shdepend.c
--- b/src/backend/catalog/pg_shdepend.c
***************
*** 1327,1337 **** shdepReassignOwned(List *roleids, Oid newrole)
switch (sdepForm->classid)
{
case CollationRelationId:
! AlterCollationOwner_oid(sdepForm->objid, newrole);
break;
case ConversionRelationId:
! AlterConversionOwner_oid(sdepForm->objid, newrole);
break;
case TypeRelationId:
--- 1327,1337 ----
switch (sdepForm->classid)
{
case CollationRelationId:
! AlterCollationOwner_oid(sdepForm->objid, newrole, NULL);
break;
case ConversionRelationId:
! AlterConversionOwner_oid(sdepForm->objid, newrole, NULL);
break;
case TypeRelationId:
***************
*** 1357,1363 **** shdepReassignOwned(List *roleids, Oid newrole)
break;
case ProcedureRelationId:
! AlterFunctionOwner_oid(sdepForm->objid, newrole);
break;
case LanguageRelationId:
--- 1357,1363 ----
break;
case ProcedureRelationId:
! AlterFunctionOwner_oid(sdepForm->objid, newrole, NULL);
break;
case LanguageRelationId:
*** a/src/backend/catalog/pg_type.c
--- b/src/backend/catalog/pg_type.c
***************
*** 678,684 **** GenerateTypeDependencies(Oid typeNamespace,
* ALTER TYPE RENAME TO command.
*/
void
! RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
{
Relation pg_type_desc;
HeapTuple tuple;
--- 678,685 ----
* ALTER TYPE RENAME TO command.
*/
void
! RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace,
! CommandContext cmd)
{
Relation pg_type_desc;
HeapTuple tuple;
***************
*** 705,710 **** RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
--- 706,721 ----
(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);
***************
*** 721,729 **** RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
{
char *arrname = makeArrayTypeName(newTypeName, typeNamespace);
! RenameTypeInternal(arrayOid, arrname, typeNamespace);
pfree(arrname);
}
}
--- 732,747 ----
{
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);
+ }
}
***************
*** 824,830 **** 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
--- 842,848 ----
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,68 ****
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_CONSTRAINT:
! RenameConstraint(stmt);
break;
case OBJECT_CONVERSION:
! RenameConversion(stmt->object, stmt->newname);
break;
case OBJECT_DATABASE:
--- 48,76 ----
void
ExecRenameStmt(RenameStmt *stmt)
{
+ CommandContextData cmd;
+ InitCommandContext(&cmd, (Node *)stmt);
+
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_CONSTRAINT:
! RenameConstraint(stmt, &cmd);
break;
case OBJECT_CONVERSION:
! RenameConversion(stmt->object, stmt->newname, &cmd);
! break;
!
! case OBJECT_CMDTRIGGER:
! RenameCmdTrigger(stmt->object, stmt->newname);
break;
case OBJECT_DATABASE:
***************
*** 70,96 **** 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:
--- 78,104 ----
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:
***************
*** 98,104 **** ExecRenameStmt(RenameStmt *stmt)
break;
case OBJECT_SCHEMA:
! RenameSchema(stmt->subname, stmt->newname);
break;
case OBJECT_TABLESPACE:
--- 106,112 ----
break;
case OBJECT_SCHEMA:
! RenameSchema(stmt->subname, stmt->newname, &cmd);
break;
case OBJECT_TABLESPACE:
***************
*** 110,146 **** ExecRenameStmt(RenameStmt *stmt)
case OBJECT_VIEW:
case OBJECT_INDEX:
case OBJECT_FOREIGN_TABLE:
! RenameRelation(stmt);
break;
case OBJECT_COLUMN:
case OBJECT_ATTRIBUTE:
! renameatt(stmt);
break;
case OBJECT_TRIGGER:
! renametrig(stmt);
break;
case OBJECT_TSPARSER:
! RenameTSParser(stmt->object, stmt->newname);
break;
case OBJECT_TSDICTIONARY:
! RenameTSDictionary(stmt->object, stmt->newname);
break;
case OBJECT_TSTEMPLATE:
! RenameTSTemplate(stmt->object, stmt->newname);
break;
case OBJECT_TSCONFIGURATION:
! RenameTSConfiguration(stmt->object, stmt->newname);
break;
case OBJECT_DOMAIN:
case OBJECT_TYPE:
! RenameType(stmt);
break;
default:
--- 118,154 ----
case OBJECT_VIEW:
case OBJECT_INDEX:
case OBJECT_FOREIGN_TABLE:
! RenameRelation(stmt, &cmd);
break;
case OBJECT_COLUMN:
case OBJECT_ATTRIBUTE:
! renameatt(stmt, &cmd);
break;
case OBJECT_TRIGGER:
! renametrig(stmt, &cmd);
break;
case OBJECT_TSPARSER:
! RenameTSParser(stmt->object, stmt->newname, &cmd);
break;
case OBJECT_TSDICTIONARY:
! RenameTSDictionary(stmt->object, stmt->newname, &cmd);
break;
case OBJECT_TSTEMPLATE:
! RenameTSTemplate(stmt->object, stmt->newname, &cmd);
break;
case OBJECT_TSCONFIGURATION:
! RenameTSConfiguration(stmt->object, stmt->newname, &cmd);
break;
case OBJECT_DOMAIN:
case OBJECT_TYPE:
! RenameType(stmt, &cmd);
break;
default:
***************
*** 156,195 **** 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:
--- 164,206 ----
void
ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
{
+ CommandContextData cmd;
+ InitCommandContext(&cmd, (Node *)stmt);
+
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:
***************
*** 200,223 **** 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:
--- 211,234 ----
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:
***************
*** 239,244 **** ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
--- 250,258 ----
*
* 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)
***************
*** 275,289 **** 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:
--- 289,303 ----
}
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:
***************
*** 353,359 **** 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;
--- 367,373 ----
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;
***************
*** 423,428 **** AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId,
--- 437,452 ----
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));
***************
*** 445,450 **** AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId,
--- 469,480 ----
changeDependencyFor(classId, objid,
NamespaceRelationId, oldNspOid, nspOid);
+ /* Call AFTER ALTER OBJECT triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->schemaname = get_namespace_name(nspOid);
+ ExecAfterCommandTriggers(cmd);
+ }
return oldNspOid;
}
***************
*** 457,475 **** 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:
--- 487,508 ----
ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
{
Oid newowner = get_role_oid(stmt->newowner, false);
+ CommandContextData cmd;
+
+ InitCommandContext(&cmd, (Node *)stmt);
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:
***************
*** 477,487 **** 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:
--- 510,520 ----
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:
***************
*** 493,511 **** 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:
--- 526,544 ----
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:
***************
*** 514,537 **** 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:
--- 547,570 ----
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);
+
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,732 ----
+ /*-------------------------------------------------------------------------
+ *
+ * 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.
+ */
+ Oid
+ CreateCmdTrigger(CreateCmdTrigStmt *stmt, const char *queryString)
+ {
+ Relation tgrel;
+ 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, 0, NULL, true);
+
+ /* we need the trigger type to validate the return type */
+ funcrettype = get_func_rettype(funcoid);
+
+ /*
+ * Generate the trigger's OID now, so that we can use it in the name if
+ * needed.
+ */
+ tgrel = heap_open(CmdTriggerRelationId, RowExclusiveLock);
+
+ /*
+ * Scan pg_cmdtrigger for existing triggers on command. We do this only
+ * to give a nice error message if there's already a trigger of the
+ * same name. (The unique index on ctgcommand/ctgname would complain
+ * anyway.)
+ *
+ * NOTE that this is cool only because we have AccessExclusiveLock on
+ * the relation, so the trigger set won't be changing underneath us.
+ */
+ check_cmdtrigger_name(stmt->trigname, tgrel);
+
+ /*
+ * Add some restrictions. We don't allow for AFTER command triggers on
+ * commands that do their own transaction management, such as VACUUM and
+ * CREATE INDEX CONCURRENTLY, because RAISE EXCEPTION at this point is
+ * meaningless, the work as already been commited.
+ *
+ * CREATE INDEX CONCURRENTLY has no specific command tag and can not be
+ * captured here, so we just document that not AFTER command trigger
+ * will get run.
+ */
+ if (stmt->timing == CMD_TRIGGER_FIRED_AFTER
+ && (strcmp(stmt->command, "VACUUM") == 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("AFTER VACUUM command triggers are not implemented")));
+
+ if (stmt->timing == CMD_TRIGGER_FIRED_AFTER
+ && (strcmp(stmt->command, "CLUSTER") == 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("AFTER CLUSTER command triggers are not implemented")));
+
+ if (stmt->timing == CMD_TRIGGER_FIRED_AFTER
+ && (strcmp(stmt->command, "CREATE INDEX") == 0))
+ ereport(WARNING,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("AFTER CREATE INDEX CONCURRENTLY triggers are not supported"),
+ errdetail("The command trigger will not fire on concurrently-created indexes.")));
+
+ if (strcmp(stmt->command, "REINDEX") == 0)
+ ereport(WARNING,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("REINDEX DATABASE triggers are not supported"),
+ errdetail("The command trigger will not fire on REINDEX DATABASE.")));
+
+ if (funcrettype != CMDTRIGGEROID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("function \"%s\" must return type \"command_trigger\"",
+ NameListToString(stmt->funcname))));
+
+ trigoid = InsertCmdTriggerTuple(tgrel, stmt->command, stmt->trigname,
+ funcoid, stmt->timing);
+
+ heap_close(tgrel, RowExclusiveLock);
+
+ return trigoid;
+ }
+
+ /*
+ * Guts of command trigger deletion.
+ */
+ void
+ RemoveCmdTriggerById(Oid trigOid)
+ {
+ Relation tgrel;
+ SysScanDesc tgscan;
+ ScanKeyData skey[1];
+ HeapTuple tup;
+
+ tgrel = heap_open(CmdTriggerRelationId, RowExclusiveLock);
+
+ /*
+ * Find the trigger to delete.
+ */
+ ScanKeyInit(&skey[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(trigOid));
+
+ tgscan = systable_beginscan(tgrel, CmdTriggerOidIndexId, true,
+ SnapshotNow, 1, skey);
+
+ tup = systable_getnext(tgscan);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "could not find tuple for command trigger %u", trigOid);
+
+ /*
+ * Delete the pg_cmdtrigger tuple.
+ */
+ simple_heap_delete(tgrel, &tup->t_self);
+
+ systable_endscan(tgscan);
+ heap_close(tgrel, RowExclusiveLock);
+ }
+
+ /*
+ * ALTER TRIGGER foo ON COMMAND ... ENABLE|DISABLE|ENABLE ALWAYS|REPLICA
+ */
+ void
+ AlterCmdTrigger(AlterCmdTrigStmt *stmt)
+ {
+ Relation tgrel;
+ SysScanDesc tgscan;
+ ScanKeyData skey[1];
+ HeapTuple tup;
+ Form_pg_cmdtrigger cmdForm;
+ char tgenabled = pstrdup(stmt->tgenabled)[0]; /* works with gram.y */
+
+ CheckCmdTriggerPrivileges();
+
+ tgrel = heap_open(CmdTriggerRelationId, RowExclusiveLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_cmdtrigger_ctgname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(stmt->trigname));
+
+ tgscan = systable_beginscan(tgrel, CmdTriggerNameIndexId, true,
+ SnapshotNow, 1, skey);
+
+ tup = systable_getnext(tgscan);
+
+ if (!HeapTupleIsValid(tup))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("trigger \"%s\" does not exist, skipping",
+ stmt->trigname)));
+
+ /* Copy tuple so we can modify it below */
+ tup = heap_copytuple(tup);
+ cmdForm = (Form_pg_cmdtrigger) GETSTRUCT(tup);
+
+ systable_endscan(tgscan);
+
+ cmdForm->ctgenabled = tgenabled;
+
+ simple_heap_update(tgrel, &tup->t_self, tup);
+ CatalogUpdateIndexes(tgrel, tup);
+
+ heap_close(tgrel, RowExclusiveLock);
+ heap_freetuple(tup);
+ }
+
+
+ /*
+ * Rename command trigger
+ */
+ void
+ RenameCmdTrigger(List *name, const char *newname)
+ {
+ SysScanDesc tgscan;
+ ScanKeyData skey[1];
+ HeapTuple tup;
+ Relation rel;
+ Form_pg_cmdtrigger cmdForm;
+ char *trigname;
+
+ Assert(list_length(name) == 1);
+ trigname = strVal((Value *)linitial(name));
+
+ CheckCmdTriggerPrivileges();
+
+ rel = heap_open(CmdTriggerRelationId, RowExclusiveLock);
+
+ /* newname must be available */
+ check_cmdtrigger_name(newname, rel);
+
+ /* get existing tuple */
+ ScanKeyInit(&skey[0],
+ Anum_pg_cmdtrigger_ctgname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(trigname));
+
+ tgscan = systable_beginscan(rel, CmdTriggerNameIndexId, true,
+ SnapshotNow, 1, skey);
+
+ tup = systable_getnext(tgscan);
+
+ if (!HeapTupleIsValid(tup))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("command trigger \"%s\" does not exist, skipping",
+ trigname)));
+
+ /* Copy tuple so we can modify it below */
+ tup = heap_copytuple(tup);
+ cmdForm = (Form_pg_cmdtrigger) GETSTRUCT(tup);
+
+ systable_endscan(tgscan);
+
+ /* rename */
+ namestrcpy(&(cmdForm->ctgname), newname);
+ simple_heap_update(rel, &tup->t_self, tup);
+ CatalogUpdateIndexes(rel, tup);
+
+ heap_freetuple(tup);
+ heap_close(rel, NoLock);
+ }
+
+ /*
+ * get_cmdtrigger_oid - Look up a trigger by name to find its OID.
+ *
+ * If missing_ok is false, throw an error if trigger not found. If
+ * true, just return InvalidOid.
+ */
+ Oid
+ get_cmdtrigger_oid(const char *trigname, bool missing_ok)
+ {
+ Relation tgrel;
+ ScanKeyData skey[1];
+ SysScanDesc tgscan;
+ HeapTuple tup;
+ Oid oid;
+
+ /*
+ * Find the trigger, verify permissions, set up object address
+ */
+ tgrel = heap_open(CmdTriggerRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_cmdtrigger_ctgname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(trigname));
+
+ tgscan = systable_beginscan(tgrel, CmdTriggerNameIndexId, true,
+ SnapshotNow, 1, skey);
+
+ tup = systable_getnext(tgscan);
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("command trigger \"%s\" does not exist, skipping",
+ trigname)));
+ oid = InvalidOid;
+ }
+ else
+ {
+ oid = HeapTupleGetOid(tup);
+ }
+
+ systable_endscan(tgscan);
+ heap_close(tgrel, AccessShareLock);
+ return oid;
+ }
+
+ /*
+ * Scan pg_cmdtrigger for existing triggers on command. We do this only to
+ * give a nice error message if there's already a trigger of the same name.
+ */
+ void
+ check_cmdtrigger_name(const char *trigname, Relation tgrel)
+ {
+ SysScanDesc tgscan;
+ ScanKeyData skey[1];
+ HeapTuple tuple;
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_cmdtrigger_ctgname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(trigname));
+
+ tgscan = systable_beginscan(tgrel, CmdTriggerNameIndexId, true,
+ SnapshotNow, 1, skey);
+
+ tuple = systable_getnext(tgscan);
+
+ if (HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("command trigger \"%s\" already exists", trigname)));
+ systable_endscan(tgscan);
+ }
+
+ /*
+ * Functions to execute the command triggers.
+ *
+ * We call the functions that matches the command triggers definitions in
+ * alphabetical order, and give them those arguments:
+ *
+ * command tag, text
+ * objectId, oid
+ * schemaname, text
+ * objectname, text
+ *
+ */
+
+ /*
+ * Scan the catalogs and fill in the CommandContext procedures that we will
+ * have to call before and after the command.
+ */
+ static bool
+ ListCommandTriggers(CommandContext cmd, bool list_any_triggers)
+ {
+ int count = 0;
+ Relation rel, irel;
+ SysScanDesc scandesc;
+ HeapTuple tuple;
+ ScanKeyData entry[1];
+ char *tag;
+
+ if (list_any_triggers)
+ {
+ tag = "ANY";
+ cmd->before_any = cmd->after_any = NIL;
+ }
+ else
+ {
+ tag = cmd->tag;
+ 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(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:
+ {
+ if (list_any_triggers)
+ cmd->before_any = lappend_oid(cmd->before_any, form->ctgfoid);
+ else
+ cmd->before = lappend_oid(cmd->before, form->ctgfoid);
+ break;
+ }
+ case CMD_TRIGGER_FIRED_AFTER:
+ {
+ if (list_any_triggers)
+ cmd->after_any = lappend_oid(cmd->after_any, form->ctgfoid);
+ else
+ 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 void
+ call_cmdtrigger_procedure(CommandContext cmd, RegProcedure proc, const char *when)
+ {
+ FmgrInfo flinfo;
+ FunctionCallInfoData fcinfo;
+ PgStat_FunctionCallUsage fcusage;
+ CommandTriggerData trigdata;
+
+ fmgr_info(proc, &flinfo);
+
+ /*
+ * Prepare the command trigger function context from the Command Context.
+ * We prepare a dedicated Node here so as not to publish internal data.
+ */
+ trigdata.type = T_CommandTriggerData;
+ trigdata.when = (char *)when;
+ trigdata.tag = cmd->tag;
+ trigdata.objectId = cmd->objectId;
+ trigdata.schemaname = cmd->schemaname;
+ trigdata.objectname = cmd->objectname;
+ trigdata.parsetree = cmd->parsetree;
+
+ /*
+ * Call the function, passing no arguments but setting a context.
+ */
+ InitFunctionCallInfoData(fcinfo, &flinfo, 0, InvalidOid,
+ (Node *) &trigdata, NULL);
+
+ pgstat_init_function_usage(&fcinfo, &fcusage);
+ FunctionCallInvoke(&fcinfo);
+ pgstat_end_function_usage(&fcusage, true);
+
+ return;
+ }
+
+ /*
+ * Execute the procedures attached to the command. We pass the list of
+ * procedures to use (either cmd->before or cmd->after) explicitely.
+ *
+ * The when argument allows to fill the trigger special variables.
+ */
+ static void
+ exec_command_triggers_internal(CommandContext cmd, char when)
+ {
+ List *procs[2];
+ char *whenstr;
+ ListCell *cell;
+ int i;
+
+ switch (when)
+ {
+ case CMD_TRIGGER_FIRED_BEFORE:
+ whenstr = "BEFORE";
+ procs[0] = cmd->before_any;
+ procs[1] = cmd->before;
+ break;
+
+ case CMD_TRIGGER_FIRED_AFTER:
+ whenstr = "AFTER";
+ procs[0] = cmd->after;
+ procs[1] = cmd->after_any;
+ break;
+
+ default:
+ elog(ERROR, "unrecognized command trigger condition: %c", when);
+ break;
+ }
+
+ for(i=0; i<2; i++)
+ {
+ foreach(cell, procs[i])
+ {
+ Oid proc = lfirst_oid(cell);
+
+ call_cmdtrigger_procedure(cmd, (RegProcedure)proc, whenstr);
+ }
+ }
+ }
+
+ /*
+ * Routine to call to setup a CommandContextData structure.
+ *
+ * This ensures that cmd->before and cmd->after are set to meaningful values.
+ */
+ void
+ InitCommandContext(CommandContext cmd, const Node *stmt)
+ {
+ 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->before_any = NIL;
+ cmd->after_any = NIL;
+ cmd->oldmctx = NULL;
+ cmd->cmdmctx = NULL;
+
+ /* Specifically drop support for command triggers on command triggers */
+ switch (nodeTag(stmt))
+ {
+ case T_RenameStmt:
+ if (((RenameStmt *) stmt)->renameType == OBJECT_CMDTRIGGER)
+ return;
+
+ case T_DropStmt:
+ if (((DropStmt *) stmt)->removeType == OBJECT_CMDTRIGGER)
+ return;
+
+ case T_IndexStmt:
+ if (((IndexStmt *)stmt)->concurrent)
+ return;
+
+ default:
+ ListCommandTriggers(cmd, true); /* list ANY command triggers */
+ ListCommandTriggers(cmd, false); /* and triggers for this command tag */
+ }
+ }
+
+ /*
+ * 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)
+ return false;
+
+ if (cmd->before != NIL || cmd->before_any != NIL
+ || cmd->after != NIL || cmd->after_any != 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)
+ return false;
+
+ if (cmd->before != NIL || cmd->before_any != NIL
+ || cmd->after != NIL || cmd->after_any != 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)
+ {
+ if (cmd == NULL)
+ return;
+
+ /* that will execute under command trigger memory context */
+ exec_command_triggers_internal(cmd, CMD_TRIGGER_FIRED_BEFORE);
+
+ /* switch back to the command Memory Context now */
+ MemoryContextSwitchTo(cmd->oldmctx);
+ }
+
+ void
+ ExecAfterCommandTriggers(CommandContext cmd)
+ {
+ if (cmd == NULL)
+ return;
+
+ /* that will execute under command trigger memory context */
+ exec_command_triggers_internal(cmd, CMD_TRIGGER_FIRED_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);
+
+ 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"
***************
*** 733,740 **** 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;
--- 734,743 ----
* 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);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = address.objectId;
+ get_object_name(&cmd, stmt->removeType, address.objectId, objname);
+ cmd.schemaname = get_namespace_name(namespaceId);
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+ cmds[i++] = &cmd;
+
add_exact_object_address(&address, objects);
}
/* Here we really delete them. */
performMultipleDeletions(objects, stmt->behavior, 0);
+ /* Call AFTER DROP command triggers */
+ for(i = 0; iobjectId = InvalidOid;
+ ExecAfterCommandTriggers(cmds[i]);
+ }
+ }
free_object_addresses(objects);
}
***************
*** 206,211 **** does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs)
--- 241,251 ----
args = NameListToString(list_truncate(objname,
list_length(objname) - 1));
break;
+ case OBJECT_CMDTRIGGER:
+ msg = gettext_noop("trigger \"%s\" for command \"%s\" does not exist, skipping");
+ name = NameListToString(objname);
+ args = strVal(linitial(objargs));
+ break;
case OBJECT_RULE:
msg = gettext_noop("rule \"%s\" for relation \"%s\" does not exist, skipping");
name = strVal(llast(objname));
***************
*** 240,242 **** does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs)
--- 280,335 ----
else
ereport(NOTICE, (errmsg(msg, name, args)));
}
+
+ /*
+ * Fill in the CommandContext name, with the non-qualified part fo the name.
+ */
+ static void
+ get_object_name(CommandContext cmd, ObjectType objtype,
+ Oid objectId, List *objname)
+ {
+ switch (objtype)
+ {
+ case OBJECT_TYPE:
+ case OBJECT_DOMAIN:
+ cmd->objectname = format_type_be_without_namespace(objectId);
+ break;
+ case OBJECT_CAST:
+ cmd->objectname = NULL;
+ break;
+ case OBJECT_RULE:
+ case OBJECT_TRIGGER:
+ cmd->objectname = pstrdup(strVal(llast(objname)));
+ break;
+ case OBJECT_COLLATION:
+ case OBJECT_CONVERSION:
+ case OBJECT_SCHEMA:
+ case OBJECT_TSPARSER:
+ case OBJECT_TSDICTIONARY:
+ case OBJECT_TSTEMPLATE:
+ case OBJECT_TSCONFIGURATION:
+ case OBJECT_EXTENSION:
+ case OBJECT_FUNCTION:
+ case OBJECT_AGGREGATE:
+ case OBJECT_OPERATOR:
+ case OBJECT_LANGUAGE:
+ case OBJECT_CMDTRIGGER:
+ case OBJECT_FDW:
+ case OBJECT_FOREIGN_SERVER:
+ case OBJECT_OPCLASS:
+ case OBJECT_OPFAMILY:
+ {
+ int len = list_length(objname);
+ if (len == 1)
+ cmd->objectname = pstrdup(strVal(linitial(objname)));
+ else if (len == 2)
+ cmd->objectname = pstrdup(strVal(lsecond(objname)));
+ else
+ elog(ERROR, "unexpected name list length (%d)", len);
+ break;
+ }
+ default:
+ elog(ERROR, "unexpected object type (%d)", (int)objtype);
+ break;
+ }
+ }
*** a/src/backend/commands/extension.c
--- b/src/backend/commands/extension.c
***************
*** 39,44 ****
--- 39,45 ----
#include "catalog/pg_namespace.h"
#include "catalog/pg_type.h"
#include "commands/alter.h"
+ #include "commands/cmdtrigger.h"
#include "commands/comment.h"
#include "commands/extension.h"
#include "commands/schemacmds.h"
***************
*** 1190,1206 **** CreateExtension(CreateExtensionStmt *stmt)
List *requiredSchemas;
Oid extensionOid;
ListCell *lc;
/* Check extension name validity before any filesystem access */
check_valid_extension_name(stmt->extname);
/*
* Check for duplicate extension name. The unique index on
* pg_extension.extname would catch this anyway, and serves as a backstop
* in case of race conditions; but this is a friendlier error message, and
* besides we need a check to support IF NOT EXISTS.
*/
! if (get_extension_oid(stmt->extname, true) != InvalidOid)
{
if (stmt->if_not_exists)
{
--- 1191,1222 ----
List *requiredSchemas;
Oid extensionOid;
ListCell *lc;
+ CommandContextData cmd;
/* Check extension name validity before any filesystem access */
check_valid_extension_name(stmt->extname);
/*
+ * Call BEFORE CREATE EXTENSION triggers
+ */
+ InitCommandContext(&cmd, (Node *)stmt);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = InvalidOid;
+ cmd.objectname = stmt->extname;
+ cmd.schemaname = NULL;
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+ /*
* Check for duplicate extension name. The unique index on
* pg_extension.extname would catch this anyway, and serves as a backstop
* in case of race conditions; but this is a friendlier error message, and
* besides we need a check to support IF NOT EXISTS.
*/
! extensionOid = get_extension_oid(stmt->extname, true);
! if ( extensionOid != InvalidOid)
{
if (stmt->if_not_exists)
{
***************
*** 1208,1213 **** CreateExtension(CreateExtensionStmt *stmt)
--- 1224,1236 ----
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("extension \"%s\" already exists, skipping",
stmt->extname)));
+
+ /* Call AFTER CREATE EXTENSION triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = extensionOid;
+ ExecAfterCommandTriggers(&cmd);
+ }
return;
}
else
***************
*** 1467,1472 **** CreateExtension(CreateExtensionStmt *stmt)
--- 1490,1502 ----
*/
ApplyExtensionUpdates(extensionOid, pcontrol,
versionName, updateVersions);
+
+ /* Call AFTER CREATE EXTENSION triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = extensionOid;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
/*
***************
*** 2186,2192 **** pg_extension_config_dump(PG_FUNCTION_ARGS)
* Execute ALTER EXTENSION SET SCHEMA
*/
void
! AlterExtensionNamespace(List *names, const char *newschema)
{
char *extensionName;
Oid extensionOid;
--- 2216,2222 ----
* Execute ALTER EXTENSION SET SCHEMA
*/
void
! AlterExtensionNamespace(List *names, const char *newschema, CommandContext cmd)
{
char *extensionName;
Oid extensionOid;
***************
*** 2247,2252 **** AlterExtensionNamespace(List *names, const char *newschema)
--- 2277,2292 ----
systable_endscan(extScan);
+ /* Call BEFORE ALTER EXTENSION triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = extensionOid;
+ cmd->objectname = extensionName;
+ cmd->schemaname = NULL;
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/*
* If the extension is already in the target schema, just silently do
* nothing.
***************
*** 2342,2347 **** AlterExtensionNamespace(List *names, const char *newschema)
--- 2382,2391 ----
/* update dependencies to point to the new schema */
changeDependencyFor(ExtensionRelationId, extensionOid,
NamespaceRelationId, oldNspOid, nspOid);
+
+ /* Call AFTER ALTER EXTENSION triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
/*
***************
*** 2363,2368 **** ExecAlterExtensionStmt(AlterExtensionStmt *stmt)
--- 2407,2413 ----
Datum datum;
bool isnull;
ListCell *lc;
+ CommandContextData cmd;
/*
* We use global variables to track the extension being created, so we can
***************
*** 2415,2420 **** ExecAlterExtensionStmt(AlterExtensionStmt *stmt)
--- 2460,2479 ----
stmt->extname);
/*
+ * Call BEFORE ALTER EXTENSION triggers
+ */
+ InitCommandContext(&cmd, (Node *)stmt);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = extensionOid;
+ cmd.objectname = stmt->extname;
+ cmd.schemaname = NULL;
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
+ /*
* Read the primary control file. Note we assume that it does not contain
* any non-ASCII data, so there is no need to worry about encoding at this
* point.
***************
*** 2480,2485 **** ExecAlterExtensionStmt(AlterExtensionStmt *stmt)
--- 2539,2548 ----
*/
ApplyExtensionUpdates(extensionOid, control,
oldVersionName, updateVersions);
+
+ /* Call AFTER ALTER EXTENSION triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ ExecAfterCommandTriggers(&cmd);
}
/*
***************
*** 2649,2654 **** ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt)
--- 2712,2718 ----
ObjectAddress object;
Relation relation;
Oid oldExtension;
+ CommandContextData cmd;
extension.classId = ExtensionRelationId;
extension.objectId = get_extension_oid(stmt->extname, false);
***************
*** 2677,2682 **** ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt)
--- 2741,2753 ----
*/
oldExtension = getExtensionOfObject(object.classId, object.objectId);
+ InitCommandContext(&cmd, (Node *)stmt);
+
+ /* Init the command context no matter what, that's cheap here */
+ cmd.objectId = extension.objectId;
+ cmd.objectname = stmt->extname;
+ cmd.schemaname = NULL;
+
if (stmt->action > 0)
{
/*
***************
*** 2689,2694 **** ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt)
--- 2760,2769 ----
getObjectDescription(&object),
get_extension_name(oldExtension))));
+ /* Call BEFORE ALTER EXTENSION command triggers */
+ if (CommandFiresTriggers(&cmd))
+ ExecBeforeCommandTriggers(&cmd);
+
/*
* OK, add the dependency.
*/
***************
*** 2706,2711 **** ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt)
--- 2781,2790 ----
getObjectDescription(&object),
stmt->extname)));
+ /* Call BEFORE ALTER EXTENSION command triggers */
+ if (CommandFiresTriggers(&cmd))
+ ExecBeforeCommandTriggers(&cmd);
+
/*
* OK, drop the dependency.
*/
***************
*** 2723,2726 **** ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt)
--- 2802,2809 ----
*/
if (relation != NULL)
relation_close(relation, NoLock);
+
+ /* Call AFTER ALTER EXTENSION command triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ ExecAfterCommandTriggers(&cmd);
}
*** a/src/backend/commands/foreigncmds.c
--- b/src/backend/commands/foreigncmds.c
***************
*** 204,210 **** GetUserOidFromMapping(const char *username, bool missing_ok)
* Rename foreign-data wrapper
*/
void
! RenameForeignDataWrapper(const char *oldname, const char *newname)
{
HeapTuple tup;
Relation rel;
--- 204,211 ----
* Rename foreign-data wrapper
*/
void
! RenameForeignDataWrapper(const char *oldname, const char *newname,
! CommandContext cmd)
{
HeapTuple tup;
Relation rel;
***************
*** 228,233 **** RenameForeignDataWrapper(const char *oldname, const char *newname)
--- 229,244 ----
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FDW,
oldname);
+ /* Call BEFORE ALTER FOREIGN DATA WRAPPER triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = pstrdup(oldname);
+ cmd->schemaname = NULL;
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* rename */
namestrcpy(&(((Form_pg_foreign_data_wrapper) GETSTRUCT(tup))->fdwname), newname);
simple_heap_update(rel, &tup->t_self, tup);
***************
*** 235,240 **** RenameForeignDataWrapper(const char *oldname, const char *newname)
--- 246,258 ----
heap_close(rel, NoLock);
heap_freetuple(tup);
+
+ /* Call AFTER ALTER FOREIGN DATA WRAPPER triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectname = pstrdup(newname);
+ ExecAfterCommandTriggers(cmd);
+ }
}
***************
*** 242,248 **** RenameForeignDataWrapper(const char *oldname, const char *newname)
* Rename foreign server
*/
void
! RenameForeignServer(const char *oldname, const char *newname)
{
HeapTuple tup;
Relation rel;
--- 260,266 ----
* Rename foreign server
*/
void
! RenameForeignServer(const char *oldname, const char *newname, CommandContext cmd)
{
HeapTuple tup;
Relation rel;
***************
*** 266,271 **** RenameForeignServer(const char *oldname, const char *newname)
--- 284,299 ----
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
oldname);
+ /* Call BEFORE ALTER SERVER triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = pstrdup(oldname);
+ cmd->schemaname = NULL;
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* rename */
namestrcpy(&(((Form_pg_foreign_server) GETSTRUCT(tup))->srvname), newname);
simple_heap_update(rel, &tup->t_self, tup);
***************
*** 273,278 **** RenameForeignServer(const char *oldname, const char *newname)
--- 301,313 ----
heap_close(rel, NoLock);
heap_freetuple(tup);
+
+ /* Call AFTER ALTER SERVER triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectname = pstrdup(newname);
+ ExecAfterCommandTriggers(cmd);
+ }
}
***************
*** 283,289 **** RenameForeignServer(const char *oldname, const char *newname)
* superuser.
*/
static void
! AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
{
Form_pg_foreign_data_wrapper form;
--- 318,325 ----
* superuser.
*/
static void
! AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId,
! CommandContext cmd)
{
Form_pg_foreign_data_wrapper form;
***************
*** 305,310 **** AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerI
--- 341,356 ----
NameStr(form->fdwname)),
errhint("The owner of a foreign-data wrapper must be a superuser.")));
+ /* Call BEFORE ALTER FOREIGN DATA WRAPPER triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = pstrdup(NameStr(form->fdwname));
+ cmd->schemaname = NULL;
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
if (form->fdwowner != newOwnerId)
{
form->fdwowner = newOwnerId;
***************
*** 317,322 **** AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerI
--- 363,372 ----
HeapTupleGetOid(tup),
newOwnerId);
}
+
+ /* Call AFTER ALTER FOREIGN DATA WRAPPER triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
/*
***************
*** 325,331 **** AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerI
* Note restrictions in the "_internal" function, above.
*/
void
! AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
{
HeapTuple tup;
Relation rel;
--- 375,382 ----
* Note restrictions in the "_internal" function, above.
*/
void
! AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId,
! CommandContext cmd)
{
HeapTuple tup;
Relation rel;
***************
*** 339,345 **** AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("foreign-data wrapper \"%s\" does not exist", name)));
! AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
heap_freetuple(tup);
--- 390,396 ----
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("foreign-data wrapper \"%s\" does not exist", name)));
! AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId, cmd);
heap_freetuple(tup);
***************
*** 366,372 **** AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId)
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("foreign-data wrapper with OID %u does not exist", fwdId)));
! AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
heap_freetuple(tup);
--- 417,423 ----
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("foreign-data wrapper with OID %u does not exist", fwdId)));
! AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId, NULL);
heap_freetuple(tup);
***************
*** 377,383 **** AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId)
* Internal workhorse for changing a foreign server's owner
*/
static void
! AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
{
Form_pg_foreign_server form;
--- 428,435 ----
* Internal workhorse for changing a foreign server's owner
*/
static void
! AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId,
! CommandContext cmd)
{
Form_pg_foreign_server form;
***************
*** 411,416 **** AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
--- 463,478 ----
}
}
+ /* Call BEFORE ALTER SERVER triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = pstrdup(NameStr(form->srvname));
+ cmd->schemaname = NULL;
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
form->srvowner = newOwnerId;
simple_heap_update(rel, &tup->t_self, tup);
***************
*** 419,424 **** AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
--- 481,490 ----
/* Update owner dependency reference */
changeDependencyOnOwner(ForeignServerRelationId, HeapTupleGetOid(tup),
newOwnerId);
+
+ /* Call AFTER ALTER SERVER triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
}
***************
*** 426,432 **** AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
* Change foreign server owner -- by name
*/
void
! AlterForeignServerOwner(const char *name, Oid newOwnerId)
{
HeapTuple tup;
Relation rel;
--- 492,498 ----
* Change foreign server owner -- by name
*/
void
! AlterForeignServerOwner(const char *name, Oid newOwnerId, CommandContext cmd)
{
HeapTuple tup;
Relation rel;
***************
*** 440,446 **** AlterForeignServerOwner(const char *name, Oid newOwnerId)
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("server \"%s\" does not exist", name)));
! AlterForeignServerOwner_internal(rel, tup, newOwnerId);
heap_freetuple(tup);
--- 506,512 ----
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("server \"%s\" does not exist", name)));
! AlterForeignServerOwner_internal(rel, tup, newOwnerId, cmd);
heap_freetuple(tup);
***************
*** 465,471 **** AlterForeignServerOwner_oid(Oid srvId, Oid newOwnerId)
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("foreign server with OID %u does not exist", srvId)));
! AlterForeignServerOwner_internal(rel, tup, newOwnerId);
heap_freetuple(tup);
--- 531,537 ----
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("foreign server with OID %u does not exist", srvId)));
! AlterForeignServerOwner_internal(rel, tup, newOwnerId, NULL);
heap_freetuple(tup);
***************
*** 578,583 **** CreateForeignDataWrapper(CreateFdwStmt *stmt)
--- 644,650 ----
Oid ownerId;
ObjectAddress myself;
ObjectAddress referenced;
+ CommandContextData cmd;
rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
***************
*** 601,606 **** CreateForeignDataWrapper(CreateFdwStmt *stmt)
--- 668,685 ----
errmsg("foreign-data wrapper \"%s\" already exists",
stmt->fdwname)));
+ /* Call BEFORE CREATE FOREIGN DATA WRAPPER triggers */
+ InitCommandContext(&cmd, (Node *)stmt);
+
+ 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, NULL);
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);
+
+ 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);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = InvalidOid;
+ cmd.objectname = stmt->servername;
+ cmd.schemaname = NULL;
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
/*
* Insert tuple into pg_foreign_server.
*/
***************
*** 966,971 **** CreateForeignServer(CreateForeignServerStmt *stmt)
--- 1082,1094 ----
ForeignServerRelationId, srvId, 0, NULL);
heap_close(rel, RowExclusiveLock);
+
+ /* Call AFTER CREATE SERVER triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = srvId;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
***************
*** 982,987 **** AlterForeignServer(AlterForeignServerStmt *stmt)
--- 1105,1111 ----
bool repl_repl[Natts_pg_foreign_server];
Oid srvId;
Form_pg_foreign_server srvForm;
+ CommandContextData cmd;
rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
***************
*** 1003,1008 **** AlterForeignServer(AlterForeignServerStmt *stmt)
--- 1127,1144 ----
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
stmt->servername);
+ /* Call BEFORE ALTER SERVER triggers */
+ InitCommandContext(&cmd, (Node *)stmt);
+
+ 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));
***************
*** 1059,1064 **** AlterForeignServer(AlterForeignServerStmt *stmt)
--- 1195,1204 ----
heap_freetuple(tp);
heap_close(rel, RowExclusiveLock);
+
+ /* Call AFTER ALTER SERVER triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ ExecAfterCommandTriggers(&cmd);
}
***************
*** 1130,1135 **** CreateUserMapping(CreateUserMappingStmt *stmt)
--- 1270,1276 ----
ObjectAddress referenced;
ForeignServer *srv;
ForeignDataWrapper *fdw;
+ CommandContextData cmd;
rel = heap_open(UserMappingRelationId, RowExclusiveLock);
***************
*** 1155,1160 **** CreateUserMapping(CreateUserMappingStmt *stmt)
--- 1296,1313 ----
fdw = GetForeignDataWrapper(srv->fdwid);
+ /* Call BEFORE CREATE USER MAPPING triggers */
+ InitCommandContext(&cmd, (Node *)stmt);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = InvalidOid;
+ cmd.objectname = NULL; /* composite object name */
+ cmd.schemaname = NULL;
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
/*
* Insert tuple into pg_user_mapping.
*/
***************
*** 1207,1212 **** CreateUserMapping(CreateUserMappingStmt *stmt)
--- 1360,1372 ----
UserMappingRelationId, umId, 0, NULL);
heap_close(rel, RowExclusiveLock);
+
+ /* Call AFTER CREATE USER MAPPING triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = umId;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
***************
*** 1224,1229 **** AlterUserMapping(AlterUserMappingStmt *stmt)
--- 1384,1390 ----
Oid useId;
Oid umId;
ForeignServer *srv;
+ CommandContextData cmd;
rel = heap_open(UserMappingRelationId, RowExclusiveLock);
***************
*** 1246,1251 **** AlterUserMapping(AlterUserMappingStmt *stmt)
--- 1407,1424 ----
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for user mapping %u", umId);
+ /* Call BEFORE ALTER USER MAPPING triggers */
+ InitCommandContext(&cmd, (Node *)stmt);
+
+ 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));
***************
*** 1293,1298 **** AlterUserMapping(AlterUserMappingStmt *stmt)
--- 1466,1475 ----
heap_freetuple(tp);
heap_close(rel, RowExclusiveLock);
+
+ /* Call AFTER ALTER SERVER triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ ExecAfterCommandTriggers(&cmd);
}
***************
*** 1306,1311 **** RemoveUserMapping(DropUserMappingStmt *stmt)
--- 1483,1489 ----
Oid useId;
Oid umId;
ForeignServer *srv;
+ CommandContextData cmd;
useId = GetUserOidFromMapping(stmt->username, stmt->missing_ok);
srv = GetForeignServerByName(stmt->servername, true);
***************
*** 1353,1358 **** RemoveUserMapping(DropUserMappingStmt *stmt)
--- 1531,1548 ----
user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername);
+ /* Call BEFORE DROP USER MAPPING triggers */
+ InitCommandContext(&cmd, (Node *)stmt);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = umId;
+ cmd.objectname = NULL; /* composite object name */
+ cmd.schemaname = NULL;
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
/*
* Do the deletion
*/
***************
*** 1361,1366 **** RemoveUserMapping(DropUserMappingStmt *stmt)
--- 1551,1562 ----
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);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = InvalidOid;
+ cmd.objectname = (char *)funcname;
+ cmd.schemaname = get_namespace_name(namespaceId);
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+ /*
* And now that we have all the parameters, and know we're permitted to do
* so, go ahead and create the function.
*/
! procOid =
! ProcedureCreate(funcname,
! namespaceId,
! stmt->replace,
! returnsSet,
! prorettype,
! languageOid,
! languageValidator,
! prosrc_str, /* converted to text later */
! probin_str, /* converted to text later */
! false, /* not an aggregate */
! isWindowFunc,
! security,
! isLeakProof,
! isStrict,
! volatility,
! parameterTypes,
! PointerGetDatum(allParameterTypes),
! PointerGetDatum(parameterModes),
! PointerGetDatum(parameterNames),
! parameterDefaults,
! PointerGetDatum(proconfig),
! procost,
! prorows);
!
! /* Call AFTER CREATE FUNCTION triggers */
! if (CommandFiresAfterTriggers(&cmd))
! {
! cmd.objectId = procOid;
! ExecAfterCommandTriggers(&cmd);
! }
}
***************
*** 1053,1059 **** RemoveFunctionById(Oid funcOid)
* Rename function
*/
void
! RenameFunction(List *name, List *argtypes, const char *newname)
{
Oid procOid;
Oid namespaceOid;
--- 1077,1083 ----
* Rename function
*/
void
! RenameFunction(List *name, List *argtypes, const char *newname, CommandContext cmd)
{
Oid procOid;
Oid namespaceOid;
***************
*** 1107,1112 **** RenameFunction(List *name, List *argtypes, const char *newname)
--- 1131,1146 ----
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
+ /* Call BEFORE ALTER FUNCTION triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = procOid;
+ cmd->objectname = pstrdup(NameStr(procForm->proname));
+ cmd->schemaname = get_namespace_name(namespaceOid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* rename */
namestrcpy(&(procForm->proname), newname);
simple_heap_update(rel, &tup->t_self, tup);
***************
*** 1114,1126 **** RenameFunction(List *name, List *argtypes, const char *newname)
heap_close(rel, NoLock);
heap_freetuple(tup);
}
/*
* Change function owner by name and args
*/
void
! AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId)
{
Relation rel;
Oid procOid;
--- 1148,1168 ----
heap_close(rel, NoLock);
heap_freetuple(tup);
+
+ /* Call AFTER ALTER FUNCTION triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectname = pstrdup(newname);
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
* Change function owner by name and args
*/
void
! AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId,
! CommandContext cmd)
{
Relation rel;
Oid procOid;
***************
*** 1141,1147 **** AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId)
NameListToString(name)),
errhint("Use ALTER AGGREGATE to change owner of aggregate functions.")));
! AlterFunctionOwner_internal(rel, tup, newOwnerId);
heap_close(rel, NoLock);
}
--- 1183,1189 ----
NameListToString(name)),
errhint("Use ALTER AGGREGATE to change owner of aggregate functions.")));
! AlterFunctionOwner_internal(rel, tup, newOwnerId, cmd);
heap_close(rel, NoLock);
}
***************
*** 1150,1156 **** AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId)
* Change function owner by Oid
*/
void
! AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId)
{
Relation rel;
HeapTuple tup;
--- 1192,1198 ----
* Change function owner by Oid
*/
void
! AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId, CommandContext cmd)
{
Relation rel;
HeapTuple tup;
***************
*** 1160,1172 **** AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId)
tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procOid));
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for function %u", procOid);
! AlterFunctionOwner_internal(rel, tup, newOwnerId);
heap_close(rel, NoLock);
}
static void
! AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
{
Form_pg_proc procForm;
AclResult aclresult;
--- 1202,1215 ----
tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procOid));
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for function %u", procOid);
! AlterFunctionOwner_internal(rel, tup, newOwnerId, cmd);
heap_close(rel, NoLock);
}
static void
! AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId,
! CommandContext cmd)
{
Form_pg_proc procForm;
AclResult aclresult;
***************
*** 1212,1217 **** AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
--- 1255,1269 ----
get_namespace_name(procForm->pronamespace));
}
+ /* Call BEFORE ALTER FUNCTION triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = procOid;
+ cmd->objectname = pstrdup(NameStr(procForm->proname));
+ cmd->schemaname = get_namespace_name(procForm->pronamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
memset(repl_null, false, sizeof(repl_null));
memset(repl_repl, false, sizeof(repl_repl));
***************
*** 1243,1248 **** AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
--- 1295,1304 ----
/* Update owner dependency reference */
changeDependencyOnOwner(ProcedureRelationId, procOid, newOwnerId);
+
+ /* Call AFTER ALTER FUNCTION triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
ReleaseSysCache(tup);
***************
*** 1268,1273 **** AlterFunction(AlterFunctionStmt *stmt)
--- 1324,1330 ----
List *set_items = NIL;
DefElem *cost_item = NULL;
DefElem *rows_item = NULL;
+ CommandContextData cmd;
rel = heap_open(ProcedureRelationId, RowExclusiveLock);
***************
*** 1342,1347 **** AlterFunction(AlterFunctionStmt *stmt)
--- 1399,1417 ----
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("ROWS is not applicable when function does not return a set")));
}
+
+ /* Call BEFORE ALTER FUNCTION command triggers */
+ InitCommandContext(&cmd, (Node *)stmt);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = HeapTupleGetOid(tup);
+ cmd.objectname = pstrdup(NameStr(procForm->proname));
+ cmd.schemaname = get_namespace_name(procForm->pronamespace);
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
if (set_items)
{
Datum datum;
***************
*** 1383,1388 **** AlterFunction(AlterFunctionStmt *stmt)
--- 1453,1462 ----
heap_close(rel, NoLock);
heap_freetuple(tup);
+
+ /* Call AFTER ALTER FUNCTION command triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ ExecAfterCommandTriggers(&cmd);
}
/*
***************
*** 1479,1484 **** CreateCast(CreateCastStmt *stmt)
--- 1553,1559 ----
ObjectAddress myself,
referenced;
AclResult aclresult;
+ CommandContextData cmd;
sourcetypeid = typenameTypeId(NULL, stmt->sourcetype);
targettypeid = typenameTypeId(NULL, stmt->targettype);
***************
*** 1697,1702 **** CreateCast(CreateCastStmt *stmt)
--- 1772,1789 ----
break;
}
+ /* Call BEFORE CREATE CAST command triggers */
+ InitCommandContext(&cmd, (Node *)stmt);
+
+ 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);
/*
***************
*** 1765,1770 **** CreateCast(CreateCastStmt *stmt)
--- 1852,1863 ----
heap_freetuple(tuple);
heap_close(relation, RowExclusiveLock);
+
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = castid;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
/*
***************
*** 1823,1829 **** DropCastById(Oid castOid)
*/
void
AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
! const char *newschema)
{
Oid procOid;
Oid nspOid;
--- 1916,1922 ----
*/
void
AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
! const char *newschema, CommandContext cmd)
{
Oid procOid;
Oid nspOid;
***************
*** 1837,1847 **** 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;
--- 1930,1940 ----
/* 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;
***************
*** 1876,1881 **** AlterFunctionNamespace_oid(Oid procOid, Oid nspOid)
--- 1969,1984 ----
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 */
***************
*** 1894,1899 **** AlterFunctionNamespace_oid(Oid procOid, Oid nspOid)
--- 1997,2007 ----
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);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = InvalidOid;
+ cmd.objectname = pstrdup(opcname);
+ cmd.schemaname = get_namespace_name(namespaceoid);
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
/*
***************
*** 720,725 **** DefineOpClass(CreateOpClassStmt *stmt)
--- 733,745 ----
OperatorClassRelationId, opclassoid, 0, NULL);
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);
+
+ 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);
+
+ 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 = pstrdup(NameStr(opfForm->opfname));
+ cmd.schemaname = get_namespace_name(opfForm->opfnamespace);
+
+ ReleaseSysCache(htup);
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
/*
* ADD and DROP cases need separate code from here on down.
*/
***************
*** 826,831 **** AlterOpFamily(AlterOpFamilyStmt *stmt)
--- 888,897 ----
AlterOpFamilyAdd(stmt->opfamilyname, amoid, opfamilyoid,
maxOpNumber, maxProcNumber,
stmt->items);
+
+ /* Call AFTER ALTER OPERATOR FAMILY command triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ ExecAfterCommandTriggers(&cmd);
}
/*
***************
*** 1667,1673 **** RemoveAmProcEntryById(Oid entryOid)
* Rename opclass
*/
void
! RenameOpClass(List *name, const char *access_method, const char *newname)
{
Oid opcOid;
Oid amOid;
--- 1733,1740 ----
* Rename opclass
*/
void
! RenameOpClass(List *name, const char *access_method, const char *newname,
! CommandContext cmd)
{
Oid opcOid;
Oid amOid;
***************
*** 1712,1717 **** RenameOpClass(List *name, const char *access_method, const char *newname)
--- 1779,1794 ----
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
+ /* Call BEFORE ALTER OPERATOR CLASS triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = opcOid;
+ cmd->objectname = NameListToString(name);
+ cmd->schemaname = get_namespace_name(namespaceOid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* rename */
namestrcpy(&(((Form_pg_opclass) GETSTRUCT(tup))->opcname), newname);
simple_heap_update(rel, &tup->t_self, tup);
***************
*** 1719,1731 **** RenameOpClass(List *name, const char *access_method, const char *newname)
heap_close(rel, NoLock);
heap_freetuple(tup);
}
/*
* Rename opfamily
*/
void
! RenameOpFamily(List *name, const char *access_method, const char *newname)
{
Oid opfOid;
Oid amOid;
--- 1796,1816 ----
heap_close(rel, NoLock);
heap_freetuple(tup);
+
+ /* Call AFTER ALTER OPERATOR CLASS triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectname = pstrdup(newname);
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
* Rename opfamily
*/
void
! RenameOpFamily(List *name, const char *access_method, const char *newname,
! CommandContext cmd)
{
Oid opfOid;
Oid amOid;
***************
*** 1801,1806 **** RenameOpFamily(List *name, const char *access_method, const char *newname)
--- 1886,1901 ----
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
+ /* Call BEFORE ALTER OPERATOR FAMILY triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = opfOid;
+ cmd->objectname = pstrdup(opfname);
+ cmd->schemaname = get_namespace_name(namespaceOid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* rename */
namestrcpy(&(((Form_pg_opfamily) GETSTRUCT(tup))->opfname), newname);
simple_heap_update(rel, &tup->t_self, tup);
***************
*** 1808,1820 **** RenameOpFamily(List *name, const char *access_method, const char *newname)
heap_close(rel, NoLock);
heap_freetuple(tup);
}
/*
* Change opclass owner by name
*/
void
! AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId)
{
Oid amOid;
Relation rel;
--- 1903,1923 ----
heap_close(rel, NoLock);
heap_freetuple(tup);
+
+ /* Call AFTER ALTER OPERATOR FAMILY triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectname = pstrdup(newname);
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
* Change opclass owner by name
*/
void
! AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId,
! CommandContext cmd)
{
Oid amOid;
Relation rel;
***************
*** 1830,1836 **** AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId)
tup = heap_copytuple(origtup);
ReleaseSysCache(origtup);
! AlterOpClassOwner_internal(rel, tup, newOwnerId);
heap_freetuple(tup);
heap_close(rel, NoLock);
--- 1933,1939 ----
tup = heap_copytuple(origtup);
ReleaseSysCache(origtup);
! AlterOpClassOwner_internal(rel, tup, newOwnerId, cmd);
heap_freetuple(tup);
heap_close(rel, NoLock);
***************
*** 1851,1857 **** AlterOpClassOwner_oid(Oid opclassOid, Oid newOwnerId)
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for opclass %u", opclassOid);
! AlterOpClassOwner_internal(rel, tup, newOwnerId);
heap_freetuple(tup);
heap_close(rel, NoLock);
--- 1954,1960 ----
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for opclass %u", opclassOid);
! AlterOpClassOwner_internal(rel, tup, newOwnerId, NULL);
heap_freetuple(tup);
heap_close(rel, NoLock);
***************
*** 1862,1868 **** AlterOpClassOwner_oid(Oid opclassOid, Oid newOwnerId)
* parameter is a copy of the tuple from pg_opclass we want to modify.
*/
static void
! AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
{
Oid namespaceOid;
AclResult aclresult;
--- 1965,1972 ----
* parameter is a copy of the tuple from pg_opclass we want to modify.
*/
static void
! AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId,
! CommandContext cmd)
{
Oid namespaceOid;
AclResult aclresult;
***************
*** 1900,1905 **** AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
--- 2004,2019 ----
get_namespace_name(namespaceOid));
}
+ /* Call BEFORE ALTER OPERATOR CLASS triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = pstrdup(NameStr(opcForm->opcname));
+ cmd->schemaname = get_namespace_name(opcForm->opcnamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/*
* Modify the owner --- okay to scribble on tup because it's a copy
*/
***************
*** 1912,1917 **** AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
--- 2026,2035 ----
/* Update owner dependency reference */
changeDependencyOnOwner(OperatorClassRelationId, HeapTupleGetOid(tup),
newOwnerId);
+
+ /* Call AFTER ALTER OPERATOR CLASS triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
}
***************
*** 1919,1925 **** AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
* ALTER OPERATOR CLASS any_name USING access_method SET SCHEMA name
*/
void
! AlterOpClassNamespace(List *name, char *access_method, const char *newschema)
{
Oid amOid;
Relation rel;
--- 2037,2044 ----
* ALTER OPERATOR CLASS any_name USING access_method SET SCHEMA name
*/
void
! AlterOpClassNamespace(List *name, char *access_method, const char *newschema,
! CommandContext cmd)
{
Oid amOid;
Relation rel;
***************
*** 1941,1947 **** AlterOpClassNamespace(List *name, char *access_method, const char *newschema)
Anum_pg_opclass_opcname,
Anum_pg_opclass_opcnamespace,
Anum_pg_opclass_opcowner,
! ACL_KIND_OPCLASS);
heap_close(rel, RowExclusiveLock);
}
--- 2060,2066 ----
Anum_pg_opclass_opcname,
Anum_pg_opclass_opcnamespace,
Anum_pg_opclass_opcowner,
! ACL_KIND_OPCLASS, cmd);
heap_close(rel, RowExclusiveLock);
}
***************
*** 1960,1966 **** AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid)
Anum_pg_opclass_opcname,
Anum_pg_opclass_opcnamespace,
Anum_pg_opclass_opcowner,
! ACL_KIND_OPCLASS);
heap_close(rel, RowExclusiveLock);
--- 2079,2085 ----
Anum_pg_opclass_opcname,
Anum_pg_opclass_opcnamespace,
Anum_pg_opclass_opcowner,
! ACL_KIND_OPCLASS, NULL);
heap_close(rel, RowExclusiveLock);
***************
*** 1971,1977 **** AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid)
* Change opfamily owner by name
*/
void
! AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId)
{
Oid amOid;
Relation rel;
--- 2090,2097 ----
* Change opfamily owner by name
*/
void
! AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId,
! CommandContext cmd)
{
Oid amOid;
Relation rel;
***************
*** 2020,2026 **** AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId)
elog(ERROR, "cache lookup failed for opfamily %u", opfOid);
}
! AlterOpFamilyOwner_internal(rel, tup, newOwnerId);
heap_freetuple(tup);
heap_close(rel, NoLock);
--- 2140,2146 ----
elog(ERROR, "cache lookup failed for opfamily %u", opfOid);
}
! AlterOpFamilyOwner_internal(rel, tup, newOwnerId, cmd);
heap_freetuple(tup);
heap_close(rel, NoLock);
***************
*** 2041,2047 **** AlterOpFamilyOwner_oid(Oid opfamilyOid, Oid newOwnerId)
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for opfamily %u", opfamilyOid);
! AlterOpFamilyOwner_internal(rel, tup, newOwnerId);
heap_freetuple(tup);
heap_close(rel, NoLock);
--- 2161,2167 ----
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for opfamily %u", opfamilyOid);
! AlterOpFamilyOwner_internal(rel, tup, newOwnerId, NULL);
heap_freetuple(tup);
heap_close(rel, NoLock);
***************
*** 2052,2058 **** AlterOpFamilyOwner_oid(Oid opfamilyOid, Oid newOwnerId)
* parameter is a copy of the tuple from pg_opfamily we want to modify.
*/
static void
! AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
{
Oid namespaceOid;
AclResult aclresult;
--- 2172,2179 ----
* parameter is a copy of the tuple from pg_opfamily we want to modify.
*/
static void
! AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId,
! CommandContext cmd)
{
Oid namespaceOid;
AclResult aclresult;
***************
*** 2090,2095 **** AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
--- 2211,2226 ----
get_namespace_name(namespaceOid));
}
+ /* Call BEFORE ALTER OPERATOR FAMILY triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = pstrdup(NameStr(opfForm->opfname));
+ cmd->schemaname = get_namespace_name(opfForm->opfnamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/*
* Modify the owner --- okay to scribble on tup because it's a copy
*/
***************
*** 2102,2107 **** AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
--- 2233,2242 ----
/* Update owner dependency reference */
changeDependencyOnOwner(OperatorFamilyRelationId, HeapTupleGetOid(tup),
newOwnerId);
+
+ /* Call AFTER ALTER OPERATOR FAMILY triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
}
***************
*** 2128,2134 **** get_am_oid(const char *amname, bool missing_ok)
* ALTER OPERATOR FAMILY any_name USING access_method SET SCHEMA name
*/
void
! AlterOpFamilyNamespace(List *name, char *access_method, const char *newschema)
{
Oid amOid;
Relation rel;
--- 2263,2270 ----
* ALTER OPERATOR FAMILY any_name USING access_method SET SCHEMA name
*/
void
! AlterOpFamilyNamespace(List *name, char *access_method, const char *newschema,
! CommandContext cmd)
{
Oid amOid;
Relation rel;
***************
*** 2150,2156 **** AlterOpFamilyNamespace(List *name, char *access_method, const char *newschema)
Anum_pg_opfamily_opfname,
Anum_pg_opfamily_opfnamespace,
Anum_pg_opfamily_opfowner,
! ACL_KIND_OPFAMILY);
heap_close(rel, RowExclusiveLock);
}
--- 2286,2292 ----
Anum_pg_opfamily_opfname,
Anum_pg_opfamily_opfnamespace,
Anum_pg_opfamily_opfowner,
! ACL_KIND_OPFAMILY, cmd);
heap_close(rel, RowExclusiveLock);
}
***************
*** 2169,2175 **** AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid)
Anum_pg_opfamily_opfname,
Anum_pg_opfamily_opfnamespace,
Anum_pg_opfamily_opfowner,
! ACL_KIND_OPFAMILY);
heap_close(rel, RowExclusiveLock);
--- 2305,2311 ----
Anum_pg_opfamily_opfname,
Anum_pg_opfamily_opfnamespace,
Anum_pg_opfamily_opfowner,
! ACL_KIND_OPFAMILY, NULL);
heap_close(rel, RowExclusiveLock);
*** a/src/backend/commands/operatorcmds.c
--- b/src/backend/commands/operatorcmds.c
***************
*** 51,57 ****
#include "utils/syscache.h"
! static void AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId);
/*
* DefineOperator
--- 51,58 ----
#include "utils/syscache.h"
! static void AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId,
! CommandContext cmd);
/*
* DefineOperator
***************
*** 62,68 **** static void AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerI
* 'parameters' is a list of DefElem
*/
void
! DefineOperator(List *names, List *parameters)
{
char *oprName;
Oid oprNamespace;
--- 63,69 ----
* 'parameters' is a list of DefElem
*/
void
! DefineOperator(List *names, List *parameters, CommandContext cmd)
{
char *oprName;
Oid oprNamespace;
***************
*** 310,316 **** DefineOperator(List *names, List *parameters)
restrictionOid, /* optional restrict. sel. procedure */
joinOid, /* optional join sel. procedure name */
canMerge, /* operator merges */
! canHash); /* operator hashes */
}
--- 311,318 ----
restrictionOid, /* optional restrict. sel. procedure */
joinOid, /* optional join sel. procedure name */
canMerge, /* operator merges */
! canHash, /* operator hashes */
! cmd);
}
***************
*** 343,349 **** AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId)
rel = heap_open(OperatorRelationId, RowExclusiveLock);
! AlterOperatorOwner_internal(rel, operOid, newOwnerId);
heap_close(rel, NoLock);
}
--- 345,351 ----
rel = heap_open(OperatorRelationId, RowExclusiveLock);
! AlterOperatorOwner_internal(rel, operOid, newOwnerId, NULL);
heap_close(rel, NoLock);
}
***************
*** 353,359 **** AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId)
*/
void
AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2,
! Oid newOwnerId)
{
Oid operOid;
Relation rel;
--- 355,361 ----
*/
void
AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2,
! Oid newOwnerId, CommandContext cmd)
{
Oid operOid;
Relation rel;
***************
*** 364,376 **** AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2,
typeName1, typeName2,
false, -1);
! AlterOperatorOwner_internal(rel, operOid, newOwnerId);
heap_close(rel, NoLock);
}
static void
! AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId)
{
HeapTuple tup;
AclResult aclresult;
--- 366,379 ----
typeName1, typeName2,
false, -1);
! AlterOperatorOwner_internal(rel, operOid, newOwnerId, cmd);
heap_close(rel, NoLock);
}
static void
! AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId,
! CommandContext cmd)
{
HeapTuple tup;
AclResult aclresult;
***************
*** 410,415 **** AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId)
--- 413,428 ----
get_namespace_name(oprForm->oprnamespace));
}
+ /* Call BEFORE ALTER OPERATOR triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = operOid;
+ cmd->objectname = pstrdup(NameStr(oprForm->oprname));
+ cmd->schemaname = get_namespace_name(oprForm->oprnamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/*
* Modify the owner --- okay to scribble on tup because it's a copy
*/
***************
*** 424,436 **** AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId)
}
heap_freetuple(tup);
}
/*
* Execute ALTER OPERATOR SET SCHEMA
*/
void
! AlterOperatorNamespace(List *names, List *argtypes, const char *newschema)
{
List *operatorName = names;
TypeName *typeName1 = (TypeName *) linitial(argtypes);
--- 437,454 ----
}
heap_freetuple(tup);
+
+ /* Call AFTER ALTER OPERATOR triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
/*
* Execute ALTER OPERATOR SET SCHEMA
*/
void
! AlterOperatorNamespace(List *names, List *argtypes, const char *newschema,
! CommandContext cmd)
{
List *operatorName = names;
TypeName *typeName1 = (TypeName *) linitial(argtypes);
***************
*** 454,460 **** AlterOperatorNamespace(List *names, List *argtypes, const char *newschema)
Anum_pg_operator_oprname,
Anum_pg_operator_oprnamespace,
Anum_pg_operator_oprowner,
! ACL_KIND_OPER);
heap_close(rel, RowExclusiveLock);
}
--- 472,478 ----
Anum_pg_operator_oprname,
Anum_pg_operator_oprnamespace,
Anum_pg_operator_oprowner,
! ACL_KIND_OPER, cmd);
heap_close(rel, RowExclusiveLock);
}
***************
*** 472,478 **** AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid)
Anum_pg_operator_oprname,
Anum_pg_operator_oprnamespace,
Anum_pg_operator_oprowner,
! ACL_KIND_OPER);
heap_close(rel, RowExclusiveLock);
--- 490,496 ----
Anum_pg_operator_oprname,
Anum_pg_operator_oprnamespace,
Anum_pg_operator_oprowner,
! ACL_KIND_OPER, NULL);
heap_close(rel, RowExclusiveLock);
*** a/src/backend/commands/proclang.c
--- b/src/backend/commands/proclang.c
***************
*** 49,60 **** typedef struct
char *tmpllibrary; /* path of shared library */
} PLTemplate;
! static void create_proc_lang(const char *languageName, bool replace,
Oid languageOwner, Oid handlerOid, Oid inlineOid,
Oid valOid, bool trusted);
static PLTemplate *find_language_template(const char *languageName);
static void AlterLanguageOwner_internal(HeapTuple tup, Relation rel,
! Oid newOwnerId);
/* ---------------------------------------------------------------------
--- 49,60 ----
char *tmpllibrary; /* path of shared library */
} PLTemplate;
! static Oid create_proc_lang(const char *languageName, bool replace,
Oid languageOwner, Oid handlerOid, Oid inlineOid,
Oid valOid, bool trusted);
static PLTemplate *find_language_template(const char *languageName);
static void AlterLanguageOwner_internal(HeapTuple tup, Relation rel,
! Oid newOwnerId, CommandContext cmd);
/* ---------------------------------------------------------------------
***************
*** 67,75 **** CreateProceduralLanguage(CreatePLangStmt *stmt)
PLTemplate *pltemplate;
Oid handlerOid,
inlineOid,
! valOid;
Oid funcrettype;
Oid funcargtypes[1];
/*
* If we have template information for the language, ignore the supplied
--- 67,79 ----
PLTemplate *pltemplate;
Oid handlerOid,
inlineOid,
! valOid,
! loid;
Oid funcrettype;
Oid funcargtypes[1];
+ CommandContextData cmd;
+
+ InitCommandContext(&cmd, (Node *)stmt);
/*
* 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, NULL);
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);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = InvalidOid;
+ cmd.objectname = (char *)schemaName;
+ cmd.schemaname = NULL; /* a schema does not live in another schema */
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+ /*
* If the requested authorization is different from the current user,
* temporarily set the current user so that the object(s) will be created
* with the correct ownership.
***************
*** 144,149 **** CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
--- 160,172 ----
/* Reset current user and security context */
SetUserIdAndSecContext(saved_uid, save_sec_context);
+
+ /* Call AFTER CREATE SCHEMA triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = namespaceId;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
/*
***************
*** 174,180 **** RemoveSchemaById(Oid schemaOid)
* Rename schema
*/
void
! RenameSchema(const char *oldname, const char *newname)
{
HeapTuple tup;
Relation rel;
--- 197,203 ----
* Rename schema
*/
void
! RenameSchema(const char *oldname, const char *newname, CommandContext cmd)
{
HeapTuple tup;
Relation rel;
***************
*** 211,216 **** RenameSchema(const char *oldname, const char *newname)
--- 234,249 ----
errmsg("unacceptable schema name \"%s\"", newname),
errdetail("The prefix \"pg_\" is reserved for system schemas.")));
+ /* Call BEFORE ALTER SCHEMA triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = pstrdup(oldname);
+ cmd->schemaname = NULL;
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* rename */
namestrcpy(&(((Form_pg_namespace) GETSTRUCT(tup))->nspname), newname);
simple_heap_update(rel, &tup->t_self, tup);
***************
*** 218,223 **** RenameSchema(const char *oldname, const char *newname)
--- 251,263 ----
heap_close(rel, NoLock);
heap_freetuple(tup);
+
+ /* Call AFTER ALTER SCHEMA triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectname = pstrdup(newname);
+ ExecAfterCommandTriggers(cmd);
+ }
}
void
***************
*** 232,238 **** AlterSchemaOwner_oid(Oid oid, Oid newOwnerId)
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for schema %u", oid);
! AlterSchemaOwner_internal(tup, rel, newOwnerId);
ReleaseSysCache(tup);
--- 272,278 ----
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for schema %u", oid);
! AlterSchemaOwner_internal(tup, rel, newOwnerId, NULL);
ReleaseSysCache(tup);
***************
*** 244,250 **** AlterSchemaOwner_oid(Oid oid, Oid newOwnerId)
* Change schema owner
*/
void
! AlterSchemaOwner(const char *name, Oid newOwnerId)
{
HeapTuple tup;
Relation rel;
--- 284,290 ----
* Change schema owner
*/
void
! AlterSchemaOwner(const char *name, Oid newOwnerId, CommandContext cmd)
{
HeapTuple tup;
Relation rel;
***************
*** 257,263 **** AlterSchemaOwner(const char *name, Oid newOwnerId)
(errcode(ERRCODE_UNDEFINED_SCHEMA),
errmsg("schema \"%s\" does not exist", name)));
! AlterSchemaOwner_internal(tup, rel, newOwnerId);
ReleaseSysCache(tup);
--- 297,303 ----
(errcode(ERRCODE_UNDEFINED_SCHEMA),
errmsg("schema \"%s\" does not exist", name)));
! AlterSchemaOwner_internal(tup, rel, newOwnerId, cmd);
ReleaseSysCache(tup);
***************
*** 265,271 **** AlterSchemaOwner(const char *name, Oid newOwnerId)
}
static void
! AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
{
Form_pg_namespace nspForm;
--- 305,312 ----
}
static void
! AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId,
! CommandContext cmd)
{
Form_pg_namespace nspForm;
***************
*** 312,317 **** AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
--- 353,368 ----
aclcheck_error(aclresult, ACL_KIND_DATABASE,
get_database_name(MyDatabaseId));
+ /* Call BEFORE ALTER SCHEMA triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = pstrdup(NameStr(nspForm->nspname));
+ cmd->schemaname = NULL;
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
memset(repl_null, false, sizeof(repl_null));
memset(repl_repl, false, sizeof(repl_repl));
***************
*** 343,348 **** AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
/* Update owner dependency reference */
changeDependencyOnOwner(NamespaceRelationId, HeapTupleGetOid(tup),
newOwnerId);
- }
}
--- 394,402 ----
/* Update owner dependency reference */
changeDependencyOnOwner(NamespaceRelationId, HeapTupleGetOid(tup),
newOwnerId);
+ /* Call AFTER ALTER SCHEMA triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
+ }
}
*** a/src/backend/commands/sequence.c
--- b/src/backend/commands/sequence.c
***************
*** 20,25 ****
--- 20,26 ----
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
+ #include "commands/cmdtrigger.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
#include "funcapi.h"
***************
*** 28,33 ****
--- 29,35 ----
#include "storage/lmgr.h"
#include "storage/proc.h"
#include "storage/smgr.h"
+ #include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
***************
*** 115,127 **** DefineSequence(CreateSeqStmt *seq)
bool null[SEQ_COL_LASTCOL];
int i;
NameData name;
/* Unlogged sequences are not implemented -- not clear if useful. */
if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unlogged sequences are not supported")));
-
/* Check and set all option values */
init_params(seq->options, true, &new, &owned_by);
--- 117,129 ----
bool null[SEQ_COL_LASTCOL];
int i;
NameData name;
+ CommandContextData cmd;
/* Unlogged sequences are not implemented -- not clear if useful. */
if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unlogged sequences are not supported")));
/* Check and set all option values */
init_params(seq->options, true, &new, &owned_by);
***************
*** 211,217 **** DefineSequence(CreateSeqStmt *seq)
stmt->tablespacename = NULL;
stmt->if_not_exists = false;
! seqoid = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId);
Assert(seqoid != InvalidOid);
rel = heap_open(seqoid, AccessExclusiveLock);
--- 213,222 ----
stmt->tablespacename = NULL;
stmt->if_not_exists = false;
! /* Prepare BEFORE CREATE SEQUENCE triggers */
! InitCommandContext(&cmd, (Node *)seq);
!
! seqoid = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId, &cmd);
Assert(seqoid != InvalidOid);
rel = heap_open(seqoid, AccessExclusiveLock);
***************
*** 226,231 **** DefineSequence(CreateSeqStmt *seq)
--- 231,243 ----
process_owned_by(rel, owned_by);
heap_close(rel, NoLock);
+
+ /* Call AFTER CREATE SEQUENCE triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = seqoid;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
/*
***************
*** 423,428 **** AlterSequence(AlterSeqStmt *stmt)
--- 435,441 ----
Form_pg_sequence seq;
FormData_pg_sequence new;
List *owned_by;
+ CommandContextData cmd;
/* Open and lock sequence. */
relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
***************
*** 458,463 **** AlterSequence(AlterSeqStmt *stmt)
--- 471,490 ----
/* Now okay to update the on-disk tuple */
memcpy(seq, &new, sizeof(FormData_pg_sequence));
+ /*
+ * Call BEFORE ALTER SEQUENCE triggers
+ */
+ InitCommandContext(&cmd, (Node *)stmt);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = relid;
+ cmd.objectname = pstrdup(stmt->sequence->relname);
+ cmd.schemaname = pstrdup(stmt->sequence->schemaname);
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
START_CRIT_SECTION();
MarkBufferDirty(buf);
***************
*** 496,501 **** AlterSequence(AlterSeqStmt *stmt)
--- 523,532 ----
process_owned_by(seqrel, owned_by);
relation_close(seqrel, NoLock);
+
+ /* Call AFTER ALTER SEQUENCE triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ ExecAfterCommandTriggers(&cmd);
}
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 41,46 ****
--- 41,47 ----
#include "catalog/pg_type_fn.h"
#include "catalog/storage.h"
#include "catalog/toasting.h"
+ #include "commands/cmdtrigger.h"
#include "commands/cluster.h"
#include "commands/comment.h"
#include "commands/defrem.h"
***************
*** 73,78 ****
--- 74,80 ----
#include "storage/lock.h"
#include "storage/predicate.h"
#include "storage/smgr.h"
+ #include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
***************
*** 413,419 **** static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
* ----------------------------------------------------------------
*/
Oid
! DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
{
char relname[NAMEDATALEN];
Oid namespaceId;
--- 415,421 ----
* ----------------------------------------------------------------
*/
Oid
! DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, CommandContext cmd)
{
char relname[NAMEDATALEN];
Oid namespaceId;
***************
*** 606,611 **** DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
--- 608,623 ----
}
}
+ /* Exec BEFORE CREATE view|sequence|table|type command triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = InvalidOid;
+ cmd->objectname = pstrdup(relname);
+ cmd->schemaname = get_namespace_name(namespaceId);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/*
* Create the relation. Inherited defaults and constraints are passed in
* for immediate handling --- since they don't need parsing, they can be
***************
*** 668,673 **** DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
--- 680,686 ----
*/
relation_close(rel, NoLock);
+ /* AFTER command triggers are fired from well outside this function */
return relationId;
}
***************
*** 738,743 **** RemoveRelations(DropStmt *drop)
--- 751,758 ----
ObjectAddresses *objects;
char relkind;
ListCell *cell;
+ int i = 0, n = list_length(drop->objects);
+ CommandContext *cmds = (CommandContext *) palloc(n * sizeof(CommandContext));
/*
* First we identify all the relations, then we delete them in a single
***************
*** 784,789 **** RemoveRelations(DropStmt *drop)
--- 799,805 ----
Oid relOid;
ObjectAddress obj;
struct DropRelationCallbackState state;
+ CommandContextData cmd;
/*
* These next few steps are a great deal like relation_openrv, but we
***************
*** 809,817 **** RemoveRelations(DropStmt *drop)
--- 825,849 ----
if (!OidIsValid(relOid))
{
DropErrorMsgNonExistent(rel->relname, relkind, drop->missing_ok);
+ cmds[i++] = NULL;
continue;
}
+ /*
+ * Call BEFORE DROP command triggers
+ */
+ InitCommandContext(&cmd, (Node *)drop);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = relOid;
+ cmd.objectname = get_rel_name(relOid);
+ cmd.schemaname = get_namespace_name(get_rel_namespace(relOid));
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+ cmds[i++] = &cmd;
+
/* OK, we're ready to delete this one */
obj.classId = RelationRelationId;
obj.objectId = relOid;
***************
*** 822,827 **** RemoveRelations(DropStmt *drop)
--- 854,869 ----
performMultipleDeletions(objects, drop->behavior, 0);
+ /* Call AFTER DROP command triggers */
+ for(i = 0; iobjectId = InvalidOid;
+ ExecAfterCommandTriggers(cmds[i]);
+ }
+ }
+
free_object_addresses(objects);
}
***************
*** 1139,1145 **** ExecuteTruncate(TruncateStmt *stmt)
/*
* Reconstruct the indexes to match, and we're done.
*/
! reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST);
}
}
--- 1181,1187 ----
/*
* Reconstruct the indexes to match, and we're done.
*/
! reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST, NULL);
}
}
***************
*** 2300,2306 **** RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
* renameatt - changes the name of a attribute in a relation
*/
void
! renameatt(RenameStmt *stmt)
{
Oid relid;
--- 2342,2348 ----
* renameatt - changes the name of a attribute in a relation
*/
void
! renameatt(RenameStmt *stmt, CommandContext cmd)
{
Oid relid;
***************
*** 2318,2323 **** renameatt(RenameStmt *stmt)
--- 2360,2375 ----
return;
}
+ /* BEFORE command triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = relid;
+ cmd->objectname = stmt->relation->relname;
+ cmd->schemaname = stmt->relation->schemaname;
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
renameatt_internal(relid,
stmt->subname, /* old att name */
stmt->newname, /* new att name */
***************
*** 2325,2330 **** renameatt(RenameStmt *stmt)
--- 2377,2386 ----
false, /* recursing? */
0, /* expected inhcount */
stmt->behavior);
+
+ /* AFTER command triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
***************
*** 2337,2343 **** rename_constraint_internal(Oid myrelid,
const char *newconname,
bool recurse,
bool recursing,
! int expected_parents)
{
Relation targetrelation;
Oid constraintOid;
--- 2393,2400 ----
const char *newconname,
bool recurse,
bool recursing,
! int expected_parents,
! CommandContext cmd)
{
Relation targetrelation;
Oid constraintOid;
***************
*** 2356,2361 **** rename_constraint_internal(Oid myrelid,
--- 2413,2427 ----
constraintOid);
con = (Form_pg_constraint) GETSTRUCT(tuple);
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = myrelid;
+ cmd->objectname = RelationGetRelationName(targetrelation);
+ cmd->schemaname = get_namespace_name(RelationGetNamespace(targetrelation));
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
if (con->contype == CONSTRAINT_CHECK && !con->conisonly)
{
if (recurse)
***************
*** 2376,2382 **** rename_constraint_internal(Oid myrelid,
if (childrelid == myrelid)
continue;
! rename_constraint_internal(childrelid, oldconname, newconname, false, true, numparents);
}
}
else
--- 2442,2448 ----
if (childrelid == myrelid)
continue;
! rename_constraint_internal(childrelid, oldconname, newconname, false, true, numparents, NULL);
}
}
else
***************
*** 2401,2417 **** rename_constraint_internal(Oid myrelid,
|| con->contype == CONSTRAINT_UNIQUE
|| con->contype == CONSTRAINT_EXCLUSION))
/* rename the index; this renames the constraint as well */
! RenameRelationInternal(con->conindid, newconname);
else
RenameConstraintById(constraintOid, newconname);
ReleaseSysCache(tuple);
relation_close(targetrelation, NoLock); /* close rel but keep lock */
}
void
! RenameConstraint(RenameStmt *stmt)
{
Oid relid;
--- 2467,2486 ----
|| con->contype == CONSTRAINT_UNIQUE
|| con->contype == CONSTRAINT_EXCLUSION))
/* rename the index; this renames the constraint as well */
! RenameRelationInternal(con->conindid, newconname, NULL);
else
RenameConstraintById(constraintOid, newconname);
ReleaseSysCache(tuple);
relation_close(targetrelation, NoLock); /* close rel but keep lock */
+
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
void
! RenameConstraint(RenameStmt *stmt, CommandContext cmd)
{
Oid relid;
***************
*** 2426,2439 **** RenameConstraint(RenameStmt *stmt)
stmt->newname,
interpretInhOption(stmt->relation->inhOpt), /* recursive? */
false, /* recursing? */
! 0 /* expected inhcount */);
}
/*
* Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE RENAME
*/
void
! RenameRelation(RenameStmt *stmt)
{
Oid relid;
--- 2495,2509 ----
stmt->newname,
interpretInhOption(stmt->relation->inhOpt), /* recursive? */
false, /* recursing? */
! 0 /* expected inhcount */,
! cmd);
}
/*
* Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE RENAME
*/
void
! RenameRelation(RenameStmt *stmt, CommandContext cmd)
{
Oid relid;
***************
*** 2458,2464 **** RenameRelation(RenameStmt *stmt)
}
/* Do the work */
! RenameRelationInternal(relid, stmt->newname);
}
/*
--- 2528,2534 ----
}
/* Do the work */
! RenameRelationInternal(relid, stmt->newname, cmd);
}
/*
***************
*** 2471,2477 **** 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 */
--- 2541,2547 ----
* 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 */
***************
*** 2502,2507 **** RenameRelationInternal(Oid myrelid, const char *newrelname)
--- 2572,2587 ----
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...)
***************
*** 2521,2527 **** 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.
--- 2601,2607 ----
*/
if (OidIsValid(targetrelation->rd_rel->reltype))
RenameTypeInternal(targetrelation->rd_rel->reltype,
! newrelname, namespaceId, NULL);
/*
* Also rename the associated constraint, if any.
***************
*** 2538,2543 **** RenameRelationInternal(Oid myrelid, const char *newrelname)
--- 2618,2630 ----
* Close rel, but keep exclusive lock!
*/
relation_close(targetrelation, NoLock);
+
+ /* Call AFTER ALTER relation triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectname = pstrdup(newrelname);
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
***************
*** 2636,2642 **** 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
--- 2723,2729 ----
* 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
***************
*** 2650,2663 **** void
--- 2737,2768 ----
AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt)
{
Relation rel;
+ CommandContextData cmd;
/* Caller is required to provide an adequate lock. */
rel = relation_open(relid, NoLock);
CheckTableNotInUse(rel, "ALTER TABLE");
+ /* Call BEFORE ALTER TABLE triggers */
+ InitCommandContext(&cmd, (Node *)stmt);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = relid;
+ cmd.objectname = stmt->relation->relname;
+ cmd.schemaname = get_namespace_name(
+ RangeVarGetCreationNamespace(stmt->relation));
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
ATController(rel, stmt->cmds, interpretInhOption(stmt->relation->inhOpt),
lockmode);
+
+ /* Call AFTER ALTER TABLE triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ ExecAfterCommandTriggers(&cmd);
}
/*
***************
*** 5436,5442 **** ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
check_rights,
skip_build,
quiet,
! false);
/*
* If TryReuseIndex() stashed a relfilenode for us, we used it for the new
--- 5541,5547 ----
check_rights,
skip_build,
quiet,
! false, NULL);
/*
* If TryReuseIndex() stashed a relfilenode for us, we used it for the new
***************
*** 5495,5501 **** 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 */
--- 5600,5606 ----
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 */
***************
*** 6973,6979 **** 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);
--- 7078,7084 ----
/* Right now only CHECK constraints can be inherited */
if (con->contype == CONSTRAINT_CHECK)
is_check_constraint = true;
!
if (con->conisonly)
{
Assert(is_check_constraint);
***************
*** 9757,9762 **** AlterTableNamespace(AlterObjectSchemaStmt *stmt)
--- 9862,9868 ----
Oid nspOid;
Relation classRel;
RangeVar *newrv;
+ CommandContextData cmd;
relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
stmt->missing_ok, false,
***************
*** 9797,9809 **** 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)
--- 9903,9927 ----
/* common checks on switching namespaces */
CheckSetNamespace(oldNspOid, nspOid, RelationRelationId, relid);
+ /* Call BEFORE ALTER TABLE triggers */
+ InitCommandContext(&cmd, (Node *)stmt);
+
+ 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)
***************
*** 9818,9823 **** AlterTableNamespace(AlterObjectSchemaStmt *stmt)
--- 9936,9948 ----
/* 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);
+ }
}
/*
***************
*** 9965,9971 **** 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);
--- 10090,10096 ----
* 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),
***************
*** 527,532 **** DropTableSpace(DropTableSpaceStmt *stmt)
--- 529,535 ----
/* We keep the lock on pg_tablespace until commit */
heap_close(rel, NoLock);
+
#else /* !HAVE_SYMLINK */
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
*** 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);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = InvalidOid;
+ cmd.objectname = stmt->trigname;
+ cmd.schemaname = NULL; /* triggers are not schema qualified */
+
+ 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);
***************
*** 277,282 **** DefineTSParser(List *names, List *parameters)
--- 290,302 ----
heap_freetuple(tup);
heap_close(prsRel, RowExclusiveLock);
+
+ /* Call AFTER CREATE TEXT SEARCH PARSER triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectId = prsOid;
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
***************
*** 306,312 **** RemoveTSParserById(Oid prsId)
* ALTER TEXT SEARCH PARSER RENAME
*/
void
! RenameTSParser(List *oldname, const char *newname)
{
HeapTuple tup;
Relation rel;
--- 326,332 ----
* ALTER TEXT SEARCH PARSER RENAME
*/
void
! RenameTSParser(List *oldname, const char *newname, CommandContext cmd)
{
HeapTuple tup;
Relation rel;
***************
*** 337,355 **** 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;
--- 357,392 ----
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;
***************
*** 366,372 **** AlterTSParserNamespace(List *name, const char *newschema)
prsId, nspOid,
Anum_pg_ts_parser_prsname,
Anum_pg_ts_parser_prsnamespace,
! -1, -1);
heap_close(rel, RowExclusiveLock);
}
--- 403,409 ----
prsId, nspOid,
Anum_pg_ts_parser_prsname,
Anum_pg_ts_parser_prsnamespace,
! -1, -1, cmd);
heap_close(rel, RowExclusiveLock);
}
***************
*** 384,390 **** AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid)
prsId, newNspOid,
Anum_pg_ts_parser_prsname,
Anum_pg_ts_parser_prsnamespace,
! -1, -1);
heap_close(rel, RowExclusiveLock);
--- 421,427 ----
prsId, newNspOid,
Anum_pg_ts_parser_prsname,
Anum_pg_ts_parser_prsnamespace,
! -1, -1, NULL);
heap_close(rel, RowExclusiveLock);
***************
*** 485,491 **** verify_dictoptions(Oid tmplId, List *dictoptions)
* CREATE TEXT SEARCH DICTIONARY
*/
void
! DefineTSDictionary(List *names, List *parameters)
{
ListCell *pl;
Relation dictRel;
--- 522,528 ----
* CREATE TEXT SEARCH DICTIONARY
*/
void
! DefineTSDictionary(List *names, List *parameters, CommandContext cmd)
{
ListCell *pl;
Relation dictRel;
***************
*** 538,543 **** DefineTSDictionary(List *names, List *parameters)
--- 575,592 ----
verify_dictoptions(templId, dictoptions);
/*
+ * Call BEFORE CREATE TEXT SEARCH DICTIONARY triggers
+ */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = InvalidOid;
+ cmd->objectname = pstrdup(dictname);
+ cmd->schemaname = get_namespace_name(namespaceoid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
+ /*
* Looks good, insert
*/
memset(values, 0, sizeof(values));
***************
*** 571,583 **** 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;
--- 620,639 ----
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;
***************
*** 616,634 **** 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;
--- 672,707 ----
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;
***************
*** 646,652 **** 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);
}
--- 719,725 ----
Anum_pg_ts_dict_dictname,
Anum_pg_ts_dict_dictnamespace,
Anum_pg_ts_dict_dictowner,
! ACL_KIND_TSDICTIONARY, cmd);
heap_close(rel, RowExclusiveLock);
}
***************
*** 665,671 **** 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);
--- 738,744 ----
Anum_pg_ts_dict_dictname,
Anum_pg_ts_dict_dictnamespace,
Anum_pg_ts_dict_dictowner,
! ACL_KIND_TSDICTIONARY, NULL);
heap_close(rel, RowExclusiveLock);
***************
*** 713,718 **** AlterTSDictionary(AlterTSDictionaryStmt *stmt)
--- 786,792 ----
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);
***************
*** 776,781 **** AlterTSDictionary(AlterTSDictionaryStmt *stmt)
--- 850,868 ----
verify_dictoptions(((Form_pg_ts_dict) GETSTRUCT(tup))->dicttemplate,
dictoptions);
+ /* Call BEFORE ALTER TEXT SEARCH DICTIONARY command triggers */
+ InitCommandContext(&cmd, (Node *)stmt);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ Form_pg_ts_dict form = (Form_pg_ts_dict) GETSTRUCT(tup);
+ cmd.objectId = dictId;
+ cmd.objectname = pstrdup(NameStr(form->dictname));
+ cmd.schemaname = get_namespace_name(form->dictnamespace);
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
/*
* Looks good, update
*/
***************
*** 807,819 **** AlterTSDictionary(AlterTSDictionaryStmt *stmt)
ReleaseSysCache(tup);
heap_close(rel, RowExclusiveLock);
}
/*
* ALTER TEXT SEARCH DICTIONARY OWNER
*/
void
! AlterTSDictionaryOwner(List *name, Oid newOwnerId)
{
HeapTuple tup;
Relation rel;
--- 894,910 ----
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;
***************
*** 855,860 **** AlterTSDictionaryOwner(List *name, Oid newOwnerId)
--- 946,961 ----
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);
***************
*** 863,868 **** AlterTSDictionaryOwner(List *name, Oid newOwnerId)
--- 964,973 ----
/* 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);
***************
*** 957,963 **** makeTSTemplateDependencies(HeapTuple tuple)
* CREATE TEXT SEARCH TEMPLATE
*/
void
! DefineTSTemplate(List *names, List *parameters)
{
ListCell *pl;
Relation tmplRel;
--- 1062,1068 ----
* CREATE TEXT SEARCH TEMPLATE
*/
void
! DefineTSTemplate(List *names, List *parameters, CommandContext cmd)
{
ListCell *pl;
Relation tmplRel;
***************
*** 1023,1028 **** DefineTSTemplate(List *names, List *parameters)
--- 1128,1145 ----
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
*/
***************
*** 1043,1055 **** 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;
--- 1160,1179 ----
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;
***************
*** 1081,1099 **** 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;
--- 1205,1240 ----
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;
***************
*** 1110,1116 **** AlterTSTemplateNamespace(List *name, const char *newschema)
tmplId, nspOid,
Anum_pg_ts_template_tmplname,
Anum_pg_ts_template_tmplnamespace,
! -1, -1);
heap_close(rel, RowExclusiveLock);
}
--- 1251,1257 ----
tmplId, nspOid,
Anum_pg_ts_template_tmplname,
Anum_pg_ts_template_tmplnamespace,
! -1, -1, cmd);
heap_close(rel, RowExclusiveLock);
}
***************
*** 1128,1134 **** AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid)
tmplId, newNspOid,
Anum_pg_ts_template_tmplname,
Anum_pg_ts_template_tmplnamespace,
! -1, -1);
heap_close(rel, RowExclusiveLock);
--- 1269,1275 ----
tmplId, newNspOid,
Anum_pg_ts_template_tmplname,
Anum_pg_ts_template_tmplnamespace,
! -1, -1, NULL);
heap_close(rel, RowExclusiveLock);
***************
*** 1276,1282 **** makeConfigurationDependencies(HeapTuple tuple, bool removeOld,
* CREATE TEXT SEARCH CONFIGURATION
*/
void
! DefineTSConfiguration(List *names, List *parameters)
{
Relation cfgRel;
Relation mapRel = NULL;
--- 1417,1423 ----
* CREATE TEXT SEARCH CONFIGURATION
*/
void
! DefineTSConfiguration(List *names, List *parameters, CommandContext cmd)
{
Relation cfgRel;
Relation mapRel = NULL;
***************
*** 1353,1358 **** DefineTSConfiguration(List *names, List *parameters)
--- 1494,1511 ----
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));
***************
*** 1429,1441 **** 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;
--- 1582,1601 ----
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;
***************
*** 1473,1491 **** 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;
--- 1633,1668 ----
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;
***************
*** 1503,1509 **** 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);
}
--- 1680,1686 ----
Anum_pg_ts_config_cfgname,
Anum_pg_ts_config_cfgnamespace,
Anum_pg_ts_config_cfgowner,
! ACL_KIND_TSCONFIGURATION, cmd);
heap_close(rel, RowExclusiveLock);
}
***************
*** 1522,1528 **** 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);
--- 1699,1705 ----
Anum_pg_ts_config_cfgname,
Anum_pg_ts_config_cfgnamespace,
Anum_pg_ts_config_cfgowner,
! ACL_KIND_TSCONFIGURATION, NULL);
heap_close(rel, RowExclusiveLock);
***************
*** 1581,1587 **** RemoveTSConfigurationById(Oid cfgId)
* ALTER TEXT SEARCH CONFIGURATION OWNER
*/
void
! AlterTSConfigurationOwner(List *name, Oid newOwnerId)
{
HeapTuple tup;
Relation rel;
--- 1758,1764 ----
* ALTER TEXT SEARCH CONFIGURATION OWNER
*/
void
! AlterTSConfigurationOwner(List *name, Oid newOwnerId, CommandContext cmd)
{
HeapTuple tup;
Relation rel;
***************
*** 1623,1628 **** AlterTSConfigurationOwner(List *name, Oid newOwnerId)
--- 1800,1815 ----
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);
***************
*** 1631,1638 **** AlterTSConfigurationOwner(List *name, Oid newOwnerId)
/* Update owner dependency reference */
changeDependencyOnOwner(TSConfigRelationId, HeapTupleGetOid(tup),
newOwnerId);
- }
heap_close(rel, NoLock);
heap_freetuple(tup);
}
--- 1818,1828 ----
/* 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);
}
***************
*** 1645,1650 **** AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
--- 1835,1841 ----
{
HeapTuple tup;
Relation relMap;
+ CommandContextData cmd;
/* Find the configuration */
tup = GetTSConfigTuple(stmt->cfgname);
***************
*** 1659,1664 **** AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
--- 1850,1869 ----
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION,
NameListToString(stmt->cfgname));
+ /* Call BEFORE ALTER TEXT SEARCH CONFIGURATION command triggers */
+ InitCommandContext(&cmd, (Node *)stmt);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ Form_pg_ts_config form = (Form_pg_ts_config) GETSTRUCT(tup);
+
+ cmd.objectId = HeapTupleGetOid(tup);
+ cmd.objectname = pstrdup(NameStr(form->cfgname));
+ cmd.schemaname = get_namespace_name(form->cfgnamespace);
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
relMap = heap_open(TSConfigMapRelationId, RowExclusiveLock);
/* Add or drop mappings */
***************
*** 1673,1678 **** AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
--- 1878,1887 ----
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);
+
+ 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);
+
+ 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);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = InvalidOid;
+ cmd.objectname = typeName;
+ cmd.schemaname = get_namespace_name(typeNamespace);
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
/* Allocate OID for array type */
rangeArrayOid = AssignTypeArrayOid();
***************
*** 1469,1474 **** DefineRange(CreateRangeStmt *stmt)
--- 1549,1561 ----
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
+
+ /* Call AFTER CREATE (range) TYPE triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = typoid;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
/*
***************
*** 1980,1986 **** AssignTypeArrayOid(void)
*-------------------------------------------------------------------
*/
Oid
! DefineCompositeType(RangeVar *typevar, List *coldeflist)
{
CreateStmt *createStmt = makeNode(CreateStmt);
Oid old_type_oid;
--- 2067,2074 ----
*-------------------------------------------------------------------
*/
Oid
! DefineCompositeType(RangeVar *typevar, List *coldeflist,
! CommandContext cmd)
{
CreateStmt *createStmt = makeNode(CreateStmt);
Oid old_type_oid;
***************
*** 2024,2031 **** DefineCompositeType(RangeVar *typevar, List *coldeflist)
/*
* Finally create the relation. This also creates the type.
*/
! relid = DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid);
Assert(relid != InvalidOid);
return relid;
}
--- 2112,2126 ----
/*
* Finally create the relation. This also creates the type.
*/
! relid = DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, cmd);
Assert(relid != InvalidOid);
+
+ /* Call AFTER CREATE (composite) TYPE triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectId = relid;
+ ExecAfterCommandTriggers(cmd);
+ }
return relid;
}
***************
*** 2035,2041 **** DefineCompositeType(RangeVar *typevar, List *coldeflist)
* Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements.
*/
void
! AlterDomainDefault(List *names, Node *defaultRaw)
{
TypeName *typename;
Oid domainoid;
--- 2130,2136 ----
* Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements.
*/
void
! AlterDomainDefault(List *names, Node *defaultRaw, CommandContext cmd)
{
TypeName *typename;
Oid domainoid;
***************
*** 2065,2070 **** AlterDomainDefault(List *names, Node *defaultRaw)
--- 2160,2175 ----
/* Check it's a domain and check user has permission for ALTER DOMAIN */
checkDomainOwner(tup);
+ /* Call BEFORE ALTER DOMAIN triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = pstrdup(NameStr(typTup->typname));
+ cmd->schemaname = get_namespace_name(typTup->typnamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* Setup new tuple */
MemSet(new_record, (Datum) 0, sizeof(new_record));
MemSet(new_record_nulls, false, sizeof(new_record_nulls));
***************
*** 2161,2166 **** AlterDomainDefault(List *names, Node *defaultRaw)
--- 2266,2275 ----
/* Clean up */
heap_close(rel, NoLock);
heap_freetuple(newtuple);
+
+ /* Call AFTER ALTER DOMAIN triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
/*
***************
*** 2169,2175 **** AlterDomainDefault(List *names, Node *defaultRaw)
* Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements.
*/
void
! AlterDomainNotNull(List *names, bool notNull)
{
TypeName *typename;
Oid domainoid;
--- 2278,2284 ----
* Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements.
*/
void
! AlterDomainNotNull(List *names, bool notNull, CommandContext cmd)
{
TypeName *typename;
Oid domainoid;
***************
*** 2192,2197 **** AlterDomainNotNull(List *names, bool notNull)
--- 2301,2316 ----
/* Check it's a domain and check user has permission for ALTER DOMAIN */
checkDomainOwner(tup);
+ /* Call BEFORE ALTER DOMAIN triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = pstrdup(NameStr(typTup->typname));
+ cmd->schemaname = get_namespace_name(typTup->typnamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* Is the domain already set to the desired constraint? */
if (typTup->typnotnull == notNull)
{
***************
*** 2257,2262 **** AlterDomainNotNull(List *names, bool notNull)
--- 2376,2385 ----
/* Clean up */
heap_freetuple(tup);
heap_close(typrel, RowExclusiveLock);
+
+ /* Call AFTER ALTER DOMAIN triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
/*
***************
*** 2266,2272 **** AlterDomainNotNull(List *names, bool notNull)
*/
void
AlterDomainDropConstraint(List *names, const char *constrName,
! DropBehavior behavior, bool missing_ok)
{
TypeName *typename;
Oid domainoid;
--- 2389,2395 ----
*/
void
AlterDomainDropConstraint(List *names, const char *constrName,
! DropBehavior behavior, bool missing_ok, CommandContext cmd)
{
TypeName *typename;
Oid domainoid;
***************
*** 2292,2297 **** AlterDomainDropConstraint(List *names, const char *constrName,
--- 2415,2432 ----
/* Check it's a domain and check user has permission for ALTER DOMAIN */
checkDomainOwner(tup);
+ /* Call BEFORE ALTER DOMAIN triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = pstrdup(NameStr(typTup->typname));
+ cmd->schemaname = get_namespace_name(typTup->typnamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* Grab an appropriate lock on the pg_constraint relation */
conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
***************
*** 2341,2346 **** AlterDomainDropConstraint(List *names, const char *constrName,
--- 2476,2485 ----
(errmsg("constraint \"%s\" of domain \"%s\" does not exist, skipping",
constrName, TypeNameToString(typename))));
}
+
+ /* Call AFTER ALTER DOMAIN triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
/*
***************
*** 2349,2355 **** AlterDomainDropConstraint(List *names, const char *constrName,
* Implements the ALTER DOMAIN .. ADD CONSTRAINT statement.
*/
void
! AlterDomainAddConstraint(List *names, Node *newConstraint)
{
TypeName *typename;
Oid domainoid;
--- 2488,2494 ----
* Implements the ALTER DOMAIN .. ADD CONSTRAINT statement.
*/
void
! AlterDomainAddConstraint(List *names, Node *newConstraint, CommandContext cmd)
{
TypeName *typename;
Oid domainoid;
***************
*** 2378,2383 **** AlterDomainAddConstraint(List *names, Node *newConstraint)
--- 2517,2532 ----
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(newConstraint));
+ /* Call BEFORE ALTER DOMAIN triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = pstrdup(NameStr(typTup->typname));
+ cmd->schemaname = get_namespace_name(typTup->typnamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
constr = (Constraint *) newConstraint;
switch (constr->contype)
***************
*** 2444,2449 **** AlterDomainAddConstraint(List *names, Node *newConstraint)
--- 2593,2602 ----
/* Clean up */
heap_close(typrel, RowExclusiveLock);
+
+ /* Call AFTER ALTER DOMAIN triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
/*
***************
*** 2452,2458 **** AlterDomainAddConstraint(List *names, Node *newConstraint)
* Implements the ALTER DOMAIN .. VALIDATE CONSTRAINT statement.
*/
void
! AlterDomainValidateConstraint(List *names, char *constrName)
{
TypeName *typename;
Oid domainoid;
--- 2605,2611 ----
* Implements the ALTER DOMAIN .. VALIDATE CONSTRAINT statement.
*/
void
! AlterDomainValidateConstraint(List *names, char *constrName, CommandContext cmd)
{
TypeName *typename;
Oid domainoid;
***************
*** 2525,2530 **** AlterDomainValidateConstraint(List *names, char *constrName)
--- 2678,2695 ----
HeapTupleGetOid(tuple));
conbin = TextDatumGetCString(val);
+ /* Call BEFORE ALTER DOMAIN triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = pstrdup(NameStr(typTup->typname));
+ cmd->schemaname = get_namespace_name(typTup->typnamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
validateDomainConstraint(domainoid, conbin);
/*
***************
*** 2543,2548 **** AlterDomainValidateConstraint(List *names, char *constrName)
--- 2708,2717 ----
heap_close(conrel, RowExclusiveLock);
ReleaseSysCache(tup);
+
+ /* Call AFTER ALTER DOMAIN triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
static void
***************
*** 3091,3097 **** GetDomainConstraints(Oid typeOid)
* Execute ALTER TYPE RENAME
*/
void
! RenameType(RenameStmt *stmt)
{
List *names = stmt->object;
const char *newTypeName = stmt->newname;
--- 3260,3266 ----
* Execute ALTER TYPE RENAME
*/
void
! RenameType(RenameStmt *stmt, CommandContext cmd)
{
List *names = stmt->object;
const char *newTypeName = stmt->newname;
***************
*** 3153,3162 **** RenameType(RenameStmt *stmt)
* RenameRelationInternal will call RenameTypeInternal automatically.
*/
if (typTup->typtype == TYPTYPE_COMPOSITE)
! RenameRelationInternal(typTup->typrelid, newTypeName);
else
RenameTypeInternal(typeOid, newTypeName,
! typTup->typnamespace);
/* Clean up */
heap_close(rel, RowExclusiveLock);
--- 3322,3331 ----
* RenameRelationInternal will call RenameTypeInternal automatically.
*/
if (typTup->typtype == TYPTYPE_COMPOSITE)
! RenameRelationInternal(typTup->typrelid, newTypeName, cmd);
else
RenameTypeInternal(typeOid, newTypeName,
! typTup->typnamespace, cmd);
/* Clean up */
heap_close(rel, RowExclusiveLock);
***************
*** 3166,3172 **** RenameType(RenameStmt *stmt)
* Change the owner of a type.
*/
void
! AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype)
{
TypeName *typename;
Oid typeOid;
--- 3335,3342 ----
* Change the owner of a type.
*/
void
! AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype,
! CommandContext cmd)
{
TypeName *typename;
Oid typeOid;
***************
*** 3252,3257 **** AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype)
--- 3422,3437 ----
get_namespace_name(typTup->typnamespace));
}
+ /* Call BEFORE ALTER TYPE triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = typeOid;
+ cmd->objectname = pstrdup(NameStr(typTup->typname));
+ cmd->schemaname = get_namespace_name(typTup->typnamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/*
* If it's a composite type, invoke ATExecChangeOwner so that we fix
* up the pg_class entry properly. That will call back to
***************
*** 3279,3284 **** AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype)
--- 3459,3468 ----
if (OidIsValid(typTup->typarray))
AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false);
}
+
+ /* Call AFTER ALTER TYPE triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
/* Clean up */
***************
*** 3336,3342 **** AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId,
* Execute ALTER TYPE SET SCHEMA
*/
void
! AlterTypeNamespace(List *names, const char *newschema, ObjectType objecttype)
{
TypeName *typename;
Oid typeOid;
--- 3520,3527 ----
* Execute ALTER TYPE SET SCHEMA
*/
void
! AlterTypeNamespace(List *names, const char *newschema, ObjectType objecttype,
! CommandContext cmd)
{
TypeName *typename;
Oid typeOid;
***************
*** 3356,3366 **** AlterTypeNamespace(List *names, const char *newschema, ObjectType objecttype)
/* get schema OID and check its permissions */
nspOid = LookupCreationNamespace(newschema);
! AlterTypeNamespace_oid(typeOid, nspOid);
}
Oid
! AlterTypeNamespace_oid(Oid typeOid, Oid nspOid)
{
Oid elemOid;
--- 3541,3551 ----
/* get schema OID and check its permissions */
nspOid = LookupCreationNamespace(newschema);
! AlterTypeNamespace_oid(typeOid, nspOid, cmd);
}
Oid
! AlterTypeNamespace_oid(Oid typeOid, Oid nspOid, CommandContext cmd)
{
Oid elemOid;
***************
*** 3380,3386 **** AlterTypeNamespace_oid(Oid typeOid, Oid nspOid)
format_type_be(elemOid))));
/* and do the work */
! return AlterTypeNamespaceInternal(typeOid, nspOid, false, true);
}
/*
--- 3565,3571 ----
format_type_be(elemOid))));
/* and do the work */
! return AlterTypeNamespaceInternal(typeOid, nspOid, false, true, cmd);
}
/*
***************
*** 3401,3407 **** AlterTypeNamespace_oid(Oid typeOid, Oid nspOid)
Oid
AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
bool isImplicitArray,
! bool errorOnTableType)
{
Relation rel;
HeapTuple tup;
--- 3586,3593 ----
Oid
AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
bool isImplicitArray,
! bool errorOnTableType,
! CommandContext cmd)
{
Relation rel;
HeapTuple tup;
***************
*** 3447,3452 **** AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
--- 3633,3648 ----
format_type_be(typeOid)),
errhint("Use ALTER TABLE instead.")));
+ /* Call BEFORE ALTER TYPE triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = typeOid;
+ cmd->objectname = pstrdup(NameStr(typform->typname));
+ cmd->schemaname = get_namespace_name(oldNspOid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* OK, modify the pg_type row */
/* tup is a copy, so we can scribble directly on it */
***************
*** 3504,3510 **** AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
/* Recursively alter the associated array type, if any */
if (OidIsValid(arrayOid))
! AlterTypeNamespaceInternal(arrayOid, nspOid, true, true);
return oldNspOid;
}
--- 3700,3711 ----
/* Recursively alter the associated array type, if any */
if (OidIsValid(arrayOid))
! AlterTypeNamespaceInternal(arrayOid, nspOid, true, true, NULL);
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->schemaname = get_namespace_name(nspOid);
+ ExecAfterCommandTriggers(cmd);
+ }
return oldNspOid;
}
*** a/src/backend/commands/vacuum.c
--- b/src/backend/commands/vacuum.c
***************
*** 30,35 ****
--- 30,36 ----
#include "catalog/namespace.h"
#include "catalog/pg_database.h"
#include "catalog/pg_namespace.h"
+ #include "commands/cmdtrigger.h"
#include "commands/cluster.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
***************
*** 122,127 **** vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
--- 123,147 ----
else
in_outer_xact = IsInTransactionChain(isTopLevel);
+ /* Call BEFORE VACUUM command triggers */
+ if (!IsAutoVacuumWorkerProcess())
+ {
+ CommandContextData cmd;
+ InitCommandContext(&cmd, (Node *)vacstmt);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = relid;
+
+ if (vacstmt->relation != NULL)
+ {
+ cmd.objectname = vacstmt->relation->relname;
+ cmd.schemaname = vacstmt->relation->schemaname;
+ }
+ ExecBeforeCommandTriggers(&cmd);
+ }
+ }
+
/*
* Send info about dead objects to the statistics collector, unless we are
* in autovacuum --- autovacuum.c does this for itself.
***************
*** 1054,1060 **** vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound)
/* VACUUM FULL is now a variant of CLUSTER; see cluster.c */
cluster_rel(relid, InvalidOid, false,
(vacstmt->options & VACOPT_VERBOSE) != 0,
! vacstmt->freeze_min_age, vacstmt->freeze_table_age);
}
else
lazy_vacuum_rel(onerel, vacstmt, vac_strategy);
--- 1074,1080 ----
/* VACUUM FULL is now a variant of CLUSTER; see cluster.c */
cluster_rel(relid, InvalidOid, false,
(vacstmt->options & VACOPT_VERBOSE) != 0,
! vacstmt->freeze_min_age, vacstmt->freeze_table_age, NULL);
}
else
lazy_vacuum_rel(onerel, vacstmt, vac_strategy);
*** a/src/backend/commands/view.c
--- b/src/backend/commands/view.c
***************
*** 28,33 ****
--- 28,34 ----
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteManip.h"
#include "rewrite/rewriteSupport.h"
+ #include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
***************
*** 99,105 **** isViewOnTempTable_walker(Node *node, void *context)
*/
static Oid
DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
! List *options)
{
Oid viewOid;
LOCKMODE lockmode;
--- 100,106 ----
*/
static Oid
DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
! List *options, CommandContext cmd)
{
Oid viewOid;
LOCKMODE lockmode;
***************
*** 195,200 **** DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
--- 196,211 ----
*/
Assert(relation->relpersistence == rel->rd_rel->relpersistence);
+ /* Call BEFORE CREATE VIEW triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = viewOid;
+ cmd->objectname = RelationGetRelationName(rel);
+ cmd->schemaname = get_namespace_name(RelationGetNamespace(rel));
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/*
* Create a tuple descriptor to compare against the existing view, and
* verify that the old column list is an initial prefix of the new
***************
*** 269,275 **** DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
* existing view, so we don't need more code to complain if "replace"
* is false).
*/
! relid = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid);
Assert(relid != InvalidOid);
return relid;
}
--- 280,286 ----
* existing view, so we don't need more code to complain if "replace"
* is false).
*/
! relid = DefineRelation(createStmt, RELKIND_VIEW, InvalidOid, cmd);
Assert(relid != InvalidOid);
return relid;
}
***************
*** 343,349 **** DefineViewRules(Oid viewOid, Query *viewParse, bool replace)
CMD_SELECT,
true,
replace,
! list_make1(viewParse));
/*
* Someday: automatic ON INSERT, etc
--- 354,361 ----
CMD_SELECT,
true,
replace,
! list_make1(viewParse),
! NULL);
/*
* Someday: automatic ON INSERT, etc
***************
*** 426,431 **** DefineView(ViewStmt *stmt, const char *queryString)
--- 438,444 ----
Query *viewParse;
Oid viewOid;
RangeVar *view;
+ CommandContextData cmd;
/*
* Run parse analysis to convert the raw parse tree to a Query. Note this
***************
*** 511,523 **** DefineView(ViewStmt *stmt, const char *queryString)
}
/*
* Create the view relation
*
* NOTE: if it already exists and replace is false, the xact will be
* aborted.
*/
viewOid = DefineVirtualRelation(view, viewParse->targetList,
! stmt->replace, stmt->options);
/*
* The relation we have just created is not visible to any other commands
--- 524,544 ----
}
/*
+ * Prepare BEFORE CREATE VIEW triggers
+ */
+ InitCommandContext(&cmd, (Node *)stmt);
+
+ /*
* Create the view relation
*
* NOTE: if it already exists and replace is false, the xact will be
* aborted.
*/
viewOid = DefineVirtualRelation(view, viewParse->targetList,
! stmt->replace, stmt->options, &cmd);
!
! if (!OidIsValid(viewOid))
! return;
/*
* The relation we have just created is not visible to any other commands
***************
*** 536,539 **** DefineView(ViewStmt *stmt, const char *queryString)
--- 557,567 ----
* Now create the rules associated with the view.
*/
DefineViewRules(viewOid, viewParse, stmt->replace);
+
+ /* Call AFTER CREATE VIEW triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = viewOid;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 3445,3450 **** _copyCreateTrigStmt(const CreateTrigStmt *from)
--- 3445,3473 ----
return newnode;
}
+ static CreateCmdTrigStmt *
+ _copyCreateCmdTrigStmt(const CreateCmdTrigStmt *from)
+ {
+ CreateCmdTrigStmt *newnode = makeNode(CreateCmdTrigStmt);
+
+ COPY_STRING_FIELD(trigname);
+ COPY_SCALAR_FIELD(timing);
+ COPY_NODE_FIELD(funcname);
+
+ return newnode;
+ }
+
+ static AlterCmdTrigStmt *
+ _copyAlterCmdTrigStmt(const AlterCmdTrigStmt *from)
+ {
+ AlterCmdTrigStmt *newnode = makeNode(AlterCmdTrigStmt);
+
+ COPY_STRING_FIELD(trigname);
+ COPY_STRING_FIELD(tgenabled);
+
+ return newnode;
+ }
+
static CreatePLangStmt *
_copyCreatePLangStmt(const CreatePLangStmt *from)
{
***************
*** 3674,3680 **** _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)); \
--- 3697,3703 ----
/*
* 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)); \
***************
*** 4294,4299 **** copyObject(const void *from)
--- 4317,4328 ----
case T_CreateTrigStmt:
retval = _copyCreateTrigStmt(from);
break;
+ case T_CreateCmdTrigStmt:
+ retval = _copyCreateCmdTrigStmt(from);
+ break;
+ case T_AlterCmdTrigStmt:
+ retval = _copyAlterCmdTrigStmt(from);
+ break;
case T_CreatePLangStmt:
retval = _copyCreatePLangStmt(from);
break;
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 1778,1783 **** _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
--- 1778,1802 ----
}
static bool
+ _equalCreateCmdTrigStmt(const CreateCmdTrigStmt *a, const CreateCmdTrigStmt *b)
+ {
+ COMPARE_STRING_FIELD(trigname);
+ COMPARE_SCALAR_FIELD(timing);
+ COMPARE_NODE_FIELD(funcname);
+
+ return true;
+ }
+
+ static bool
+ _equalAlterCmdTrigStmt(const AlterCmdTrigStmt *a, const AlterCmdTrigStmt *b)
+ {
+ COMPARE_STRING_FIELD(trigname);
+ COMPARE_STRING_FIELD(tgenabled);
+
+ return true;
+ }
+
+ static bool
_equalCreatePLangStmt(const CreatePLangStmt *a, const CreatePLangStmt *b)
{
COMPARE_SCALAR_FIELD(replace);
***************
*** 2853,2858 **** equal(const void *a, const void *b)
--- 2872,2883 ----
case T_CreateTrigStmt:
retval = _equalCreateTrigStmt(a, b);
break;
+ case T_CreateCmdTrigStmt:
+ retval = _equalCreateCmdTrigStmt(a, b);
+ break;
+ case T_AlterCmdTrigStmt:
+ retval = _equalAlterCmdTrigStmt(a, b);
+ break;
case T_CreatePLangStmt:
retval = _equalCreatePLangStmt(a, b);
break;
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
***************
*** 255,260 **** _readDeclareCursorStmt(void)
--- 255,276 ----
}
/*
+ * _readCreateCmdTrigStmt
+ */
+ static CreateCmdTrigStmt *
+ _readCreateCmdTrigStmt(void)
+ {
+ READ_LOCALS(CreateCmdTrigStmt);
+
+ READ_NODE_FIELD(command);
+ READ_STRING_FIELD(trigname);
+ READ_CHAR_FIELD(timing);
+ READ_NODE_FIELD(funcname);
+
+ READ_DONE();
+ }
+
+ /*
* _readSortGroupClause
*/
static SortGroupClause *
***************
*** 1255,1260 **** parseNodeString(void)
--- 1271,1278 ----
if (MATCH("QUERY", 5))
return_value = _readQuery();
+ else if (MATCH("CREATECMDTRIGSTMT", 17))
+ return_value = _readCreateCmdTrigStmt();
else if (MATCH("SORTGROUPCLAUSE", 15))
return_value = _readSortGroupClause();
else if (MATCH("WINDOWCLAUSE", 12))
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 53,58 ****
--- 53,59 ----
#include "catalog/index.h"
#include "catalog/namespace.h"
+ #include "catalog/pg_cmdtrigger.h"
#include "catalog/pg_trigger.h"
#include "commands/defrem.h"
#include "nodes/makefuncs.h"
***************
*** 195,200 **** static void processCASbits(int cas_bits, int location, const char *constrType,
--- 196,202 ----
}
%type stmt schema_stmt
+ AlterCmdTrigStmt
AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
***************
*** 208,219 **** static void processCASbits(int cas_bits, int location, const char *constrType,
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
! CreateAssertStmt CreateTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
! DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
! DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
LockStmt NotifyStmt ExplainableStmt PreparableStmt
--- 210,221 ----
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
! CreateAssertStmt CreateTrigStmt CreateCmdTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
! DropAssertStmt DropTrigStmt DropCmdTrigStmt DropRuleStmt DropCastStmt
! DropRoleStmt DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
LockStmt NotifyStmt ExplainableStmt PreparableStmt
***************
*** 264,273 **** static void processCASbits(int cas_bits, int location, const char *constrType,
%type OptSchemaEltList
%type TriggerForSpec TriggerForType
! %type TriggerActionTime
%type TriggerEvents TriggerOneEvent
%type TriggerFuncArg
%type TriggerWhen
%type copy_file_name
database_name access_method_clause access_method attr_name
--- 266,276 ----
%type OptSchemaEltList
%type TriggerForSpec TriggerForType
! %type TriggerActionTime CmdTriggerActionTime
%type TriggerEvents TriggerOneEvent
%type TriggerFuncArg
%type TriggerWhen
+ %type trigger_command enable_trigger
%type copy_file_name
database_name access_method_clause access_method attr_name
***************
*** 495,501 **** static void processCASbits(int cas_bits, int location, const char *constrType,
CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
! CLUSTER COALESCE COLLATE COLLATION COLUMN COMMENT COMMENTS COMMIT
COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS
CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
CROSS CSV CURRENT_P
--- 498,504 ----
CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
! CLUSTER COALESCE COLLATE COLLATION COLUMN COMMAND COMMENT COMMENTS COMMIT
COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS
CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
CROSS CSV CURRENT_P
***************
*** 675,681 **** stmtmulti: stmtmulti ';' stmt
;
stmt :
! AlterDatabaseStmt
| AlterDatabaseSetStmt
| AlterDefaultPrivilegesStmt
| AlterDomainStmt
--- 678,685 ----
;
stmt :
! AlterCmdTrigStmt
! | AlterDatabaseStmt
| AlterDatabaseSetStmt
| AlterDefaultPrivilegesStmt
| AlterDomainStmt
***************
*** 726,731 **** stmt :
--- 730,736 ----
| CreateStmt
| CreateTableSpaceStmt
| CreateTrigStmt
+ | CreateCmdTrigStmt
| CreateRoleStmt
| CreateUserStmt
| CreateUserMappingStmt
***************
*** 749,754 **** stmt :
--- 754,760 ----
| DropStmt
| DropTableSpaceStmt
| DropTrigStmt
+ | DropCmdTrigStmt
| DropRoleStmt
| DropUserStmt
| DropUserMappingStmt
***************
*** 4260,4265 **** DropTrigStmt:
--- 4266,4436 ----
/*****************************************************************************
*
* QUERIES :
+ * CREATE COMMAND TRIGGER ... BEFORE|AFTER COMMAND ...
+ * DROP COMMAND TRIGGER ... BEFORE|AFTER ...
+ *
+ *****************************************************************************/
+
+ CreateCmdTrigStmt:
+ CREATE COMMAND TRIGGER name CmdTriggerActionTime trigger_command
+ EXECUTE PROCEDURE func_name '(' ')'
+ {
+ CreateCmdTrigStmt *n = makeNode(CreateCmdTrigStmt);
+ n->trigname = $4;
+ n->timing = $5;
+ n->command = $6;
+ n->funcname = $9;
+ $$ = (Node *)n;
+ }
+ | CREATE COMMAND TRIGGER name CmdTriggerActionTime ANY COMMAND
+ EXECUTE PROCEDURE func_name '(' ')'
+ {
+ CreateCmdTrigStmt *n = makeNode(CreateCmdTrigStmt);
+ n->trigname = $4;
+ n->timing = $5;
+ n->command = "ANY";
+ n->funcname = $10;
+ $$ = (Node *)n;
+ }
+ ;
+
+ CmdTriggerActionTime:
+ BEFORE { $$ = CMD_TRIGGER_FIRED_BEFORE; }
+ | AFTER { $$ = CMD_TRIGGER_FIRED_AFTER; }
+ ;
+
+ /*
+ * that will get matched against what CreateCommandTag returns
+ *
+ * we don't support Command Triggers on every possible command that PostgreSQL
+ * supports, this list should match with the implementation.
+ */
+ trigger_command:
+ CREATE SCHEMA { $$ = "CREATE SCHEMA"; }
+ | CREATE EXTENSION { $$ = "CREATE EXTENSION"; }
+ | CREATE LANGUAGE { $$ = "CREATE LANGUAGE"; }
+ | CREATE FUNCTION { $$ = "CREATE FUNCTION"; }
+ | CREATE TABLE { $$ = "CREATE TABLE"; }
+ | CREATE SERVER { $$ = "CREATE SERVER"; }
+ | CREATE FOREIGN TABLE { $$ = "CREATE FOREIGN TABLE"; }
+ | CREATE FOREIGN DATA_P WRAPPER { $$ = "CREATE FOREIGN DATA WRAPPER"; }
+ | CREATE USER MAPPING { $$ = "CREATE USER MAPPING"; }
+ | CREATE INDEX { $$ = "CREATE INDEX"; }
+ | CREATE SEQUENCE { $$ = "CREATE SEQUENCE"; }
+ | CREATE VIEW { $$ = "CREATE VIEW"; }
+ | CREATE RULE { $$ = "CREATE RULE"; }
+ | CREATE AGGREGATE { $$ = "CREATE AGGREGATE"; }
+ | CREATE OPERATOR { $$ = "CREATE OPERATOR"; }
+ | CREATE COLLATION { $$ = "CREATE COLLATION"; }
+ | CREATE TEXT_P SEARCH PARSER { $$ = "CREATE TEXT SEARCH PARSER"; }
+ | CREATE TEXT_P SEARCH DICTIONARY { $$ = "CREATE TEXT SEARCH DICTIONARY"; }
+ | CREATE TEXT_P SEARCH TEMPLATE { $$ = "CREATE TEXT SEARCH TEMPLATE"; }
+ | CREATE TEXT_P SEARCH CONFIGURATION { $$ = "CREATE TEXT SEARCH CONFIGURATION"; }
+ | CREATE TYPE_P { $$ = "CREATE TYPE"; }
+ | CREATE DOMAIN_P { $$ = "CREATE DOMAIN"; }
+ | CREATE TRIGGER { $$ = "CREATE TRIGGER"; }
+ | CREATE CONVERSION_P { $$ = "CREATE CONVERSION"; }
+ | CREATE CAST { $$ = "CREATE CAST"; }
+ | CREATE OPERATOR CLASS { $$ = "CREATE OPERATOR CLASS"; }
+ | CREATE OPERATOR FAMILY { $$ = "CREATE OPERATOR FAMILY"; }
+ | ALTER SCHEMA { $$ = "ALTER SCHEMA"; }
+ | ALTER EXTENSION { $$ = "ALTER EXTENSION"; }
+ | ALTER LANGUAGE { $$ = "ALTER LANGUAGE"; }
+ | ALTER FUNCTION { $$ = "ALTER FUNCTION"; }
+ | ALTER TABLE { $$ = "ALTER TABLE"; }
+ | ALTER SERVER { $$ = "ALTER SERVER"; }
+ | ALTER FOREIGN TABLE { $$ = "ALTER FOREIGN TABLE"; }
+ | ALTER FOREIGN DATA_P WRAPPER { $$ = "ALTER FOREIGN DATA WRAPPER"; }
+ | ALTER USER MAPPING { $$ = "ALTER USER MAPPING"; }
+ | ALTER SEQUENCE { $$ = "ALTER SEQUENCE"; }
+ | ALTER VIEW { $$ = "ALTER VIEW"; }
+ | ALTER AGGREGATE { $$ = "ALTER AGGREGATE"; }
+ | ALTER OPERATOR { $$ = "ALTER OPERATOR"; }
+ | ALTER OPERATOR CLASS { $$ = "ALTER OPERATOR CLASS"; }
+ | ALTER OPERATOR FAMILY { $$ = "ALTER OPERATOR FAMILY"; }
+ | ALTER COLLATION { $$ = "ALTER COLLATION"; }
+ | ALTER TEXT_P SEARCH PARSER { $$ = "ALTER TEXT SEARCH PARSER"; }
+ | ALTER TEXT_P SEARCH DICTIONARY { $$ = "ALTER TEXT SEARCH DICTIONARY"; }
+ | ALTER TEXT_P SEARCH TEMPLATE { $$ = "ALTER TEXT SEARCH TEMPLATE"; }
+ | ALTER TEXT_P SEARCH CONFIGURATION { $$ = "ALTER TEXT SEARCH CONFIGURATION"; }
+ | ALTER TYPE_P { $$ = "ALTER TYPE"; }
+ | ALTER DOMAIN_P { $$ = "ALTER DOMAIN"; }
+ | ALTER TRIGGER { $$ = "ALTER TRIGGER"; }
+ | ALTER CONVERSION_P { $$ = "ALTER CONVERSION"; }
+ | DROP TABLE { $$ = "DROP TABLE"; }
+ | DROP SEQUENCE { $$ = "DROP SEQUENCE"; }
+ | DROP VIEW { $$ = "DROP VIEW"; }
+ | DROP INDEX { $$ = "DROP INDEX"; }
+ | DROP TYPE_P { $$ = "DROP TYPE"; }
+ | DROP DOMAIN_P { $$ = "DROP DOMAIN"; }
+ | DROP COLLATION { $$ = "DROP COLLATION"; }
+ | DROP CONVERSION_P { $$ = "DROP CONVERSION"; }
+ | DROP SCHEMA { $$ = "DROP SCHEMA"; }
+ | DROP EXTENSION { $$ = "DROP EXTENSION"; }
+ | DROP TEXT_P SEARCH PARSER { $$ = "DROP TEXT SEARCH PARSER"; }
+ | DROP TEXT_P SEARCH DICTIONARY { $$ = "DROP TEXT SEARCH DICTIONARY"; }
+ | DROP TEXT_P SEARCH TEMPLATE { $$ = "DROP TEXT SEARCH TEMPLATE"; }
+ | DROP TEXT_P SEARCH CONFIGURATION { $$ = "DROP TEXT SEARCH CONFIGURATION"; }
+ | DROP LANGUAGE { $$ = "DROP LANGUAGE"; }
+ | DROP SERVER { $$ = "DROP SERVER"; }
+ | DROP FOREIGN TABLE { $$ = "DROP FOREIGN TABLE"; }
+ | DROP FOREIGN DATA_P WRAPPER { $$ = "DROP FOREIGN DATA WRAPPER"; }
+ | DROP USER MAPPING { $$ = "DROP USER MAPPING"; }
+ | DROP TRIGGER { $$ = "DROP TRIGGER"; }
+ | DROP OPERATOR CLASS { $$ = "DROP OPERATOR CLASS"; }
+ | DROP OPERATOR FAMILY { $$ = "DROP OPERATOR FAMILY"; }
+ | DROP FUNCTION { $$ = "DROP FUNCTION"; }
+ | DROP AGGREGATE { $$ = "DROP AGGREGATE"; }
+ | DROP OPERATOR { $$ = "DROP OPERATOR"; }
+ | DROP CAST { $$ = "DROP CAST"; }
+ | DROP RULE { $$ = "DROP RULE"; }
+ | REINDEX { $$ = "REINDEX"; }
+ | VACUUM { $$ = "VACUUM"; }
+ | CLUSTER { $$ = "CLUSTER"; }
+ | LOAD { $$ = "LOAD"; }
+ ;
+
+ DropCmdTrigStmt:
+ DROP COMMAND TRIGGER name opt_drop_behavior
+ {
+ DropStmt *n = makeNode(DropStmt);
+ n->removeType = OBJECT_CMDTRIGGER;
+ n->objects = list_make1(list_make1(makeString($4)));
+ n->behavior = $5;
+ n->missing_ok = false;
+ $$ = (Node *) n;
+ }
+ | DROP COMMAND TRIGGER IF_P EXISTS name opt_drop_behavior
+ {
+ DropStmt *n = makeNode(DropStmt);
+ n->removeType = OBJECT_CMDTRIGGER;
+ n->objects = list_make1(list_make1(makeString($6)));
+ n->behavior = $7;
+ n->missing_ok = true;
+ $$ = (Node *) n;
+ }
+ ;
+
+ AlterCmdTrigStmt:
+ ALTER COMMAND TRIGGER name SET enable_trigger
+ {
+ AlterCmdTrigStmt *n = makeNode(AlterCmdTrigStmt);
+ n->trigname = $4;
+ n->tgenabled = $6;
+ $$ = (Node *) n;
+ }
+ ;
+
+ enable_trigger:
+ ENABLE_P { $$ = "O"; }
+ | ENABLE_P REPLICA { $$ = "R"; }
+ | ENABLE_P ALWAYS { $$ = "A"; }
+ | DISABLE_P { $$ = "D"; }
+ ;
+
+ /*****************************************************************************
+ *
+ * QUERIES :
* CREATE ASSERTION ...
* DROP ASSERTION ...
*
***************
*** 6773,6778 **** RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
--- 6944,6957 ----
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER COMMAND TRIGGER name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_CMDTRIGGER;
+ n->object = list_make1(makeString($4));
+ n->newname = $7;
+ $$ = (Node *)n;
+ }
| ALTER ROLE RoleId RENAME TO RoleId
{
RenameStmt *n = makeNode(RenameStmt);
*** a/src/backend/rewrite/rewriteDefine.c
--- b/src/backend/rewrite/rewriteDefine.c
***************
*** 195,204 **** DefineRule(RuleStmt *stmt, const char *queryString)
--- 195,208 ----
List *actions;
Node *whereClause;
Oid relId;
+ CommandContextData cmd;
/* Parse analysis. */
transformRuleStmt(stmt, queryString, &actions, &whereClause);
+ /* Prepare command context */
+ InitCommandContext(&cmd, (Node *)stmt);
+
/*
* Find and lock the relation. Lock level should match
* DefineQueryRewrite.
***************
*** 212,218 **** DefineRule(RuleStmt *stmt, const char *queryString)
stmt->event,
stmt->instead,
stmt->replace,
! actions);
}
--- 216,223 ----
stmt->event,
stmt->instead,
stmt->replace,
! actions,
! &cmd);
}
***************
*** 230,237 **** DefineQueryRewrite(char *rulename,
CmdType event_type,
bool is_instead,
bool replace,
! List *action)
{
Relation event_relation;
int event_attno;
ListCell *l;
--- 235,244 ----
CmdType event_type,
bool is_instead,
bool replace,
! List *action,
! CommandContext cmd)
{
+ Oid ruleOid;
Relation event_relation;
int event_attno;
ListCell *l;
***************
*** 480,485 **** DefineQueryRewrite(char *rulename,
--- 487,502 ----
}
}
+ /* Call BEFORE CREATE RULE triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = InvalidOid;
+ cmd->objectname = rulename;
+ cmd->schemaname = NULL; /* rules are not schema qualified */
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/*
* This rule is allowed - prepare to install it.
*/
***************
*** 488,501 **** DefineQueryRewrite(char *rulename,
/* discard rule if it's null action and not INSTEAD; it's a no-op */
if (action != NIL || is_instead)
{
! InsertRule(rulename,
! event_type,
! event_relid,
! event_attno,
! is_instead,
! event_qual,
! action,
! replace);
/*
* Set pg_class 'relhasrules' field TRUE for event relation. If
--- 505,518 ----
/* discard rule if it's null action and not INSTEAD; it's a no-op */
if (action != NIL || is_instead)
{
! ruleOid = InsertRule(rulename,
! event_type,
! event_relid,
! event_attno,
! is_instead,
! event_qual,
! action,
! replace);
/*
* Set pg_class 'relhasrules' field TRUE for event relation. If
***************
*** 520,525 **** DefineQueryRewrite(char *rulename,
--- 537,549 ----
/* Close rel, but keep lock till commit... */
heap_close(event_relation, NoLock);
+
+ /* Call AFTER CREATE RULE triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectId = ruleOid;
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 25,30 ****
--- 25,31 ----
#include "commands/alter.h"
#include "commands/async.h"
#include "commands/cluster.h"
+ #include "commands/cmdtrigger.h"
#include "commands/comment.h"
#include "commands/collationcmds.h"
#include "commands/conversioncmds.h"
***************
*** 58,66 ****
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/guc.h"
#include "utils/syscache.h"
-
/* Hook for plugins to get control in ProcessUtility() */
ProcessUtility_hook_type ProcessUtility_hook = NULL;
--- 59,67 ----
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/guc.h"
+ #include "utils/lsyscache.h"
#include "utils/syscache.h"
/* Hook for plugins to get control in ProcessUtility() */
ProcessUtility_hook_type ProcessUtility_hook = NULL;
***************
*** 184,189 **** check_xact_readonly(Node *parsetree)
--- 185,192 ----
case T_CommentStmt:
case T_DefineStmt:
case T_CreateCastStmt:
+ case T_CreateCmdTrigStmt:
+ case T_AlterCmdTrigStmt:
case T_CreateConversionStmt:
case T_CreatedbStmt:
case T_CreateDomainStmt:
***************
*** 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)
--- 512,526 ----
{
List *stmts;
ListCell *l;
! Oid relOid = InvalidOid;
! CreateStmt *stmt = (CreateStmt *) parsetree;
! CommandContextData cmd;
!
! /* Prepare BEFORE CREATE TABLE triggers */
! InitCommandContext(&cmd, parsetree);
/* Run parse analysis ... */
! stmts = transformCreateStmt(stmt, queryString);
/* ... and do it */
foreach(l, stmts)
***************
*** 528,534 **** standard_ProcessUtility(Node *parsetree,
/* Create the table itself */
relOid = DefineRelation((CreateStmt *) stmt,
RELKIND_RELATION,
! InvalidOid);
/*
* Let AlterTableCreateToastTable decide if this one
--- 535,542 ----
/* Create the table itself */
relOid = DefineRelation((CreateStmt *) stmt,
RELKIND_RELATION,
! InvalidOid,
! &cmd);
/*
* Let AlterTableCreateToastTable decide if this one
***************
*** 552,558 **** standard_ProcessUtility(Node *parsetree,
/* Create the table itself */
relOid = DefineRelation((CreateStmt *) stmt,
RELKIND_FOREIGN_TABLE,
! InvalidOid);
CreateForeignTable((CreateForeignTableStmt *) stmt,
relOid);
}
--- 560,567 ----
/* Create the table itself */
relOid = DefineRelation((CreateStmt *) stmt,
RELKIND_FOREIGN_TABLE,
! InvalidOid,
! &cmd);
CreateForeignTable((CreateForeignTableStmt *) stmt,
relOid);
}
***************
*** 571,576 **** standard_ProcessUtility(Node *parsetree,
--- 580,592 ----
if (lnext(l) != NULL)
CommandCounterIncrement();
}
+
+ /* Call AFTER CREATE TABLE triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = relOid;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
break;
***************
*** 755,760 **** standard_ProcessUtility(Node *parsetree,
--- 771,780 ----
case T_AlterDomainStmt:
{
AlterDomainStmt *stmt = (AlterDomainStmt *) parsetree;
+ CommandContextData cmd;
+
+ /* Prepare BEFORE ALTER DOMAIN triggers */
+ InitCommandContext(&cmd, parsetree);
/*
* 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",
--- 789,818 ----
* 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",
--- 840,882 ----
case T_DefineStmt:
{
DefineStmt *stmt = (DefineStmt *) parsetree;
+ CommandContextData cmd;
+
+ InitCommandContext(&cmd, parsetree);
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;
--- 889,898 ----
case T_CompositeTypeStmt: /* CREATE TYPE (composite) */
{
CompositeTypeStmt *stmt = (CompositeTypeStmt *) parsetree;
+ CommandContextData cmd;
! InitCommandContext(&cmd, parsetree);
! DefineCompositeType(stmt->typevar, stmt->coldeflist, &cmd);
}
break;
***************
*** 904,909 **** standard_ProcessUtility(Node *parsetree,
--- 930,938 ----
case T_IndexStmt: /* CREATE INDEX */
{
IndexStmt *stmt = (IndexStmt *) parsetree;
+ CommandContextData cmd;
+
+ InitCommandContext(&cmd, parsetree);
if (stmt->concurrent)
PreventTransactionChain(isTopLevel,
***************
*** 934,940 **** standard_ProcessUtility(Node *parsetree,
true, /* check_rights */
false, /* skip_build */
false, /* quiet */
! stmt->concurrent); /* concurrent */
}
break;
--- 963,970 ----
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;
--- 1002,1008 ----
DropdbStmt *stmt = (DropdbStmt *) parsetree;
PreventTransactionChain(isTopLevel, "DROP DATABASE");
! dropdb(stmt);
}
break;
***************
*** 1012,1021 **** standard_ProcessUtility(Node *parsetree,
--- 1042,1065 ----
case T_LoadStmt:
{
LoadStmt *stmt = (LoadStmt *) parsetree;
+ CommandContextData cmd;
+
+ InitCommandContext(&cmd, parsetree);
+
+ 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,
--- 1103,1116 ----
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:
--- 1183,1201 ----
case T_ReindexStmt:
{
ReindexStmt *stmt = (ReindexStmt *) parsetree;
+ CommandContextData cmd;
+
+ InitCommandContext(&cmd, parsetree);
/* 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:
***************
*** 1435,1440 **** AlterObjectTypeCommandTag(ObjectType objtype)
--- 1490,1498 ----
case OBJECT_TRIGGER:
tag = "ALTER TRIGGER";
break;
+ case OBJECT_CMDTRIGGER:
+ tag = "ALTER COMMAND TRIGGER";
+ break;
case OBJECT_TSCONFIGURATION:
tag = "ALTER TEXT SEARCH CONFIGURATION";
break;
***************
*** 1704,1709 **** CreateCommandTag(Node *parsetree)
--- 1762,1770 ----
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)
--- 2011,2024 ----
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)
--- 2220,2232 ----
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)
--- 2521,2534 ----
lev = LOGSTMT_DDL;
break;
+ case T_CreateCmdTrigStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
+ case T_AlterCmdTrigStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_CreatePLangStmt:
lev = LOGSTMT_DDL;
break;
*** a/src/backend/utils/adt/format_type.c
--- b/src/backend/utils/adt/format_type.c
***************
*** 28,34 ****
#define MAX_INT32_LEN 11
static char *format_type_internal(Oid type_oid, int32 typemod,
! bool typemod_given, bool allow_invalid);
static char *printTypmod(const char *typname, int32 typmod, Oid typmodout);
static char *
psnprintf(size_t len, const char *fmt,...)
--- 28,35 ----
#define MAX_INT32_LEN 11
static char *format_type_internal(Oid type_oid, int32 typemod,
! bool typemod_given, bool allow_invalid,
! bool qualify);
static char *printTypmod(const char *typname, int32 typmod, Oid typmodout);
static char *
psnprintf(size_t len, const char *fmt,...)
***************
*** 76,86 **** format_type(PG_FUNCTION_ARGS)
type_oid = PG_GETARG_OID(0);
if (PG_ARGISNULL(1))
! result = format_type_internal(type_oid, -1, false, true);
else
{
typemod = PG_GETARG_INT32(1);
! result = format_type_internal(type_oid, typemod, true, true);
}
PG_RETURN_TEXT_P(cstring_to_text(result));
--- 77,87 ----
type_oid = PG_GETARG_OID(0);
if (PG_ARGISNULL(1))
! result = format_type_internal(type_oid, -1, false, true, true);
else
{
typemod = PG_GETARG_INT32(1);
! result = format_type_internal(type_oid, typemod, true, true, true);
}
PG_RETURN_TEXT_P(cstring_to_text(result));
***************
*** 95,101 **** format_type(PG_FUNCTION_ARGS)
char *
format_type_be(Oid type_oid)
{
! return format_type_internal(type_oid, -1, false, false);
}
/*
--- 96,113 ----
char *
format_type_be(Oid type_oid)
{
! return format_type_internal(type_oid, -1, false, false, true);
! }
!
! /*
! * Allow formating a type name without namespace, useful for command context
! * where we probide object name and namespace separately and still want nice
! * formating of type names.
! */
! char *
! format_type_be_without_namespace(Oid type_oid)
! {
! return format_type_internal(type_oid, -1, false, false, false);
}
/*
***************
*** 104,117 **** format_type_be(Oid type_oid)
char *
format_type_with_typemod(Oid type_oid, int32 typemod)
{
! return format_type_internal(type_oid, typemod, true, false);
}
static char *
format_type_internal(Oid type_oid, int32 typemod,
! bool typemod_given, bool allow_invalid)
{
bool with_typemod = typemod_given && (typemod >= 0);
HeapTuple tuple;
--- 116,129 ----
char *
format_type_with_typemod(Oid type_oid, int32 typemod)
{
! return format_type_internal(type_oid, typemod, true, false, true);
}
static char *
format_type_internal(Oid type_oid, int32 typemod,
! bool typemod_given, bool allow_invalid, bool qualify)
{
bool with_typemod = typemod_given && (typemod >= 0);
HeapTuple tuple;
***************
*** 299,305 **** format_type_internal(Oid type_oid, int32 typemod,
char *nspname;
char *typname;
! if (TypeIsVisible(type_oid))
nspname = NULL;
else
nspname = get_namespace_name(typeform->typnamespace);
--- 311,319 ----
char *nspname;
char *typname;
! if (!qualify)
! nspname = NULL;
! else if (TypeIsVisible(type_oid))
nspname = NULL;
else
nspname = get_namespace_name(typeform->typnamespace);
***************
*** 420,426 **** oidvectortypes(PG_FUNCTION_ARGS)
for (num = 0; num < numargs; num++)
{
char *typename = format_type_internal(oidArray->values[num], -1,
! false, true);
size_t slen = strlen(typename);
if (left < (slen + 2))
--- 434,440 ----
for (num = 0; num < numargs; num++)
{
char *typename = format_type_internal(oidArray->values[num], -1,
! false, true, true);
size_t slen = strlen(typename);
if (left < (slen + 2))
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
***************
*** 39,47 ****
--- 39,49 ----
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/tlist.h"
+ #include "parser/analyze.h"
#include "parser/keywords.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
+ #include "parser/parse_type.h"
#include "parser/parser.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h"
***************
*** 259,265 **** static char *flatten_reloptions(Oid relid);
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
-
/* ----------
* get_ruledef - Do it all and return a text
* that could be used as a statement
--- 261,266 ----
*** a/src/bin/pg_dump/common.c
--- b/src/bin/pg_dump/common.c
***************
*** 98,103 **** getSchemaData(Archive *fout, int *numTablesPtr)
--- 98,104 ----
int numForeignDataWrappers;
int numForeignServers;
int numDefaultACLs;
+ int numCmdTriggers;
if (g_verbose)
write_msg(NULL, "reading schemas\n");
***************
*** 234,239 **** getSchemaData(Archive *fout, int *numTablesPtr)
--- 235,244 ----
write_msg(NULL, "reading triggers\n");
getTriggers(fout, tblinfo, numTables);
+ if (g_verbose)
+ write_msg(NULL, "reading command triggers\n");
+ getCmdTriggers(fout, &numCmdTriggers);
+
*numTablesPtr = numTables;
return tblinfo;
}
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 48,53 ****
--- 48,54 ----
#include "access/transam.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_class.h"
+ #include "catalog/pg_cmdtrigger.h"
#include "catalog/pg_default_acl.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
***************
*** 191,196 **** static void dumpConversion(Archive *fout, ConvInfo *convinfo);
--- 192,198 ----
static void dumpRule(Archive *fout, RuleInfo *rinfo);
static void dumpAgg(Archive *fout, AggInfo *agginfo);
static void dumpTrigger(Archive *fout, TriggerInfo *tginfo);
+ static void dumpCmdTrigger(Archive *fout, CmdTriggerInfo *ctginfo);
static void dumpTable(Archive *fout, TableInfo *tbinfo);
static void dumpTableSchema(Archive *fout, TableInfo *tbinfo);
static void dumpAttrDef(Archive *fout, AttrDefInfo *adinfo);
***************
*** 5285,5290 **** getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
--- 5287,5363 ----
}
/*
+ * getCmdTriggers
+ * get information about every command trigger on a dumpable table
+ */
+ CmdTriggerInfo *
+ getCmdTriggers(Archive *fout, int *numCmdTriggers)
+ {
+ int i;
+ PQExpBuffer query = createPQExpBuffer();
+ PGresult *res;
+ CmdTriggerInfo *ctginfo;
+ int i_tableoid,
+ i_oid,
+ i_ctgcommand,
+ i_ctgname,
+ i_ctgfname,
+ i_ctgtype,
+ i_ctgenabled;
+ int ntups;
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(fout, "pg_catalog");
+
+ if (fout->remoteVersion >= 90200)
+ {
+ appendPQExpBuffer(query,
+ "SELECT c.tableoid, c.oid, "
+ "ctgname, ctgtype, ctgcommand, "
+ "n.nspname || '.' || p.proname as ctgfname, ctgenabled "
+ "FROM pg_cmdtrigger c JOIN pg_proc p on c.ctgfoid = p.oid "
+ "JOIN pg_namespace n ON p.pronamespace = n.oid "
+ "ORDER BY c.oid");
+ }
+
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ *numCmdTriggers = ntups;
+
+ ctginfo = (CmdTriggerInfo *) pg_malloc(ntups * sizeof(CmdTriggerInfo));
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_ctgname = PQfnumber(res, "ctgname");
+ i_ctgtype = PQfnumber(res, "ctgtype");
+ i_ctgcommand = PQfnumber(res, "ctgcommand");
+ i_ctgfname = PQfnumber(res, "ctgfname");
+ i_ctgenabled = PQfnumber(res, "ctgenabled");
+
+ for (i = 0; i < ntups; i++)
+ {
+ ctginfo[i].dobj.objType = DO_CMDTRIGGER;
+ ctginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ ctginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&ctginfo[i].dobj);
+ ctginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_ctgname));
+ ctginfo[i].ctgname = pg_strdup(PQgetvalue(res, i, i_ctgname));
+ ctginfo[i].ctgtype = *(PQgetvalue(res, i, i_ctgtype));
+ ctginfo[i].ctgcommand = pg_strdup(PQgetvalue(res, i, i_ctgcommand));
+ ctginfo[i].ctgfname = pg_strdup(PQgetvalue(res, i, i_ctgfname));
+ ctginfo[i].ctgenabled = *(PQgetvalue(res, i, i_ctgenabled));
+ }
+
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+
+ return ctginfo;
+ }
+
+ /*
* getProcLangs
* get basic information about every procedural language in the system
*
***************
*** 7183,7188 **** dumpDumpableObject(Archive *fout, DumpableObject *dobj)
--- 7256,7264 ----
case DO_TRIGGER:
dumpTrigger(fout, (TriggerInfo *) dobj);
break;
+ case DO_CMDTRIGGER:
+ dumpCmdTrigger(fout, (CmdTriggerInfo *) dobj);
+ break;
case DO_CONSTRAINT:
dumpConstraint(fout, (ConstraintInfo *) dobj);
break;
***************
*** 13660,13665 **** dumpTrigger(Archive *fout, TriggerInfo *tginfo)
--- 13736,13812 ----
destroyPQExpBuffer(labelq);
}
+ static void
+ dumpCmdTrigger(Archive *fout, CmdTriggerInfo *ctginfo)
+ {
+ PQExpBuffer query;
+ PQExpBuffer labelq;
+
+ query = createPQExpBuffer();
+ labelq = createPQExpBuffer();
+
+ appendPQExpBuffer(query, "CREATE COMMAND TRIGGER ");
+ appendPQExpBufferStr(query, fmtId(ctginfo->dobj.name));
+
+ /* Trigger type */
+ if (ctginfo->ctgtype == CMD_TRIGGER_FIRED_BEFORE)
+ appendPQExpBuffer(query, " BEFORE ");
+ else if (ctginfo->ctgtype == CMD_TRIGGER_FIRED_AFTER)
+ appendPQExpBuffer(query, " AFTER ");
+ else
+ {
+ write_msg(NULL, "unexpected ctgtype value: %d\n", ctginfo->ctgtype);
+ exit_nicely(1);
+ }
+
+ if (strcmp("ANY", ctginfo->ctgcommand) == 0)
+ appendPQExpBufferStr(query, "ANY COMMAND");
+ else
+ {
+ appendPQExpBufferStr(query, ctginfo->ctgcommand);
+ }
+
+ appendPQExpBuffer(query, " EXECUTE PROCEDURE ");
+ appendPQExpBufferStr(query, ctginfo->ctgfname);
+ appendPQExpBuffer(query, " ();\n");
+
+ if (ctginfo->ctgenabled != 'O')
+ {
+ appendPQExpBuffer(query, "\nALTER COMMAND TRIGGER %s SET ",
+ fmtId(ctginfo->dobj.name));
+ switch (ctginfo->ctgenabled)
+ {
+ case 'D':
+ appendPQExpBuffer(query, "DISABLE");
+ break;
+ case 'A':
+ appendPQExpBuffer(query, "ENABLE ALWAYS");
+ break;
+ case 'R':
+ appendPQExpBuffer(query, "ENABLE REPLICA");
+ break;
+ default:
+ appendPQExpBuffer(query, "ENABLE");
+ break;
+ }
+ appendPQExpBuffer(query, ";\n");
+ }
+ appendPQExpBuffer(labelq, "COMMAND TRIGGER %s ",
+ fmtId(ctginfo->dobj.name));
+
+ ArchiveEntry(fout, ctginfo->dobj.catId, ctginfo->dobj.dumpId,
+ ctginfo->dobj.name, NULL, NULL, "", false,
+ "COMMAND TRIGGER", SECTION_POST_DATA,
+ query->data, "", NULL, NULL, 0, NULL, NULL);
+
+ dumpComment(fout, labelq->data,
+ NULL, NULL,
+ ctginfo->dobj.catId, 0, ctginfo->dobj.dumpId);
+
+ destroyPQExpBuffer(query);
+ destroyPQExpBuffer(labelq);
+ }
+
/*
* dumpRule
* Dump a rule
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
***************
*** 118,124 **** typedef enum
DO_DEFAULT_ACL,
DO_BLOB,
DO_BLOB_DATA,
! DO_COLLATION
} DumpableObjectType;
typedef struct _dumpableObject
--- 118,125 ----
DO_DEFAULT_ACL,
DO_BLOB,
DO_BLOB_DATA,
! DO_COLLATION,
! DO_CMDTRIGGER
} DumpableObjectType;
typedef struct _dumpableObject
***************
*** 350,355 **** typedef struct _triggerInfo
--- 351,366 ----
char *tgdef;
} TriggerInfo;
+ typedef struct _cmdtriggerInfo
+ {
+ DumpableObject dobj;
+ char *ctgcommand;
+ char *ctgname;
+ char *ctgfname;
+ char ctgtype;
+ char ctgenabled;
+ } CmdTriggerInfo;
+
/*
* struct ConstraintInfo is used for all constraint types. However we
* use a different objType for foreign key constraints, to make it easier
***************
*** 558,562 **** extern ForeignServerInfo *getForeignServers(Archive *fout,
--- 569,574 ----
extern DefaultACLInfo *getDefaultACLs(Archive *fout, int *numDefaultACLs);
extern void getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
int numExtensions);
+ extern CmdTriggerInfo *getCmdTriggers(Archive *fout, int *numCmdTriggers);
#endif /* PG_DUMP_H */
*** a/src/bin/pg_dump/pg_dump_sort.c
--- b/src/bin/pg_dump/pg_dump_sort.c
***************
*** 59,65 **** static const int oldObjectTypePriority[] =
17, /* DO_DEFAULT_ACL */
9, /* DO_BLOB */
11, /* DO_BLOB_DATA */
! 2 /* DO_COLLATION */
};
/*
--- 59,66 ----
17, /* DO_DEFAULT_ACL */
9, /* DO_BLOB */
11, /* DO_BLOB_DATA */
! 2, /* DO_COLLATION */
! 18 /* DO_CMDTRIGGER */
};
/*
***************
*** 98,104 **** static const int newObjectTypePriority[] =
29, /* DO_DEFAULT_ACL */
21, /* DO_BLOB */
23, /* DO_BLOB_DATA */
! 3 /* DO_COLLATION */
};
--- 99,106 ----
29, /* DO_DEFAULT_ACL */
21, /* DO_BLOB */
23, /* DO_BLOB_DATA */
! 3, /* DO_COLLATION */
! 30 /* DO_CMDTRIGGER */
};
***************
*** 1114,1119 **** describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
--- 1116,1126 ----
"TRIGGER %s (ID %d OID %u)",
obj->name, obj->dumpId, obj->catId.oid);
return;
+ case DO_CMDTRIGGER:
+ snprintf(buf, bufsize,
+ "TRIGGER %s ON COMMAND %s (ID %d OID %u)",
+ obj->name, ((CmdTriggerInfo *)obj)->ctgcommand, obj->dumpId, obj->catId.oid);
+ return;
case DO_CONSTRAINT:
snprintf(buf, bufsize,
"CONSTRAINT %s (ID %d OID %u)",
*** a/src/bin/psql/command.c
--- b/src/bin/psql/command.c
***************
*** 363,369 **** exec_command(const char *cmd,
success = describeTablespaces(pattern, show_verbose);
break;
case 'c':
! success = listConversions(pattern, show_verbose, show_system);
break;
case 'C':
success = listCasts(pattern, show_verbose);
--- 363,380 ----
success = describeTablespaces(pattern, show_verbose);
break;
case 'c':
! switch (cmd[2])
! {
! case '\0':
! success = listConversions(pattern, show_verbose, show_system);
! break;
! case 't':
! success = listCmdTriggers(pattern, show_verbose);
! break;
! default:
! status = PSQL_CMD_UNKNOWN;
! break;
! }
break;
case 'C':
success = listCasts(pattern, show_verbose);
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
***************
*** 2947,2952 **** listConversions(const char *pattern, bool verbose, bool showSystem)
--- 2947,3009 ----
}
/*
+ * \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"
/* ----------------
***************
*** 1725,1730 **** extern void OperatorCreate(const char *operatorName,
Oid restrictionId,
Oid joinId,
bool canMerge,
! bool canHash);
#endif /* PG_OPERATOR_H */
--- 1726,1732 ----
Oid restrictionId,
Oid joinId,
bool canMerge,
! bool canHash,
! CommandContext cmd);
#endif /* PG_OPERATOR_H */
*** a/src/include/catalog/pg_type.h
--- b/src/include/catalog/pg_type.h
***************
*** 650,655 **** DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void
--- 650,657 ----
#define VOIDOID 2278
DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define TRIGGEROID 2279
+ DATA(insert OID = 3838 ( command_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+ #define CMDTRIGGEROID 3838
DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ ));
*** 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,73 ----
+ /*-------------------------------------------------------------------------
+ *
+ * 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 */
+ List *before_any; /* procedures to call before any command */
+ List *after_any; /* procedures to call after any command */
+ MemoryContext oldmctx; /* Memory Context to switch back to */
+ MemoryContext cmdmctx; /* Memory Context for the command triggers */
+ } CommandContextData;
+
+ typedef struct CommandContextData *CommandContext;
+
+ /*
+ * CommandTriggerData is the node type that is passed as fmgr "context" info
+ * when a function is called by the command trigger manager.
+ */
+ #define CALLED_AS_COMMAND_TRIGGER(fcinfo) \
+ ((fcinfo)->context != NULL && IsA((fcinfo)->context, CommandTriggerData))
+
+ typedef struct CommandTriggerData
+ {
+ NodeTag type;
+ char *when; /* Either BEFORE or AFTER */
+ 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 */
+ } CommandTriggerData;
+
+ extern Oid CreateCmdTrigger(CreateCmdTrigStmt *stmt, const char *queryString);
+ extern void RemoveCmdTriggerById(Oid ctrigOid);
+ extern Oid get_cmdtrigger_oid(const char *trigname, bool missing_ok);
+ extern void AlterCmdTrigger(AlterCmdTrigStmt *stmt);
+ extern void RenameCmdTrigger(List *name, const char *newname);
+
+ extern void InitCommandContext(CommandContext cmd, const Node *stmt);
+ extern bool CommandFiresTriggers(CommandContext cmd);
+ extern bool CommandFiresAfterTriggers(CommandContext cmd);
+ extern void ExecBeforeCommandTriggers(CommandContext cmd);
+ extern void ExecBeforeAnyCommandTriggers(CommandContext cmd);
+ extern void ExecAfterCommandTriggers(CommandContext cmd);
+ extern void ExecAfterAnyCommandTriggers(CommandContext cmd);
+
+ #endif /* CMD_TRIGGER_H */
*** a/src/include/commands/collationcmds.h
--- b/src/include/commands/collationcmds.h
***************
*** 15,27 ****
#ifndef COLLATIONCMDS_H
#define COLLATIONCMDS_H
#include "nodes/parsenodes.h"
! extern void DefineCollation(List *names, List *parameters);
! extern void RenameCollation(List *name, const char *newname);
! extern void AlterCollationOwner(List *name, Oid newOwnerId);
! extern void AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId);
! extern void AlterCollationNamespace(List *name, const char *newschema);
! extern Oid AlterCollationNamespace_oid(Oid collOid, Oid newNspOid);
#endif /* COLLATIONCMDS_H */
--- 15,30 ----
#ifndef COLLATIONCMDS_H
#define COLLATIONCMDS_H
+ #include "commands/cmdtrigger.h"
#include "nodes/parsenodes.h"
! extern void DefineCollation(List *names, List *parameters, CommandContext cmd);
! extern void RenameCollation(List *name, const char *newname, CommandContext cmd);
! extern void AlterCollationOwner(List *name, Oid newOwnerId, CommandContext cmd);
! extern void AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId, CommandContext cmd);
! extern void AlterCollationNamespace(List *name, const char *newschema,
! CommandContext cmd);
! extern Oid AlterCollationNamespace_oid(Oid collOid, Oid newNspOid,
! CommandContext cmd);
#endif /* COLLATIONCMDS_H */
*** a/src/include/commands/conversioncmds.h
--- b/src/include/commands/conversioncmds.h
***************
*** 18,27 ****
#include "nodes/parsenodes.h"
extern void CreateConversionCommand(CreateConversionStmt *parsetree);
! extern void RenameConversion(List *name, const char *newname);
! extern void AlterConversionOwner(List *name, Oid newOwnerId);
! extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId);
! extern void AlterConversionNamespace(List *name, const char *newschema);
extern Oid AlterConversionNamespace_oid(Oid convOid, Oid newNspOid);
#endif /* CONVERSIONCMDS_H */
--- 18,28 ----
#include "nodes/parsenodes.h"
extern void CreateConversionCommand(CreateConversionStmt *parsetree);
! extern void RenameConversion(List *name, const char *newname, CommandContext cmd);
! extern void AlterConversionOwner(List *name, Oid newOwnerId, CommandContext cmd);
! extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId, CommandContext cmd);
! extern void AlterConversionNamespace(List *name, const char *newschema,
! CommandContext cmd);
extern Oid AlterConversionNamespace_oid(Oid convOid, Oid newNspOid);
#endif /* CONVERSIONCMDS_H */
*** a/src/include/commands/dbcommands.h
--- b/src/include/commands/dbcommands.h
***************
*** 53,59 **** typedef struct xl_dbase_drop_rec
} xl_dbase_drop_rec;
extern void createdb(const CreatedbStmt *stmt);
! extern void dropdb(const char *dbname, bool missing_ok);
extern void RenameDatabase(const char *oldname, const char *newname);
extern void AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel);
extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt);
--- 53,59 ----
} xl_dbase_drop_rec;
extern void createdb(const CreatedbStmt *stmt);
! extern void dropdb(const DropdbStmt *stmt);
extern void RenameDatabase(const char *oldname, const char *newname);
extern void AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel);
extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt);
*** a/src/include/commands/defrem.h
--- b/src/include/commands/defrem.h
***************
*** 14,19 ****
--- 14,20 ----
#ifndef DEFREM_H
#define DEFREM_H
+ #include "commands/cmdtrigger.h"
#include "nodes/parsenodes.h"
/* commands/dropcmds.c */
***************
*** 39,47 **** extern Oid DefineIndex(RangeVar *heapRelation,
bool check_rights,
bool skip_build,
bool quiet,
! bool concurrent);
! extern void ReindexIndex(RangeVar *indexRelation);
! extern void ReindexTable(RangeVar *relation);
extern void ReindexDatabase(const char *databaseName,
bool do_system, bool do_user);
extern char *makeObjectName(const char *name1, const char *name2,
--- 40,49 ----
bool check_rights,
bool skip_build,
bool quiet,
! bool concurrent,
! CommandContext cmd);
! extern void ReindexIndex(RangeVar *indexRelation, CommandContext cmd);
! extern void ReindexTable(RangeVar *relation, CommandContext cmd);
extern void ReindexDatabase(const char *databaseName,
bool do_system, bool do_user);
extern char *makeObjectName(const char *name1, const char *name2,
***************
*** 64,95 **** extern void CreateFunction(CreateFunctionStmt *stmt, const char *queryString);
extern void RemoveFunctionById(Oid funcOid);
extern void SetFunctionReturnType(Oid funcOid, Oid newRetType);
extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType);
! extern void RenameFunction(List *name, List *argtypes, const char *newname);
! extern void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId);
! extern void AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId);
extern void AlterFunction(AlterFunctionStmt *stmt);
extern void CreateCast(CreateCastStmt *stmt);
extern void DropCastById(Oid castOid);
extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
! const char *newschema);
! extern Oid AlterFunctionNamespace_oid(Oid procOid, Oid nspOid);
extern void ExecuteDoStmt(DoStmt *stmt);
extern Oid get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok);
/* commands/operatorcmds.c */
! extern void DefineOperator(List *names, List *parameters);
extern void RemoveOperatorById(Oid operOid);
extern void AlterOperatorOwner(List *name, TypeName *typeName1,
! TypeName *typename2, Oid newOwnerId);
extern void AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId);
! extern void AlterOperatorNamespace(List *names, List *argtypes, const char *newschema);
extern Oid AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid);
/* commands/aggregatecmds.c */
extern void DefineAggregate(List *name, List *args, bool oldstyle,
! List *parameters);
! extern void RenameAggregate(List *name, List *args, const char *newname);
! extern void AlterAggregateOwner(List *name, List *args, Oid newOwnerId);
/* commands/opclasscmds.c */
extern void DefineOpClass(CreateOpClassStmt *stmt);
--- 66,99 ----
extern void RemoveFunctionById(Oid funcOid);
extern void SetFunctionReturnType(Oid funcOid, Oid newRetType);
extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType);
! extern void RenameFunction(List *name, List *argtypes, const char *newname, CommandContext cmd);
! extern void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId, CommandContext cmd);
! extern void AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId, CommandContext cmd);
extern void AlterFunction(AlterFunctionStmt *stmt);
extern void CreateCast(CreateCastStmt *stmt);
extern void DropCastById(Oid castOid);
extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
! const char *newschema, CommandContext cmd);
! extern Oid AlterFunctionNamespace_oid(Oid procOid, Oid nspOid,
! CommandContext cmd);
extern void ExecuteDoStmt(DoStmt *stmt);
extern Oid get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok);
/* commands/operatorcmds.c */
! extern void DefineOperator(List *names, List *parameters, CommandContext cmd);
extern void RemoveOperatorById(Oid operOid);
extern void AlterOperatorOwner(List *name, TypeName *typeName1,
! TypeName *typename2, Oid newOwnerId, CommandContext cmd);
extern void AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId);
! extern void AlterOperatorNamespace(List *names, List *argtypes,
! const char *newschema, CommandContext cmd);
extern Oid AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid);
/* commands/aggregatecmds.c */
extern void DefineAggregate(List *name, List *args, bool oldstyle,
! List *parameters, CommandContext cmd);
! extern void RenameAggregate(List *name, List *args, const char *newname, CommandContext cmd);
! extern void AlterAggregateOwner(List *name, List *args, Oid newOwnerId, CommandContext cmd);
/* commands/opclasscmds.c */
extern void DefineOpClass(CreateOpClassStmt *stmt);
***************
*** 99,156 **** extern void RemoveOpClassById(Oid opclassOid);
extern void RemoveOpFamilyById(Oid opfamilyOid);
extern void RemoveAmOpEntryById(Oid entryOid);
extern void RemoveAmProcEntryById(Oid entryOid);
! extern void RenameOpClass(List *name, const char *access_method, const char *newname);
! extern void RenameOpFamily(List *name, const char *access_method, const char *newname);
! extern void AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId);
extern void AlterOpClassOwner_oid(Oid opclassOid, Oid newOwnerId);
! extern void AlterOpClassNamespace(List *name, char *access_method, const char *newschema);
extern Oid AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid);
! extern void AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId);
extern void AlterOpFamilyOwner_oid(Oid opfamilyOid, Oid newOwnerId);
! extern void AlterOpFamilyNamespace(List *name, char *access_method, const char *newschema);
extern Oid AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid);
extern Oid get_am_oid(const char *amname, bool missing_ok);
extern Oid get_opclass_oid(Oid amID, List *opclassname, bool missing_ok);
extern Oid get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok);
/* commands/tsearchcmds.c */
! extern void DefineTSParser(List *names, List *parameters);
! extern void RenameTSParser(List *oldname, const char *newname);
! extern void AlterTSParserNamespace(List *name, const char *newschema);
extern Oid AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid);
extern void RemoveTSParserById(Oid prsId);
! extern void DefineTSDictionary(List *names, List *parameters);
! extern void RenameTSDictionary(List *oldname, const char *newname);
extern void RemoveTSDictionaryById(Oid dictId);
extern void AlterTSDictionary(AlterTSDictionaryStmt *stmt);
! extern void AlterTSDictionaryOwner(List *name, Oid newOwnerId);
! extern void AlterTSDictionaryNamespace(List *name, const char *newschema);
extern Oid AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid);
! extern void DefineTSTemplate(List *names, List *parameters);
! extern void RenameTSTemplate(List *oldname, const char *newname);
! extern void AlterTSTemplateNamespace(List *name, const char *newschema);
extern Oid AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid);
extern void RemoveTSTemplateById(Oid tmplId);
! extern void DefineTSConfiguration(List *names, List *parameters);
! extern void RenameTSConfiguration(List *oldname, const char *newname);
extern void RemoveTSConfigurationById(Oid cfgId);
extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
! extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId);
! extern void AlterTSConfigurationNamespace(List *name, const char *newschema);
extern Oid AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid);
extern text *serialize_deflist(List *deflist);
extern List *deserialize_deflist(Datum txt);
/* commands/foreigncmds.c */
! extern void RenameForeignServer(const char *oldname, const char *newname);
! extern void RenameForeignDataWrapper(const char *oldname, const char *newname);
! extern void AlterForeignServerOwner(const char *name, Oid newOwnerId);
extern void AlterForeignServerOwner_oid(Oid , Oid newOwnerId);
! extern void AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId);
extern void AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId);
extern void CreateForeignDataWrapper(CreateFdwStmt *stmt);
extern void AlterForeignDataWrapper(AlterFdwStmt *stmt);
--- 103,166 ----
extern void RemoveOpFamilyById(Oid opfamilyOid);
extern void RemoveAmOpEntryById(Oid entryOid);
extern void RemoveAmProcEntryById(Oid entryOid);
! extern void RenameOpClass(List *name, const char *access_method,
! const char *newname, CommandContext cmd);
! extern void RenameOpFamily(List *name, const char *access_method,
! const char *newname, CommandContext cmd);
! extern void AlterOpClassOwner(List *name, const char *access_method,
! Oid newOwnerId, CommandContext cmd);
extern void AlterOpClassOwner_oid(Oid opclassOid, Oid newOwnerId);
! extern void AlterOpClassNamespace(List *name, char *access_method,
! const char *newschema, CommandContext cmd);
extern Oid AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid);
! extern void AlterOpFamilyOwner(List *name, const char *access_method,
! Oid newOwnerId, CommandContext cmd);
extern void AlterOpFamilyOwner_oid(Oid opfamilyOid, Oid newOwnerId);
! extern void AlterOpFamilyNamespace(List *name, char *access_method,
! const char *newschema, CommandContext cmd);
extern Oid AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid);
extern Oid get_am_oid(const char *amname, bool missing_ok);
extern Oid get_opclass_oid(Oid amID, List *opclassname, bool missing_ok);
extern Oid get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok);
/* commands/tsearchcmds.c */
! extern void DefineTSParser(List *names, List *parameters, CommandContext cmd);
! extern void RenameTSParser(List *oldname, const char *newname, CommandContext cmd);
! extern void AlterTSParserNamespace(List *name, const char *newschema, CommandContext cmd);
extern Oid AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid);
extern void RemoveTSParserById(Oid prsId);
! extern void DefineTSDictionary(List *names, List *parameters, CommandContext cmd);
! extern void RenameTSDictionary(List *oldname, const char *newname, CommandContext cmd);
extern void RemoveTSDictionaryById(Oid dictId);
extern void AlterTSDictionary(AlterTSDictionaryStmt *stmt);
! extern void AlterTSDictionaryOwner(List *name, Oid newOwnerId, CommandContext cmd);
! extern void AlterTSDictionaryNamespace(List *name, const char *newschema, CommandContext cmd);
extern Oid AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid);
! extern void DefineTSTemplate(List *names, List *parameters, CommandContext cmd);
! extern void RenameTSTemplate(List *oldname, const char *newname, CommandContext cmd);
! extern void AlterTSTemplateNamespace(List *name, const char *newschema, CommandContext cmd);
extern Oid AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid);
extern void RemoveTSTemplateById(Oid tmplId);
! extern void DefineTSConfiguration(List *names, List *parameters, CommandContext cmd);
! extern void RenameTSConfiguration(List *oldname, const char *newname, CommandContext cmd);
extern void RemoveTSConfigurationById(Oid cfgId);
extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
! extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId, CommandContext cmd);
! extern void AlterTSConfigurationNamespace(List *name, const char *newschema, CommandContext cmd);
extern Oid AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid);
extern text *serialize_deflist(List *deflist);
extern List *deserialize_deflist(Datum txt);
/* commands/foreigncmds.c */
! extern void RenameForeignServer(const char *oldname, const char *newname, CommandContext cmd);
! extern void RenameForeignDataWrapper(const char *oldname, const char *newname, CommandContext cmd);
! extern void AlterForeignServerOwner(const char *name, Oid newOwnerId, CommandContext cmd);
extern void AlterForeignServerOwner_oid(Oid , Oid newOwnerId);
! extern void AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId, CommandContext cmd);
extern void AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId);
extern void CreateForeignDataWrapper(CreateFdwStmt *stmt);
extern void AlterForeignDataWrapper(AlterFdwStmt *stmt);
*** a/src/include/commands/extension.h
--- b/src/include/commands/extension.h
***************
*** 14,19 ****
--- 14,20 ----
#ifndef EXTENSION_H
#define EXTENSION_H
+ #include "commands/cmdtrigger.h"
#include "nodes/parsenodes.h"
***************
*** 43,48 **** extern void ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt);
extern Oid get_extension_oid(const char *extname, bool missing_ok);
extern char *get_extension_name(Oid ext_oid);
! extern void AlterExtensionNamespace(List *names, const char *newschema);
#endif /* EXTENSION_H */
--- 44,50 ----
extern Oid get_extension_oid(const char *extname, bool missing_ok);
extern char *get_extension_name(Oid ext_oid);
! extern void AlterExtensionNamespace(List *names, const char *newschema,
! CommandContext cmd);
#endif /* EXTENSION_H */
*** a/src/include/commands/proclang.h
--- b/src/include/commands/proclang.h
***************
*** 12,23 ****
#ifndef PROCLANG_H
#define PROCLANG_H
#include "nodes/parsenodes.h"
extern void CreateProceduralLanguage(CreatePLangStmt *stmt);
extern void DropProceduralLanguageById(Oid langOid);
! extern void RenameLanguage(const char *oldname, const char *newname);
! extern void AlterLanguageOwner(const char *name, Oid newOwnerId);
extern void AlterLanguageOwner_oid(Oid oid, Oid newOwnerId);
extern bool PLTemplateExists(const char *languageName);
extern Oid get_language_oid(const char *langname, bool missing_ok);
--- 12,24 ----
#ifndef PROCLANG_H
#define PROCLANG_H
+ #include "commands/cmdtrigger.h"
#include "nodes/parsenodes.h"
extern void CreateProceduralLanguage(CreatePLangStmt *stmt);
extern void DropProceduralLanguageById(Oid langOid);
! extern void RenameLanguage(const char *oldname, const char *newname, CommandContext cmd);
! extern void AlterLanguageOwner(const char *name, Oid newOwnerId, CommandContext cmd);
extern void AlterLanguageOwner_oid(Oid oid, Oid newOwnerId);
extern bool PLTemplateExists(const char *languageName);
extern Oid get_language_oid(const char *langname, bool missing_ok);
*** a/src/include/commands/schemacmds.h
--- b/src/include/commands/schemacmds.h
***************
*** 22,29 **** extern void CreateSchemaCommand(CreateSchemaStmt *parsetree,
extern void RemoveSchemaById(Oid schemaOid);
! extern void RenameSchema(const char *oldname, const char *newname);
! extern void AlterSchemaOwner(const char *name, Oid newOwnerId);
extern void AlterSchemaOwner_oid(Oid schemaOid, Oid newOwnerId);
#endif /* SCHEMACMDS_H */
--- 22,29 ----
extern void RemoveSchemaById(Oid schemaOid);
! extern void RenameSchema(const char *oldname, const char *newname, CommandContext cmd);
! extern void AlterSchemaOwner(const char *name, Oid newOwnerId, CommandContext cmd);
extern void AlterSchemaOwner_oid(Oid schemaOid, Oid newOwnerId);
#endif /* SCHEMACMDS_H */
*** a/src/include/commands/tablecmds.h
--- b/src/include/commands/tablecmds.h
***************
*** 15,26 ****
#define TABLECMDS_H
#include "access/htup.h"
#include "nodes/parsenodes.h"
#include "storage/lock.h"
#include "utils/relcache.h"
! extern Oid DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId);
extern void RemoveRelations(DropStmt *drop);
--- 15,28 ----
#define TABLECMDS_H
#include "access/htup.h"
+ #include "commands/cmdtrigger.h"
#include "nodes/parsenodes.h"
#include "storage/lock.h"
#include "utils/relcache.h"
! extern Oid DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
! CommandContext cmd);
extern void RemoveRelations(DropStmt *drop);
***************
*** 46,59 **** extern void ExecuteTruncate(TruncateStmt *stmt);
extern void SetRelationHasSubclass(Oid relationId, bool relhassubclass);
! extern void renameatt(RenameStmt *stmt);
! extern void RenameConstraint(RenameStmt *stmt);
!
! extern void RenameRelation(RenameStmt *stmt);
extern void RenameRelationInternal(Oid myrelid,
! const char *newrelname);
extern void find_composite_type_dependencies(Oid typeOid,
Relation origRelation,
--- 48,60 ----
extern void SetRelationHasSubclass(Oid relationId, bool relhassubclass);
! extern void renameatt(RenameStmt *stmt, CommandContext cmd);
! extern void RenameRelation(RenameStmt *stmt, CommandContext cmd);
! extern void RenameConstraint(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
***************
*** 289,294 **** typedef enum NodeTag
--- 289,295 ----
T_IndexStmt,
T_CreateFunctionStmt,
T_AlterFunctionStmt,
+ T_RemoveFuncStmt,
T_DoStmt,
T_RenameStmt,
T_RuleStmt,
***************
*** 310,315 **** typedef enum NodeTag
--- 311,317 ----
T_DiscardStmt,
T_CreateTrigStmt,
T_CreatePLangStmt,
+ T_DropPLangStmt,
T_CreateRoleStmt,
T_AlterRoleStmt,
T_DropRoleStmt,
***************
*** 323,331 **** typedef enum NodeTag
--- 325,336 ----
T_AlterRoleSetStmt,
T_CreateConversionStmt,
T_CreateCastStmt,
+ T_DropCastStmt,
T_CreateOpClassStmt,
T_CreateOpFamilyStmt,
T_AlterOpFamilyStmt,
+ T_RemoveOpClassStmt,
+ T_RemoveOpFamilyStmt,
T_PrepareStmt,
T_ExecuteStmt,
T_DeallocateStmt,
***************
*** 344,351 **** typedef enum NodeTag
--- 349,358 ----
T_AlterTSConfigurationStmt,
T_CreateFdwStmt,
T_AlterFdwStmt,
+ T_DropFdwStmt,
T_CreateForeignServerStmt,
T_AlterForeignServerStmt,
+ T_DropForeignServerStmt,
T_CreateUserMappingStmt,
T_AlterUserMappingStmt,
T_DropUserMappingStmt,
***************
*** 355,360 **** typedef enum NodeTag
--- 362,369 ----
T_CreateExtensionStmt,
T_AlterExtensionStmt,
T_AlterExtensionContentsStmt,
+ T_CreateCmdTrigStmt,
+ T_AlterCmdTrigStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
***************
*** 411,416 **** typedef enum NodeTag
--- 420,426 ----
* pass multiple object types through the same pointer).
*/
T_TriggerData = 950, /* in commands/trigger.h */
+ T_CommandTriggerData, /* in commands/cmdtrigger.h */
T_ReturnSetInfo, /* in nodes/execnodes.h */
T_WindowObjectData, /* private in nodeWindowAgg.c */
T_TIDBitmap, /* in nodes/tidbitmap.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
* ----------------------
*/
*** 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, RESERVED_KEYWORD)
PG_KEYWORD("column", COLUMN, RESERVED_KEYWORD)
+ PG_KEYWORD("command", COMMAND, UNRESERVED_KEYWORD)
PG_KEYWORD("comment", COMMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("comments", COMMENTS, UNRESERVED_KEYWORD)
PG_KEYWORD("commit", COMMIT, UNRESERVED_KEYWORD)
*** a/src/include/rewrite/rewriteDefine.h
--- b/src/include/rewrite/rewriteDefine.h
***************
*** 14,19 ****
--- 14,20 ----
#ifndef REWRITEDEFINE_H
#define REWRITEDEFINE_H
+ #include "commands/cmdtrigger.h"
#include "nodes/parsenodes.h"
#include "utils/relcache.h"
***************
*** 30,36 **** extern void DefineQueryRewrite(char *rulename,
CmdType event_type,
bool is_instead,
bool replace,
! List *action);
extern void RenameRewriteRule(Oid owningRel, const char *oldName,
const char *newName);
--- 31,38 ----
CmdType event_type,
bool is_instead,
bool replace,
! List *action,
! CommandContext cmd);
extern void RenameRewriteRule(Oid owningRel, const char *oldName,
const char *newName);
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 1012,1017 **** extern Datum pg_encoding_max_length_sql(PG_FUNCTION_ARGS);
--- 1012,1018 ----
/* format_type.c */
extern Datum format_type(PG_FUNCTION_ARGS);
extern char *format_type_be(Oid type_oid);
+ extern char *format_type_be_without_namespace(Oid type_oid);
extern char *format_type_with_typemod(Oid type_oid, int32 typemod);
extern Datum oidvectortypes(PG_FUNCTION_ARGS);
extern int32 type_maximum_size(Oid type_oid, int32 typemod);
*** a/src/pl/plperl/expected/plperl_trigger.out
--- b/src/pl/plperl/expected/plperl_trigger.out
***************
*** 309,311 **** $$ LANGUAGE plperl;
--- 309,348 ----
SELECT direct_trigger();
ERROR: trigger functions can only be called as triggers
CONTEXT: compilation of PL/Perl function "direct_trigger"
+ -- test plperl command triggers
+ create or replace function perlsnitch() returns command_trigger language plperl as $$
+ elog(NOTICE, "perlsnitch: "
+ . $_TD->{when} . " "
+ . $_TD->{tag} . " "
+ . $_TD->{schemaname} . " "
+ . $_TD->{objectname});
+ $$;
+ create command trigger perl_a_snitch after any command execute procedure perlsnitch();
+ create command trigger perl_b_snitch before any command execute procedure perlsnitch();
+ create or replace function foobar() returns int language sql as $$select 1;$$;
+ NOTICE: perlsnitch: BEFORE CREATE FUNCTION public foobar
+ CONTEXT: PL/Perl function "perlsnitch"
+ NOTICE: perlsnitch: AFTER CREATE FUNCTION public foobar
+ CONTEXT: PL/Perl function "perlsnitch"
+ alter function foobar() cost 77;
+ NOTICE: perlsnitch: BEFORE ALTER FUNCTION public foobar
+ CONTEXT: PL/Perl function "perlsnitch"
+ NOTICE: perlsnitch: AFTER ALTER FUNCTION public foobar
+ CONTEXT: PL/Perl function "perlsnitch"
+ drop function foobar();
+ NOTICE: perlsnitch: BEFORE DROP FUNCTION public foobar
+ CONTEXT: PL/Perl function "perlsnitch"
+ NOTICE: perlsnitch: AFTER DROP FUNCTION public foobar
+ CONTEXT: PL/Perl function "perlsnitch"
+ create table foo();
+ NOTICE: perlsnitch: BEFORE CREATE TABLE public foo
+ CONTEXT: PL/Perl function "perlsnitch"
+ NOTICE: perlsnitch: AFTER CREATE TABLE public foo
+ CONTEXT: PL/Perl function "perlsnitch"
+ drop table foo;
+ NOTICE: perlsnitch: BEFORE DROP TABLE public foo
+ CONTEXT: PL/Perl function "perlsnitch"
+ NOTICE: perlsnitch: AFTER DROP TABLE public foo
+ CONTEXT: PL/Perl function "perlsnitch"
+ drop command trigger perl_a_snitch;
+ drop command trigger perl_b_snitch;
*** a/src/pl/plperl/plperl.c
--- b/src/pl/plperl/plperl.c
***************
*** 234,241 **** static void set_interp_require(bool trusted);
static Datum plperl_func_handler(PG_FUNCTION_ARGS);
static Datum plperl_trigger_handler(PG_FUNCTION_ARGS);
! static plperl_proc_desc *compile_plperl_function(Oid fn_oid, bool is_trigger);
static SV *plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc);
static SV *plperl_hash_from_datum(Datum attr);
--- 234,244 ----
static Datum plperl_func_handler(PG_FUNCTION_ARGS);
static Datum plperl_trigger_handler(PG_FUNCTION_ARGS);
+ static void plperl_command_trigger_handler(PG_FUNCTION_ARGS);
! static plperl_proc_desc *compile_plperl_function(Oid fn_oid,
! bool is_dml_trigger,
! bool is_cmd_trigger);
static SV *plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc);
static SV *plperl_hash_from_datum(Datum attr);
***************
*** 1567,1572 **** plperl_trigger_build_args(FunctionCallInfo fcinfo)
--- 1570,1604 ----
}
+ /* Set up the arguments for a command trigger call. */
+ static SV *
+ plperl_command_trigger_build_args(FunctionCallInfo fcinfo)
+ {
+ CommandTriggerData *tdata;
+ char *objectid;
+ HV *hv;
+
+ hv = newHV();
+ hv_ksplit(hv, 12); /* pre-grow the hash */
+
+ tdata = (CommandTriggerData *) fcinfo->context;
+
+ hv_store_string(hv, "when", cstr2sv(tdata->when));
+ hv_store_string(hv, "tag", cstr2sv(tdata->tag));
+
+ if (tdata->objectId == InvalidOid)
+ objectid = pstrdup("NULL");
+ else
+ objectid = DatumGetCString(
+ DirectFunctionCall1(oidout, ObjectIdGetDatum(tdata->objectId)));
+ hv_store_string(hv, "objectid", cstr2sv(objectid));
+
+ hv_store_string(hv, "objectname", cstr2sv(tdata->objectname == NULL ? "NULL" : tdata->objectname));
+ hv_store_string(hv, "schemaname", cstr2sv(tdata->schemaname == NULL ? "NULL" : tdata->schemaname));
+
+ return newRV_noinc((SV *) hv);
+ }
+
/* Set up the new tuple returned from a trigger. */
static HeapTuple
***************
*** 1668,1673 **** plperl_call_handler(PG_FUNCTION_ARGS)
--- 1700,1707 ----
{
if (CALLED_AS_TRIGGER(fcinfo))
retval = PointerGetDatum(plperl_trigger_handler(fcinfo));
+ else if (CALLED_AS_COMMAND_TRIGGER(fcinfo))
+ plperl_command_trigger_handler(fcinfo);
else
retval = plperl_func_handler(fcinfo);
}
***************
*** 1794,1800 **** plperl_validator(PG_FUNCTION_ARGS)
Oid *argtypes;
char **argnames;
char *argmodes;
! bool istrigger = false;
int i;
/* Get the new function's pg_proc entry */
--- 1828,1834 ----
Oid *argtypes;
char **argnames;
char *argmodes;
! bool is_dml_trigger = false, is_cmd_trigger = false;
int i;
/* Get the new function's pg_proc entry */
***************
*** 1806,1818 **** plperl_validator(PG_FUNCTION_ARGS)
functyptype = get_typtype(proc->prorettype);
/* Disallow pseudotype result */
! /* except for TRIGGER, RECORD, or VOID */
if (functyptype == TYPTYPE_PSEUDO)
{
/* we assume OPAQUE with no arguments means a trigger */
if (proc->prorettype == TRIGGEROID ||
(proc->prorettype == OPAQUEOID && proc->pronargs == 0))
! istrigger = true;
else if (proc->prorettype != RECORDOID &&
proc->prorettype != VOIDOID)
ereport(ERROR,
--- 1840,1854 ----
functyptype = get_typtype(proc->prorettype);
/* Disallow pseudotype result */
! /* except for TRIGGER, CMDTRIGGER, RECORD, or VOID */
if (functyptype == TYPTYPE_PSEUDO)
{
/* we assume OPAQUE with no arguments means a trigger */
if (proc->prorettype == TRIGGEROID ||
(proc->prorettype == OPAQUEOID && proc->pronargs == 0))
! is_dml_trigger = true;
! else if (proc->prorettype == CMDTRIGGEROID)
! is_cmd_trigger = true;
else if (proc->prorettype != RECORDOID &&
proc->prorettype != VOIDOID)
ereport(ERROR,
***************
*** 1839,1845 **** plperl_validator(PG_FUNCTION_ARGS)
/* Postpone body checks if !check_function_bodies */
if (check_function_bodies)
{
! (void) compile_plperl_function(funcoid, istrigger);
}
/* the result of a validator is ignored */
--- 1875,1881 ----
/* Postpone body checks if !check_function_bodies */
if (check_function_bodies)
{
! (void) compile_plperl_function(funcoid, is_dml_trigger, is_cmd_trigger);
}
/* the result of a validator is ignored */
***************
*** 2110,2115 **** plperl_call_perl_trigger_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo,
--- 2146,2207 ----
}
+ static void
+ plperl_call_perl_command_trigger_func(plperl_proc_desc *desc,
+ FunctionCallInfo fcinfo,
+ SV *td)
+ {
+ dSP;
+ SV *retval,
+ *TDsv;
+ int count;
+
+ ENTER;
+ SAVETMPS;
+
+ TDsv = get_sv("_TD", 0);
+ if (!TDsv)
+ elog(ERROR, "couldn't fetch $_TD");
+
+ save_item(TDsv); /* local $_TD */
+ sv_setsv(TDsv, td);
+
+ PUSHMARK(sp);
+ PUTBACK;
+
+ /* Do NOT use G_KEEPERR here */
+ count = perl_call_sv(desc->reference, G_SCALAR | G_EVAL);
+
+ SPAGAIN;
+
+ if (count != 1)
+ {
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+ elog(ERROR, "didn't get a return item from trigger function");
+ }
+
+ if (SvTRUE(ERRSV))
+ {
+ (void) POPs;
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+ /* XXX need to find a way to assign an errcode here */
+ ereport(ERROR,
+ (errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV)))));
+ }
+
+ retval = newSVsv(POPs);
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return;
+ }
+
static Datum
plperl_func_handler(PG_FUNCTION_ARGS)
{
***************
*** 2129,2135 **** plperl_func_handler(PG_FUNCTION_ARGS)
if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "could not connect to SPI manager");
! prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, false);
current_call_data->prodesc = prodesc;
/* Set a callback for error reporting */
--- 2221,2227 ----
if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "could not connect to SPI manager");
! prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, false, false);
current_call_data->prodesc = prodesc;
/* Set a callback for error reporting */
***************
*** 2249,2255 **** plperl_trigger_handler(PG_FUNCTION_ARGS)
elog(ERROR, "could not connect to SPI manager");
/* Find or compile the function */
! prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, true);
current_call_data->prodesc = prodesc;
/* Set a callback for error reporting */
--- 2341,2347 ----
elog(ERROR, "could not connect to SPI manager");
/* Find or compile the function */
! prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, true, false);
current_call_data->prodesc = prodesc;
/* Set a callback for error reporting */
***************
*** 2339,2344 **** plperl_trigger_handler(PG_FUNCTION_ARGS)
--- 2431,2481 ----
}
+ static void
+ plperl_command_trigger_handler(PG_FUNCTION_ARGS)
+ {
+ plperl_proc_desc *prodesc;
+ SV *svTD;
+ ErrorContextCallback pl_error_context;
+
+ /*
+ * Create the call_data before connecting to SPI, so that it is not
+ * allocated in the SPI memory context
+ */
+ current_call_data = (plperl_call_data *) palloc0(sizeof(plperl_call_data));
+ current_call_data->fcinfo = fcinfo;
+
+ /* Connect to SPI manager */
+ if (SPI_connect() != SPI_OK_CONNECT)
+ elog(ERROR, "could not connect to SPI manager");
+
+ /* Find or compile the function */
+ prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, false, true);
+ current_call_data->prodesc = prodesc;
+
+ /* Set a callback for error reporting */
+ pl_error_context.callback = plperl_exec_callback;
+ pl_error_context.previous = error_context_stack;
+ pl_error_context.arg = prodesc->proname;
+ error_context_stack = &pl_error_context;
+
+ activate_interpreter(prodesc->interp);
+
+ svTD = plperl_command_trigger_build_args(fcinfo);
+ plperl_call_perl_command_trigger_func(prodesc, fcinfo, svTD);
+
+ if (SPI_finish() != SPI_OK_FINISH)
+ elog(ERROR, "SPI_finish() failed");
+
+ /* Restore the previous error callback */
+ error_context_stack = pl_error_context.previous;
+
+ SvREFCNT_dec(svTD);
+
+ return;
+ }
+
+
static bool
validate_plperl_function(plperl_proc_ptr *proc_ptr, HeapTuple procTup)
{
***************
*** 2378,2384 **** validate_plperl_function(plperl_proc_ptr *proc_ptr, HeapTuple procTup)
static plperl_proc_desc *
! compile_plperl_function(Oid fn_oid, bool is_trigger)
{
HeapTuple procTup;
Form_pg_proc procStruct;
--- 2515,2521 ----
static plperl_proc_desc *
! compile_plperl_function(Oid fn_oid, bool is_dml_trigger, bool is_cmd_trigger)
{
HeapTuple procTup;
Form_pg_proc procStruct;
***************
*** 2403,2409 **** compile_plperl_function(Oid fn_oid, bool is_trigger)
/* Try to find function in plperl_proc_hash */
proc_key.proc_id = fn_oid;
! proc_key.is_trigger = is_trigger;
proc_key.user_id = GetUserId();
proc_ptr = hash_search(plperl_proc_hash, &proc_key,
--- 2540,2546 ----
/* Try to find function in plperl_proc_hash */
proc_key.proc_id = fn_oid;
! proc_key.is_trigger = is_dml_trigger;
proc_key.user_id = GetUserId();
proc_ptr = hash_search(plperl_proc_hash, &proc_key,
***************
*** 2480,2486 **** compile_plperl_function(Oid fn_oid, bool is_trigger)
* Get the required information for input conversion of the
* return value.
************************************************************/
! if (!is_trigger)
{
typeTup =
SearchSysCache1(TYPEOID,
--- 2617,2623 ----
* Get the required information for input conversion of the
* return value.
************************************************************/
! if (!is_dml_trigger && !is_cmd_trigger)
{
typeTup =
SearchSysCache1(TYPEOID,
***************
*** 2538,2544 **** compile_plperl_function(Oid fn_oid, bool is_trigger)
* Get the required information for output conversion
* of all procedure arguments
************************************************************/
! if (!is_trigger)
{
prodesc->nargs = procStruct->pronargs;
for (i = 0; i < prodesc->nargs; i++)
--- 2675,2681 ----
* Get the required information for output conversion
* of all procedure arguments
************************************************************/
! if (!is_dml_trigger && !is_cmd_trigger)
{
prodesc->nargs = procStruct->pronargs;
for (i = 0; i < prodesc->nargs; i++)
*** a/src/pl/plperl/sql/plperl_trigger.sql
--- b/src/pl/plperl/sql/plperl_trigger.sql
***************
*** 169,171 **** CREATE FUNCTION direct_trigger() RETURNS trigger AS $$
--- 169,193 ----
$$ LANGUAGE plperl;
SELECT direct_trigger();
+
+ -- test plperl command triggers
+ create or replace function perlsnitch() returns command_trigger language plperl as $$
+ elog(NOTICE, "perlsnitch: "
+ . $_TD->{when} . " "
+ . $_TD->{tag} . " "
+ . $_TD->{schemaname} . " "
+ . $_TD->{objectname});
+ $$;
+
+ create command trigger perl_a_snitch after any command execute procedure perlsnitch();
+ create command trigger perl_b_snitch before any command execute procedure perlsnitch();
+
+ create or replace function foobar() returns int language sql as $$select 1;$$;
+ alter function foobar() cost 77;
+ drop function foobar();
+
+ create table foo();
+ drop table foo;
+
+ drop command trigger perl_a_snitch;
+ drop command trigger perl_b_snitch;
*** a/src/pl/plpgsql/src/pl_comp.c
--- b/src/pl/plpgsql/src/pl_comp.c
***************
*** 263,269 **** do_compile(FunctionCallInfo fcinfo,
bool forValidator)
{
Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
! bool is_trigger = CALLED_AS_TRIGGER(fcinfo);
Datum prosrcdatum;
bool isnull;
char *proc_source;
--- 263,270 ----
bool forValidator)
{
Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
! bool is_dml_trigger = CALLED_AS_TRIGGER(fcinfo);
! bool is_cmd_trigger = CALLED_AS_COMMAND_TRIGGER(fcinfo);
Datum prosrcdatum;
bool isnull;
char *proc_source;
***************
*** 345,356 **** do_compile(FunctionCallInfo fcinfo,
function->fn_oid = fcinfo->flinfo->fn_oid;
function->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
function->fn_tid = procTup->t_self;
- function->fn_is_trigger = is_trigger;
function->fn_input_collation = fcinfo->fncollation;
function->fn_cxt = func_cxt;
function->out_param_varno = -1; /* set up for no OUT param */
function->resolve_option = plpgsql_variable_conflict;
/*
* Initialize the compiler, particularly the namespace stack. The
* outermost namespace contains function parameters and other special
--- 346,363 ----
function->fn_oid = fcinfo->flinfo->fn_oid;
function->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
function->fn_tid = procTup->t_self;
function->fn_input_collation = fcinfo->fncollation;
function->fn_cxt = func_cxt;
function->out_param_varno = -1; /* set up for no OUT param */
function->resolve_option = plpgsql_variable_conflict;
+ if (is_dml_trigger)
+ function->fn_is_trigger = PLPGSQL_DML_TRIGGER;
+ else if (is_cmd_trigger)
+ function->fn_is_trigger = PLPGSQL_CMD_TRIGGER;
+ else
+ function->fn_is_trigger = PLPGSQL_NOT_TRIGGER;
+
/*
* Initialize the compiler, particularly the namespace stack. The
* outermost namespace contains function parameters and other special
***************
*** 367,375 **** do_compile(FunctionCallInfo fcinfo,
sizeof(PLpgSQL_datum *) * datums_alloc);
datums_last = 0;
! switch (is_trigger)
{
! case false:
/*
* Fetch info about the procedure's parameters. Allocations aren't
--- 374,382 ----
sizeof(PLpgSQL_datum *) * datums_alloc);
datums_last = 0;
! switch (function->fn_is_trigger)
{
! case PLPGSQL_NOT_TRIGGER:
/*
* Fetch info about the procedure's parameters. Allocations aren't
***************
*** 568,574 **** do_compile(FunctionCallInfo fcinfo,
ReleaseSysCache(typeTup);
break;
! case true:
/* Trigger procedure's return type is unknown yet */
function->fn_rettype = InvalidOid;
function->fn_retbyval = false;
--- 575,581 ----
ReleaseSysCache(typeTup);
break;
! case PLPGSQL_DML_TRIGGER:
/* Trigger procedure's return type is unknown yet */
function->fn_rettype = InvalidOid;
function->fn_retbyval = false;
***************
*** 672,679 **** do_compile(FunctionCallInfo fcinfo,
break;
default:
! elog(ERROR, "unrecognized function typecode: %d", (int) is_trigger);
break;
}
--- 679,741 ----
break;
+ case PLPGSQL_CMD_TRIGGER:
+ function->fn_rettype = VOIDOID;
+ function->fn_retbyval = false;
+ function->fn_retistuple = true;
+ function->fn_retset = false;
+
+ /* shouldn't be any declared arguments */
+ if (procStruct->pronargs != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("command trigger functions cannot have declared arguments")));
+
+ /* Add the variable tg_when */
+ var = plpgsql_build_variable("tg_when", 0,
+ plpgsql_build_datatype(TEXTOID,
+ -1,
+ function->fn_input_collation),
+ true);
+ function->tg_when_varno = var->dno;
+
+ /* Add the variable tg_tag */
+ var = plpgsql_build_variable("tg_tag", 0,
+ plpgsql_build_datatype(TEXTOID,
+ -1,
+ function->fn_input_collation),
+ true);
+ function->tg_tag_varno = var->dno;
+
+ /* Add the variable tg_objectid */
+ var = plpgsql_build_variable("tg_objectid", 0,
+ plpgsql_build_datatype(OIDOID,
+ -1,
+ InvalidOid),
+ true);
+ function->tg_objectid_varno = var->dno;
+
+ /* Add the variable tg_schemaname */
+ var = plpgsql_build_variable("tg_schemaname", 0,
+ plpgsql_build_datatype(NAMEOID,
+ -1,
+ InvalidOid),
+ true);
+ function->tg_schemaname_varno = var->dno;
+
+ /* Add the variable tg_objectname */
+ var = plpgsql_build_variable("tg_objectname", 0,
+ plpgsql_build_datatype(NAMEOID,
+ -1,
+ InvalidOid),
+ true);
+ function->tg_objectname_varno = var->dno;
+
+ break;
+
default:
! elog(ERROR, "unrecognized function typecode: %d",
! (int) function->fn_is_trigger);
break;
}
***************
*** 803,809 **** plpgsql_compile_inline(char *proc_source)
compile_tmp_cxt = MemoryContextSwitchTo(func_cxt);
function->fn_signature = pstrdup(func_name);
! function->fn_is_trigger = false;
function->fn_input_collation = InvalidOid;
function->fn_cxt = func_cxt;
function->out_param_varno = -1; /* set up for no OUT param */
--- 865,871 ----
compile_tmp_cxt = MemoryContextSwitchTo(func_cxt);
function->fn_signature = pstrdup(func_name);
! function->fn_is_trigger = PLPGSQL_NOT_TRIGGER;
function->fn_input_collation = InvalidOid;
function->fn_cxt = func_cxt;
function->out_param_varno = -1; /* set up for no OUT param */
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
***************
*** 773,778 **** plpgsql_exec_trigger(PLpgSQL_function *func,
--- 773,909 ----
return rettup;
}
+ void plpgsql_exec_command_trigger(PLpgSQL_function *func,
+ CommandTriggerData *trigdata)
+ {
+ PLpgSQL_execstate estate;
+ ErrorContextCallback plerrcontext;
+ int i;
+ int rc;
+ PLpgSQL_var *var;
+
+ /*
+ * Setup the execution state
+ */
+ plpgsql_estate_setup(&estate, func, NULL);
+
+ /*
+ * Setup error traceback support for ereport()
+ */
+ plerrcontext.callback = plpgsql_exec_error_callback;
+ plerrcontext.arg = &estate;
+ plerrcontext.previous = error_context_stack;
+ error_context_stack = &plerrcontext;
+
+ /*
+ * Make local execution copies of all the datums
+ */
+ estate.err_text = gettext_noop("during initialization of execution state");
+ for (i = 0; i < estate.ndatums; i++)
+ estate.datums[i] = copy_plpgsql_datum(func->datums[i]);
+
+ /*
+ * Assign the special tg_ variables
+ */
+ var = (PLpgSQL_var *) (estate.datums[func->tg_when_varno]);
+ var->value = CStringGetTextDatum(trigdata->when);
+ var->isnull = false;
+ var->freeval = true;
+
+ var = (PLpgSQL_var *) (estate.datums[func->tg_tag_varno]);
+ var->value = CStringGetTextDatum(trigdata->tag);
+ var->isnull = false;
+ var->freeval = true;
+
+ var = (PLpgSQL_var *) (estate.datums[func->tg_objectid_varno]);
+ if (trigdata->objectId == InvalidOid)
+ {
+ var->isnull = true;
+ }
+ else
+ {
+ var->value = ObjectIdGetDatum(trigdata->objectId);
+ var->isnull = false;
+ }
+ var->freeval = false;
+
+ var = (PLpgSQL_var *) (estate.datums[func->tg_schemaname_varno]);
+ if (trigdata->schemaname == NULL)
+ {
+ var->isnull = true;
+ }
+ else
+ {
+ var->value = DirectFunctionCall1(namein,
+ CStringGetDatum(trigdata->schemaname));
+ var->isnull = false;
+ }
+ var->freeval = true;
+
+ var = (PLpgSQL_var *) (estate.datums[func->tg_objectname_varno]);
+ if (trigdata->objectname == NULL)
+ {
+ var->isnull = true;
+ }
+ else
+ {
+ var->value = DirectFunctionCall1(namein,
+ CStringGetDatum(trigdata->objectname));
+ var->isnull = false;
+ }
+ var->freeval = true;
+
+ /*
+ * Let the instrumentation plugin peek at this function
+ */
+ if (*plugin_ptr && (*plugin_ptr)->func_beg)
+ ((*plugin_ptr)->func_beg) (&estate, func);
+
+ /*
+ * Now call the toplevel block of statements
+ */
+ estate.err_text = NULL;
+ estate.err_stmt = (PLpgSQL_stmt *) (func->action);
+ rc = exec_stmt_block(&estate, func->action);
+ if (rc != PLPGSQL_RC_RETURN)
+ {
+ estate.err_stmt = NULL;
+ estate.err_text = NULL;
+
+ /*
+ * Provide a more helpful message if a CONTINUE or RAISE has been used
+ * outside the context it can work in.
+ */
+ if (rc == PLPGSQL_RC_CONTINUE)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("CONTINUE cannot be used outside a loop")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
+ errmsg("control reached end of trigger procedure without RETURN")));
+ }
+
+ estate.err_stmt = NULL;
+ estate.err_text = gettext_noop("during function exit");
+
+ /*
+ * Let the instrumentation plugin peek at this function
+ */
+ if (*plugin_ptr && (*plugin_ptr)->func_end)
+ ((*plugin_ptr)->func_end) (&estate, func);
+
+ /* Clean up any leftover temporary memory */
+ plpgsql_destroy_econtext(&estate);
+ exec_eval_cleanup(&estate);
+
+ /*
+ * Pop the error context stack
+ */
+ error_context_stack = plerrcontext.previous;
+
+ return;
+ }
/*
* error context callback to let us supply a call-stack traceback
*** a/src/pl/plpgsql/src/pl_handler.c
--- b/src/pl/plpgsql/src/pl_handler.c
***************
*** 118,123 **** plpgsql_call_handler(PG_FUNCTION_ARGS)
--- 118,126 ----
if (CALLED_AS_TRIGGER(fcinfo))
retval = PointerGetDatum(plpgsql_exec_trigger(func,
(TriggerData *) fcinfo->context));
+ else if (CALLED_AS_COMMAND_TRIGGER(fcinfo))
+ plpgsql_exec_command_trigger(func,
+ (CommandTriggerData *) fcinfo->context);
else
retval = plpgsql_exec_function(func, fcinfo);
}
***************
*** 224,230 **** plpgsql_validator(PG_FUNCTION_ARGS)
Oid *argtypes;
char **argnames;
char *argmodes;
! bool istrigger = false;
int i;
/* Get the new function's pg_proc entry */
--- 227,234 ----
Oid *argtypes;
char **argnames;
char *argmodes;
! bool is_dml_trigger = false;
! bool is_cmd_trigger = false;
int i;
/* Get the new function's pg_proc entry */
***************
*** 242,248 **** plpgsql_validator(PG_FUNCTION_ARGS)
/* we assume OPAQUE with no arguments means a trigger */
if (proc->prorettype == TRIGGEROID ||
(proc->prorettype == OPAQUEOID && proc->pronargs == 0))
! istrigger = true;
else if (proc->prorettype != RECORDOID &&
proc->prorettype != VOIDOID &&
!IsPolymorphicType(proc->prorettype))
--- 246,254 ----
/* we assume OPAQUE with no arguments means a trigger */
if (proc->prorettype == TRIGGEROID ||
(proc->prorettype == OPAQUEOID && proc->pronargs == 0))
! is_dml_trigger = true;
! else if (proc->prorettype == CMDTRIGGEROID)
! is_cmd_trigger = true;
else if (proc->prorettype != RECORDOID &&
proc->prorettype != VOIDOID &&
!IsPolymorphicType(proc->prorettype))
***************
*** 273,279 **** plpgsql_validator(PG_FUNCTION_ARGS)
{
FunctionCallInfoData fake_fcinfo;
FmgrInfo flinfo;
- TriggerData trigdata;
int rc;
/*
--- 279,284 ----
***************
*** 291,302 **** plpgsql_validator(PG_FUNCTION_ARGS)
fake_fcinfo.flinfo = &flinfo;
flinfo.fn_oid = funcoid;
flinfo.fn_mcxt = CurrentMemoryContext;
! if (istrigger)
{
MemSet(&trigdata, 0, sizeof(trigdata));
trigdata.type = T_TriggerData;
fake_fcinfo.context = (Node *) &trigdata;
}
/* Test-compile the function */
plpgsql_compile(&fake_fcinfo, true);
--- 296,315 ----
fake_fcinfo.flinfo = &flinfo;
flinfo.fn_oid = funcoid;
flinfo.fn_mcxt = CurrentMemoryContext;
! if (is_dml_trigger)
{
+ TriggerData trigdata;
MemSet(&trigdata, 0, sizeof(trigdata));
trigdata.type = T_TriggerData;
fake_fcinfo.context = (Node *) &trigdata;
}
+ else if (is_cmd_trigger)
+ {
+ CommandTriggerData trigdata;
+ MemSet(&trigdata, 0, sizeof(trigdata));
+ trigdata.type = T_CommandTriggerData;
+ fake_fcinfo.context = (Node *) &trigdata;
+ }
/* Test-compile the function */
plpgsql_compile(&fake_fcinfo, true);
*** a/src/pl/plpgsql/src/plpgsql.h
--- b/src/pl/plpgsql/src/plpgsql.h
***************
*** 20,25 ****
--- 20,26 ----
#include "access/xact.h"
#include "commands/trigger.h"
+ #include "commands/cmdtrigger.h"
#include "executor/spi.h"
/**********************************************************************
***************
*** 675,680 **** typedef struct PLpgSQL_func_hashkey
--- 676,687 ----
Oid argtypes[FUNC_MAX_ARGS];
} PLpgSQL_func_hashkey;
+ typedef enum PLpgSQL_trigtype
+ {
+ PLPGSQL_DML_TRIGGER,
+ PLPGSQL_CMD_TRIGGER,
+ PLPGSQL_NOT_TRIGGER
+ } PLpgSQL_trigtype;
typedef struct PLpgSQL_function
{ /* Complete compiled function */
***************
*** 682,688 **** typedef struct PLpgSQL_function
Oid fn_oid;
TransactionId fn_xmin;
ItemPointerData fn_tid;
! bool fn_is_trigger;
Oid fn_input_collation;
PLpgSQL_func_hashkey *fn_hashkey; /* back-link to hashtable key */
MemoryContext fn_cxt;
--- 689,695 ----
Oid fn_oid;
TransactionId fn_xmin;
ItemPointerData fn_tid;
! PLpgSQL_trigtype fn_is_trigger;
Oid fn_input_collation;
PLpgSQL_func_hashkey *fn_hashkey; /* back-link to hashtable key */
MemoryContext fn_cxt;
***************
*** 713,718 **** typedef struct PLpgSQL_function
--- 720,730 ----
int tg_nargs_varno;
int tg_argv_varno;
+ int tg_tag_varno;
+ int tg_objectid_varno;
+ int tg_schemaname_varno;
+ int tg_objectname_varno;
+
PLpgSQL_resolve_option resolve_option;
int ndatums;
***************
*** 920,925 **** extern Datum plpgsql_exec_function(PLpgSQL_function *func,
--- 932,939 ----
FunctionCallInfo fcinfo);
extern HeapTuple plpgsql_exec_trigger(PLpgSQL_function *func,
TriggerData *trigdata);
+ extern void plpgsql_exec_command_trigger(PLpgSQL_function *func,
+ CommandTriggerData *trigdata);
extern void plpgsql_xact_cb(XactEvent event, void *arg);
extern void plpgsql_subxact_cb(SubXactEvent event, SubTransactionId mySubid,
SubTransactionId parentSubid, void *arg);
*** a/src/pl/plpython/expected/plpython_trigger.out
--- b/src/pl/plpython/expected/plpython_trigger.out
***************
*** 567,569 **** SELECT * FROM composite_trigger_test;
--- 567,596 ----
(3,f) | (7,t)
(1 row)
+ -- test plpython command triggers
+ create or replace function pysnitch() returns command_trigger language plpythonu as $$
+ plpy.notice(" pysnitch: %s %s %s.%s [%s]" %
+ (TD["when"], TD["tag"], TD["schemaname"], TD["objectname"]));
+ $$;
+ create command trigger py_a_snitch after any command execute procedure pysnitch();
+ create command trigger py_b_snitch before any command execute procedure pysnitch();
+ create or replace function foobar() returns int language sql as $$select 1;$$;
+ ERROR: TypeError: not enough arguments for format string
+ CONTEXT: Traceback (most recent call last):
+ PL/Python function "pysnitch", line 3, in
+ (TD["when"], TD["tag"], TD["schemaname"], TD["objectname"]));
+ PL/Python function "pysnitch"
+ alter function foobar() cost 77;
+ ERROR: function foobar() does not exist
+ drop function foobar();
+ ERROR: function foobar() does not exist
+ create table foo();
+ ERROR: TypeError: not enough arguments for format string
+ CONTEXT: Traceback (most recent call last):
+ PL/Python function "pysnitch", line 3, in
+ (TD["when"], TD["tag"], TD["schemaname"], TD["objectname"]));
+ PL/Python function "pysnitch"
+ drop table foo;
+ ERROR: table "foo" does not exist
+ drop command trigger py_a_snitch;
+ drop command trigger py_b_snitch;
*** a/src/pl/plpython/plpy_exec.c
--- b/src/pl/plpython/plpy_exec.c
***************
*** 342,347 **** PLy_exec_trigger(FunctionCallInfo fcinfo, PLyProcedure *proc)
--- 342,420 ----
return rv;
}
+ /* command trigger handler
+ */
+ void
+ PLy_exec_command_trigger(FunctionCallInfo fcinfo, PLyProcedure *proc)
+ {
+ CommandTriggerData *tdata;
+
+ Assert(CALLED_AS_COMMAND_TRIGGER(fcinfo));
+
+ tdata = (CommandTriggerData *) fcinfo->context;
+
+ PG_TRY();
+ {
+ /* build command trigger args */
+ PyObject *pltwhen,
+ *plttag,
+ *pltobjectid,
+ *pltschemaname,
+ *pltobjectname;
+ PyObject *volatile pltdata = NULL;
+ char *stroid;
+
+ pltdata = PyDict_New();
+ if (!pltdata)
+ PLy_elog(ERROR, "could not create new dictionary while building command trigger arguments");
+
+ pltwhen = PyString_FromString(tdata->when);
+ PyDict_SetItemString(pltdata, "when", pltwhen);
+ Py_DECREF(pltwhen);
+
+ plttag = PyString_FromString(tdata->tag);
+ PyDict_SetItemString(pltdata, "tag", plttag);
+ Py_DECREF(plttag);
+
+ if (tdata->objectId == InvalidOid)
+ stroid = pstrdup("NULL");
+ else
+ stroid = DatumGetCString(
+ DirectFunctionCall1(oidout, ObjectIdGetDatum(tdata->objectId)));
+ pltobjectid = PyString_FromString(stroid);
+ PyDict_SetItemString(pltdata, "objectId", pltobjectid);
+ Py_DECREF(pltobjectid);
+ pfree(stroid);
+
+ pltobjectname = PyString_FromString(
+ tdata->objectname == NULL ? "NULL" : tdata->objectname);
+ PyDict_SetItemString(pltdata, "objectname", pltobjectname);
+ Py_DECREF(pltobjectname);
+
+ pltschemaname = PyString_FromString(
+ tdata->schemaname == NULL ? "NULL" : tdata->schemaname);
+ PyDict_SetItemString(pltdata, "schemaname", pltschemaname);
+ Py_DECREF(pltschemaname);
+
+ /* now call the procedure */
+ PLy_procedure_call(proc, "TD", pltdata);
+
+ /*
+ * Disconnect from SPI manager
+ */
+ if (SPI_finish() != SPI_OK_FINISH)
+ elog(ERROR, "SPI_finish failed");
+
+ }
+ PG_CATCH();
+ {
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ return;
+ }
+
/* helper functions for Python code execution */
static PyObject *
*** a/src/pl/plpython/plpy_exec.h
--- b/src/pl/plpython/plpy_exec.h
***************
*** 9,13 ****
--- 9,14 ----
extern Datum PLy_exec_function(FunctionCallInfo fcinfo, PLyProcedure *proc);
extern HeapTuple PLy_exec_trigger(FunctionCallInfo fcinfo, PLyProcedure *proc);
+ extern void PLy_exec_command_trigger(FunctionCallInfo fcinfo, PLyProcedure *proc);
#endif /* PLPY_EXEC_H */
*** a/src/pl/plpython/plpy_main.c
--- b/src/pl/plpython/plpy_main.c
***************
*** 63,68 **** PG_FUNCTION_INFO_V1(plpython2_inline_handler);
--- 63,69 ----
static bool PLy_procedure_is_trigger(Form_pg_proc procStruct);
+ static bool PLy_procedure_is_command_trigger(Form_pg_proc procStruct);
static void plpython_error_callback(void *arg);
static void plpython_inline_error_callback(void *arg);
static void PLy_init_interp(void);
***************
*** 156,162 **** plpython_validator(PG_FUNCTION_ARGS)
Oid funcoid = PG_GETARG_OID(0);
HeapTuple tuple;
Form_pg_proc procStruct;
! bool is_trigger;
if (!check_function_bodies)
{
--- 157,163 ----
Oid funcoid = PG_GETARG_OID(0);
HeapTuple tuple;
Form_pg_proc procStruct;
! bool is_dml_trigger, is_cmd_trigger;
if (!check_function_bodies)
{
***************
*** 169,179 **** plpython_validator(PG_FUNCTION_ARGS)
elog(ERROR, "cache lookup failed for function %u", funcoid);
procStruct = (Form_pg_proc) GETSTRUCT(tuple);
! is_trigger = PLy_procedure_is_trigger(procStruct);
ReleaseSysCache(tuple);
! PLy_procedure_get(funcoid, is_trigger);
PG_RETURN_VOID();
}
--- 170,181 ----
elog(ERROR, "cache lookup failed for function %u", funcoid);
procStruct = (Form_pg_proc) GETSTRUCT(tuple);
! is_dml_trigger = PLy_procedure_is_trigger(procStruct);
! is_cmd_trigger = PLy_procedure_is_command_trigger(procStruct);
ReleaseSysCache(tuple);
! PLy_procedure_get(funcoid, is_dml_trigger, is_cmd_trigger);
PG_RETURN_VOID();
}
***************
*** 220,233 **** plpython_call_handler(PG_FUNCTION_ARGS)
{
HeapTuple trv;
! proc = PLy_procedure_get(fcinfo->flinfo->fn_oid, true);
exec_ctx->curr_proc = proc;
trv = PLy_exec_trigger(fcinfo, proc);
retval = PointerGetDatum(trv);
}
else
{
! proc = PLy_procedure_get(fcinfo->flinfo->fn_oid, false);
exec_ctx->curr_proc = proc;
retval = PLy_exec_function(fcinfo, proc);
}
--- 222,241 ----
{
HeapTuple trv;
! proc = PLy_procedure_get(fcinfo->flinfo->fn_oid, true, false);
exec_ctx->curr_proc = proc;
trv = PLy_exec_trigger(fcinfo, proc);
retval = PointerGetDatum(trv);
}
+ else if (CALLED_AS_COMMAND_TRIGGER(fcinfo))
+ {
+ proc = PLy_procedure_get(fcinfo->flinfo->fn_oid, false, true);
+ exec_ctx->curr_proc = proc;
+ PLy_exec_command_trigger(fcinfo, proc);
+ }
else
{
! proc = PLy_procedure_get(fcinfo->flinfo->fn_oid, false, false);
exec_ctx->curr_proc = proc;
retval = PLy_exec_function(fcinfo, proc);
}
***************
*** 337,342 **** static bool PLy_procedure_is_trigger(Form_pg_proc procStruct)
--- 345,355 ----
procStruct->pronargs == 0));
}
+ static bool PLy_procedure_is_command_trigger(Form_pg_proc procStruct)
+ {
+ return (procStruct->prorettype == CMDTRIGGEROID);
+ }
+
static void
plpython_error_callback(void *arg)
{
*** a/src/pl/plpython/plpy_procedure.c
--- b/src/pl/plpython/plpy_procedure.c
***************
*** 25,31 ****
static HTAB *PLy_procedure_cache = NULL;
static HTAB *PLy_trigger_cache = NULL;
! static PLyProcedure *PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger);
static bool PLy_procedure_argument_valid(PLyTypeInfo *arg);
static bool PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup);
static char *PLy_procedure_munge_source(const char *name, const char *src);
--- 25,32 ----
static HTAB *PLy_procedure_cache = NULL;
static HTAB *PLy_trigger_cache = NULL;
! static PLyProcedure *PLy_procedure_create(HeapTuple procTup, Oid fn_oid,
! bool is_dml_trigger, bool is_cmd_trigger);
static bool PLy_procedure_argument_valid(PLyTypeInfo *arg);
static bool PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup);
static char *PLy_procedure_munge_source(const char *name, const char *src);
***************
*** 73,79 **** PLy_procedure_name(PLyProcedure *proc)
* function calls.
*/
PLyProcedure *
! PLy_procedure_get(Oid fn_oid, bool is_trigger)
{
HeapTuple procTup;
PLyProcedureEntry *volatile entry;
--- 74,80 ----
* function calls.
*/
PLyProcedure *
! PLy_procedure_get(Oid fn_oid, bool is_dml_trigger, bool is_cmd_trigger)
{
HeapTuple procTup;
PLyProcedureEntry *volatile entry;
***************
*** 84,90 **** PLy_procedure_get(Oid fn_oid, bool is_trigger)
elog(ERROR, "cache lookup failed for function %u", fn_oid);
/* Look for the function in the corresponding cache */
! if (is_trigger)
entry = hash_search(PLy_trigger_cache,
&fn_oid, HASH_ENTER, &found);
else
--- 85,91 ----
elog(ERROR, "cache lookup failed for function %u", fn_oid);
/* Look for the function in the corresponding cache */
! if (is_dml_trigger || is_cmd_trigger)
entry = hash_search(PLy_trigger_cache,
&fn_oid, HASH_ENTER, &found);
else
***************
*** 96,116 **** PLy_procedure_get(Oid fn_oid, bool is_trigger)
if (!found)
{
/* Haven't found it, create a new cache entry */
! entry->proc = PLy_procedure_create(procTup, fn_oid, is_trigger);
}
else if (!PLy_procedure_valid(entry->proc, procTup))
{
/* Found it, but it's invalid, free and reuse the cache entry */
PLy_procedure_delete(entry->proc);
PLy_free(entry->proc);
! entry->proc = PLy_procedure_create(procTup, fn_oid, is_trigger);
}
/* Found it and it's valid, it's fine to use it */
}
PG_CATCH();
{
/* Do not leave an uninitialised entry in the cache */
! if (is_trigger)
hash_search(PLy_trigger_cache,
&fn_oid, HASH_REMOVE, NULL);
else
--- 97,119 ----
if (!found)
{
/* Haven't found it, create a new cache entry */
! entry->proc = PLy_procedure_create(procTup, fn_oid,
! is_dml_trigger, is_cmd_trigger);
}
else if (!PLy_procedure_valid(entry->proc, procTup))
{
/* Found it, but it's invalid, free and reuse the cache entry */
PLy_procedure_delete(entry->proc);
PLy_free(entry->proc);
! entry->proc = PLy_procedure_create(procTup, fn_oid,
! is_dml_trigger, is_cmd_trigger);
}
/* Found it and it's valid, it's fine to use it */
}
PG_CATCH();
{
/* Do not leave an uninitialised entry in the cache */
! if (is_dml_trigger || is_cmd_trigger)
hash_search(PLy_trigger_cache,
&fn_oid, HASH_REMOVE, NULL);
else
***************
*** 129,135 **** PLy_procedure_get(Oid fn_oid, bool is_trigger)
* Create a new PLyProcedure structure
*/
static PLyProcedure *
! PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
{
char procName[NAMEDATALEN + 256];
Form_pg_proc procStruct;
--- 132,139 ----
* Create a new PLyProcedure structure
*/
static PLyProcedure *
! PLy_procedure_create(HeapTuple procTup, Oid fn_oid,
! bool is_dml_trigger, bool is_cmd_trigger)
{
char procName[NAMEDATALEN + 256];
Form_pg_proc procStruct;
***************
*** 173,179 **** PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
* get information required for output conversion of the return value,
* but only if this isn't a trigger.
*/
! if (!is_trigger)
{
HeapTuple rvTypeTup;
Form_pg_type rvTypeStruct;
--- 177,183 ----
* get information required for output conversion of the return value,
* but only if this isn't a trigger.
*/
! if (!is_dml_trigger && !is_cmd_trigger)
{
HeapTuple rvTypeTup;
Form_pg_type rvTypeStruct;
*** a/src/pl/plpython/plpy_procedure.h
--- b/src/pl/plpython/plpy_procedure.h
***************
*** 41,47 **** typedef struct PLyProcedureEntry
/* PLyProcedure manipulation */
extern char *PLy_procedure_name(PLyProcedure *proc);
! extern PLyProcedure *PLy_procedure_get(Oid fn_oid, bool is_trigger);
extern void PLy_procedure_compile(PLyProcedure *proc, const char *src);
extern void PLy_procedure_delete(PLyProcedure *proc);
--- 41,48 ----
/* PLyProcedure manipulation */
extern char *PLy_procedure_name(PLyProcedure *proc);
! extern PLyProcedure *PLy_procedure_get(Oid fn_oid,
! bool is_dml_trigger, bool is_cmd_trigger);
extern void PLy_procedure_compile(PLyProcedure *proc, const char *src);
extern void PLy_procedure_delete(PLyProcedure *proc);
*** a/src/pl/plpython/sql/plpython_trigger.sql
--- b/src/pl/plpython/sql/plpython_trigger.sql
***************
*** 346,348 **** CREATE TRIGGER composite_trigger BEFORE INSERT ON composite_trigger_test
--- 346,367 ----
INSERT INTO composite_trigger_test VALUES (NULL, NULL);
SELECT * FROM composite_trigger_test;
+
+ -- test plpython command triggers
+ create or replace function pysnitch() returns command_trigger language plpythonu as $$
+ plpy.notice(" pysnitch: %s %s %s.%s [%s]" %
+ (TD["when"], TD["tag"], TD["schemaname"], TD["objectname"]));
+ $$;
+
+ create command trigger py_a_snitch after any command execute procedure pysnitch();
+ create command trigger py_b_snitch before any command execute procedure pysnitch();
+
+ create or replace function foobar() returns int language sql as $$select 1;$$;
+ alter function foobar() cost 77;
+ drop function foobar();
+
+ create table foo();
+ drop table foo;
+
+ drop command trigger py_a_snitch;
+ drop command trigger py_b_snitch;
*** a/src/pl/tcl/expected/pltcl_setup.out
--- b/src/pl/tcl/expected/pltcl_setup.out
***************
*** 519,521 **** select tcl_date_week(2001,10,24);
--- 519,544 ----
42
(1 row)
+ -- test pltcl command triggers
+ create or replace function tclsnitch() returns command_trigger language pltcl as $$
+ elog NOTICE " tclsnitch: $TG_when $TG_tag $TG_schemaname $TG_objectname"
+ $$;
+ create command trigger tcl_a_snitch after any command execute procedure tclsnitch();
+ create command trigger tcl_b_snitch before any command execute procedure tclsnitch();
+ create or replace function foobar() returns int language sql as $$select 1;$$;
+ NOTICE: tclsnitch: BEFORE CREATE FUNCTION public foobar
+ NOTICE: tclsnitch: AFTER CREATE FUNCTION public foobar
+ alter function foobar() cost 77;
+ NOTICE: tclsnitch: BEFORE ALTER FUNCTION public foobar
+ NOTICE: tclsnitch: AFTER ALTER FUNCTION public foobar
+ drop function foobar();
+ NOTICE: tclsnitch: BEFORE DROP FUNCTION public foobar
+ NOTICE: tclsnitch: AFTER DROP FUNCTION public foobar
+ create table foo();
+ NOTICE: tclsnitch: BEFORE CREATE TABLE public foo
+ NOTICE: tclsnitch: AFTER CREATE TABLE public foo
+ drop table foo;
+ NOTICE: tclsnitch: BEFORE DROP TABLE public foo
+ NOTICE: tclsnitch: AFTER DROP TABLE public foo
+ drop command trigger tcl_a_snitch;
+ drop command trigger tcl_b_snitch;
*** a/src/pl/tcl/pltcl.c
--- b/src/pl/tcl/pltcl.c
***************
*** 194,204 **** static Datum pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted);
static Datum pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted);
static HeapTuple pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted);
static void throw_tcl_error(Tcl_Interp *interp, const char *proname);
static pltcl_proc_desc *compile_pltcl_function(Oid fn_oid, Oid tgreloid,
! bool pltrusted);
static int pltcl_elog(ClientData cdata, Tcl_Interp *interp,
int argc, CONST84 char *argv[]);
--- 194,206 ----
static Datum pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted);
static HeapTuple pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted);
+ static void pltcl_command_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted);
static void throw_tcl_error(Tcl_Interp *interp, const char *proname);
static pltcl_proc_desc *compile_pltcl_function(Oid fn_oid, Oid tgreloid,
! bool is_cmd_trigger,
! bool pltrusted);
static int pltcl_elog(ClientData cdata, Tcl_Interp *interp,
int argc, CONST84 char *argv[]);
***************
*** 637,642 **** pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
--- 639,649 ----
pltcl_current_fcinfo = NULL;
retval = PointerGetDatum(pltcl_trigger_handler(fcinfo, pltrusted));
}
+ else if (CALLED_AS_COMMAND_TRIGGER(fcinfo))
+ {
+ pltcl_current_fcinfo = NULL;
+ pltcl_command_trigger_handler(fcinfo, pltrusted);
+ }
else
{
pltcl_current_fcinfo = fcinfo;
***************
*** 678,684 **** pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted)
/* Find or compile the function */
prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid,
! pltrusted);
pltcl_current_prodesc = prodesc;
--- 685,691 ----
/* Find or compile the function */
prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid,
! false, pltrusted);
pltcl_current_prodesc = prodesc;
***************
*** 837,842 **** pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
--- 844,850 ----
/* Find or compile the function */
prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
RelationGetRelid(trigdata->tg_relation),
+ false, /* not a command trigger */
pltrusted);
pltcl_current_prodesc = prodesc;
***************
*** 1123,1128 **** pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
--- 1131,1210 ----
return rettup;
}
+ /**********************************************************************
+ * pltcl_command_trigger_handler() - Handler for command trigger calls
+ **********************************************************************/
+ static void
+ pltcl_command_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
+ {
+ pltcl_proc_desc *prodesc;
+ Tcl_Interp *volatile interp;
+ CommandTriggerData *tdata = (CommandTriggerData *) fcinfo->context;
+ char *stroid;
+ Tcl_DString tcl_cmd;
+ int tcl_rc;
+
+ /* Connect to SPI manager */
+ if (SPI_connect() != SPI_OK_CONNECT)
+ elog(ERROR, "could not connect to SPI manager");
+
+ /* Find or compile the function */
+ prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
+ InvalidOid, true, pltrusted);
+
+ pltcl_current_prodesc = prodesc;
+
+ interp = prodesc->interp_desc->interp;
+
+ /************************************************************
+ * Create the tcl command to call the internal
+ * proc in the interpreter
+ ************************************************************/
+ Tcl_DStringInit(&tcl_cmd);
+ Tcl_DStringAppendElement(&tcl_cmd, prodesc->internal_proname);
+ PG_TRY();
+ {
+ Tcl_DStringAppendElement(&tcl_cmd, tdata->when);
+ Tcl_DStringAppendElement(&tcl_cmd, tdata->tag);
+
+ if (tdata->objectId == InvalidOid)
+ stroid = pstrdup("NULL");
+ else
+ stroid = DatumGetCString(
+ DirectFunctionCall1(oidout, ObjectIdGetDatum(tdata->objectId)));
+ Tcl_DStringAppendElement(&tcl_cmd, stroid);
+ pfree(stroid);
+
+ Tcl_DStringAppendElement(&tcl_cmd, tdata->schemaname);
+ Tcl_DStringAppendElement(&tcl_cmd, tdata->objectname);
+ }
+ PG_CATCH();
+ {
+ Tcl_DStringFree(&tcl_cmd);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ /************************************************************
+ * Call the Tcl function
+ *
+ * We assume no PG error can be thrown directly from this call.
+ ************************************************************/
+ tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd));
+ Tcl_DStringFree(&tcl_cmd);
+
+ /************************************************************
+ * Check for errors reported by Tcl.
+ ************************************************************/
+ if (tcl_rc != TCL_OK)
+ throw_tcl_error(interp, prodesc->user_proname);
+
+ if (SPI_finish() != SPI_OK_FINISH)
+ elog(ERROR, "SPI_finish() failed");
+
+ return;
+ }
+
/**********************************************************************
* throw_tcl_error - ereport an error returned from the Tcl interpreter
***************
*** 1161,1167 **** throw_tcl_error(Tcl_Interp *interp, const char *proname)
* (InvalidOid) when compiling a plain function.
**********************************************************************/
static pltcl_proc_desc *
! compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
{
HeapTuple procTup;
Form_pg_proc procStruct;
--- 1243,1250 ----
* (InvalidOid) when compiling a plain function.
**********************************************************************/
static pltcl_proc_desc *
! compile_pltcl_function(Oid fn_oid, Oid tgreloid,
! bool is_cmd_trigger, bool pltrusted)
{
HeapTuple procTup;
Form_pg_proc procStruct;
***************
*** 1218,1224 **** compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
************************************************************/
if (prodesc == NULL)
{
! bool is_trigger = OidIsValid(tgreloid);
char internal_proname[128];
HeapTuple typeTup;
Form_pg_type typeStruct;
--- 1301,1307 ----
************************************************************/
if (prodesc == NULL)
{
! bool is_dml_trigger = OidIsValid(tgreloid);
char internal_proname[128];
HeapTuple typeTup;
Form_pg_type typeStruct;
***************
*** 1238,1247 **** compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
* "_trigger" when appropriate to ensure the normal and trigger
* cases are kept separate.
************************************************************/
! if (!is_trigger)
snprintf(internal_proname, sizeof(internal_proname),
"__PLTcl_proc_%u", fn_oid);
! else
snprintf(internal_proname, sizeof(internal_proname),
"__PLTcl_proc_%u_trigger", fn_oid);
--- 1321,1333 ----
* "_trigger" when appropriate to ensure the normal and trigger
* cases are kept separate.
************************************************************/
! if (!is_dml_trigger && !is_cmd_trigger)
snprintf(internal_proname, sizeof(internal_proname),
"__PLTcl_proc_%u", fn_oid);
! else if (is_cmd_trigger)
! snprintf(internal_proname, sizeof(internal_proname),
! "__PLTcl_proc_%u_cmdtrigger", fn_oid);
! else if (is_dml_trigger)
snprintf(internal_proname, sizeof(internal_proname),
"__PLTcl_proc_%u_trigger", fn_oid);
***************
*** 1279,1285 **** compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
* Get the required information for input conversion of the
* return value.
************************************************************/
! if (!is_trigger)
{
typeTup =
SearchSysCache1(TYPEOID,
--- 1365,1371 ----
* Get the required information for input conversion of the
* return value.
************************************************************/
! if (!is_dml_trigger && !is_cmd_trigger)
{
typeTup =
SearchSysCache1(TYPEOID,
***************
*** 1340,1346 **** compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
* Get the required information for output conversion
* of all procedure arguments
************************************************************/
! if (!is_trigger)
{
prodesc->nargs = procStruct->pronargs;
proc_internal_args[0] = '\0';
--- 1426,1432 ----
* Get the required information for output conversion
* of all procedure arguments
************************************************************/
! if (!is_dml_trigger && !is_cmd_trigger)
{
prodesc->nargs = procStruct->pronargs;
proc_internal_args[0] = '\0';
***************
*** 1390,1401 **** compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
ReleaseSysCache(typeTup);
}
}
! else
{
/* trigger procedure has fixed args */
strcpy(proc_internal_args,
"TG_name TG_relid TG_table_name TG_table_schema TG_relatts TG_when TG_level TG_op __PLTcl_Tup_NEW __PLTcl_Tup_OLD args");
}
/************************************************************
* Create the tcl command to define the internal
--- 1476,1493 ----
ReleaseSysCache(typeTup);
}
}
! else if (is_dml_trigger)
{
/* trigger procedure has fixed args */
strcpy(proc_internal_args,
"TG_name TG_relid TG_table_name TG_table_schema TG_relatts TG_when TG_level TG_op __PLTcl_Tup_NEW __PLTcl_Tup_OLD args");
}
+ else if (is_cmd_trigger)
+ {
+ /* command trigger procedure has fixed args */
+ strcpy(proc_internal_args,
+ "TG_when TG_tag TG_objectid TG_schemaname TG_objectname");
+ }
/************************************************************
* Create the tcl command to define the internal
***************
*** 1415,1434 **** compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
Tcl_DStringAppend(&proc_internal_body, "upvar #0 ", -1);
Tcl_DStringAppend(&proc_internal_body, internal_proname, -1);
Tcl_DStringAppend(&proc_internal_body, " GD\n", -1);
! if (!is_trigger)
! {
! for (i = 0; i < prodesc->nargs; i++)
! {
! if (prodesc->arg_is_rowtype[i])
! {
! snprintf(buf, sizeof(buf),
! "array set %d $__PLTcl_Tup_%d\n",
! i + 1, i + 1);
! Tcl_DStringAppend(&proc_internal_body, buf, -1);
! }
! }
! }
! else
{
Tcl_DStringAppend(&proc_internal_body,
"array set NEW $__PLTcl_Tup_NEW\n", -1);
--- 1507,1513 ----
Tcl_DStringAppend(&proc_internal_body, "upvar #0 ", -1);
Tcl_DStringAppend(&proc_internal_body, internal_proname, -1);
Tcl_DStringAppend(&proc_internal_body, " GD\n", -1);
! if (is_dml_trigger)
{
Tcl_DStringAppend(&proc_internal_body,
"array set NEW $__PLTcl_Tup_NEW\n", -1);
***************
*** 1444,1449 **** compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
--- 1523,1545 ----
"}\n"
"unset i v\n\n", -1);
}
+ else if (is_cmd_trigger)
+ {
+ /* no argument support for command triggers */
+ }
+ else
+ {
+ for (i = 0; i < prodesc->nargs; i++)
+ {
+ if (prodesc->arg_is_rowtype[i])
+ {
+ snprintf(buf, sizeof(buf),
+ "array set %d $__PLTcl_Tup_%d\n",
+ i + 1, i + 1);
+ Tcl_DStringAppend(&proc_internal_body, buf, -1);
+ }
+ }
+ }
/************************************************************
* Add user's function definition to proc body
*** a/src/pl/tcl/sql/pltcl_setup.sql
--- b/src/pl/tcl/sql/pltcl_setup.sql
***************
*** 559,561 **** $$ language pltcl immutable;
--- 559,579 ----
select tcl_date_week(2010,1,24);
select tcl_date_week(2001,10,24);
+
+ -- test pltcl command triggers
+ create or replace function tclsnitch() returns command_trigger language pltcl as $$
+ elog NOTICE " tclsnitch: $TG_when $TG_tag $TG_schemaname $TG_objectname"
+ $$;
+
+ create command trigger tcl_a_snitch after any command execute procedure tclsnitch();
+ create command trigger tcl_b_snitch before any command execute procedure tclsnitch();
+
+ create or replace function foobar() returns int language sql as $$select 1;$$;
+ alter function foobar() cost 77;
+ drop function foobar();
+
+ create table foo();
+ drop table foo;
+
+ drop command trigger tcl_a_snitch;
+ drop command trigger tcl_b_snitch;
*** /dev/null
--- b/src/test/regress/expected/cmdtriggers.out
***************
*** 0 ****
--- 1,608 ----
+ --
+ -- COMMAND TRIGGERS
+ --
+ create or replace function any_snitch()
+ returns command_trigger
+ language plpgsql
+ as $$
+ begin
+ -- can't output tg_objectid here that would break pg_regress
+ raise notice 'snitch: % any % %.%', tg_when, tg_tag, tg_schemaname, tg_objectname;
+ end;
+ $$;
+ create or replace function snitch()
+ returns command_trigger
+ language plpgsql
+ as $$
+ begin
+ -- can't output tg_objectid here that would break pg_regress
+ raise notice 'snitch: % % %.%', tg_when, tg_tag, tg_schemaname, tg_objectname;
+ end;
+ $$;
+ create command trigger snitch_before before any command execute procedure any_snitch();
+ create command trigger snitch_after_ after any command execute procedure any_snitch();
+ alter command trigger snitch_before set disable;
+ alter command trigger snitch_before set enable;
+ alter command trigger snitch_after_ rename to snitch_after;
+ create command trigger snitch_create_table after create table execute procedure snitch();
+ create command trigger snitch_create_seq after create sequence execute procedure snitch();
+ create command trigger snitch_create_view after create view execute procedure snitch();
+ create command trigger snitch_alter_table after alter table execute procedure snitch();
+ create command trigger snitch_alter_seq after alter sequence execute procedure snitch();
+ create command trigger snitch_alter_view after alter view execute procedure snitch();
+ create command trigger snitch_drop_table after drop table execute procedure snitch();
+ create command trigger snitch_create_function after create function execute procedure snitch();
+ create command trigger snitch_create_collation after create collation execute procedure snitch();
+ create command trigger snitch_alter_operator after alter operator execute procedure snitch();
+ create command trigger snitch_create_domain after create domain execute procedure snitch();
+ create command trigger snitch_alter_schema after alter schema execute procedure snitch();
+ create command trigger snitch_create_tsconfig after create text search configuration execute procedure snitch();
+ create command trigger snitch_create_tsdict after create text search dictionary execute procedure snitch();
+ create command trigger snitch_create_tsparser after create text search parser execute procedure snitch();
+ create command trigger snitch_create_tstmpl after create text search template execute procedure snitch();
+ create command trigger snitch_after_alter_function after alter function execute procedure snitch();
+ create command trigger snitch_create_cast after create cast execute procedure snitch();
+ create command trigger snitch_create_opclass after create operator class execute procedure snitch();
+ create command trigger snitch_create_trigger before create trigger execute procedure snitch();
+ create command trigger snitch_alter_trigger before alter trigger execute procedure snitch();
+ create command trigger snitch_drop_trigger before drop trigger execute procedure snitch();
+ create command trigger snitch_create_schema before create schema execute procedure snitch();
+ create command trigger snitch_drop_schema before drop schema execute procedure snitch();
+ create command trigger snitch_create_aggregate before create aggregate execute procedure snitch();
+ create command trigger snitch_alter_collation before alter collation execute procedure snitch();
+ create command trigger snitch_create_operator before create operator execute procedure snitch();
+ create command trigger snitch_alter_domain before alter domain execute procedure snitch();
+ create command trigger snitch_create_type before create type execute procedure snitch();
+ create command trigger snitch_alter_type before alter type execute procedure snitch();
+ create command trigger snitch_before_alter_function before alter function execute procedure snitch();
+ create command trigger snitch_alter_conversion before alter conversion execute procedure snitch();
+ create command trigger snitch_drop_agg before drop aggregate execute procedure snitch();
+ create command trigger snitch_drop_domain before drop domain execute procedure snitch();
+ create command trigger snitch_before_drop_tsconf before drop text search configuration execute procedure snitch();
+ create command trigger snitch_before_drop_tsdict before drop text search dictionary execute procedure snitch();
+ create command trigger snitch_before_drop_tsparser before drop text search parser execute procedure snitch();
+ create command trigger snitch_before_drop_tstmpl before drop text search template execute procedure snitch();
+ create command trigger snitch_vacuum before vacuum execute procedure snitch();
+ create command trigger snitch_reindex before reindex execute procedure snitch();
+ WARNING: REINDEX DATABASE triggers are not supported
+ DETAIL: The command trigger will not fire on REINDEX DATABASE.
+ create schema cmd;
+ NOTICE: snitch: BEFORE any CREATE SCHEMA .cmd
+ NOTICE: snitch: BEFORE CREATE SCHEMA .cmd
+ NOTICE: snitch: AFTER any CREATE SCHEMA .cmd
+ create schema cmd2;
+ NOTICE: snitch: BEFORE any CREATE SCHEMA .cmd2
+ NOTICE: snitch: BEFORE CREATE SCHEMA .cmd2
+ NOTICE: snitch: AFTER any CREATE SCHEMA .cmd2
+ create role regbob;
+ create table cmd.foo(id bigserial primary key);
+ NOTICE: CREATE TABLE will create implicit sequence "foo_id_seq" for serial column "foo.id"
+ NOTICE: snitch: BEFORE any CREATE SEQUENCE cmd.foo_id_seq
+ NOTICE: snitch: AFTER CREATE SEQUENCE cmd.foo_id_seq
+ NOTICE: snitch: AFTER any CREATE SEQUENCE cmd.foo_id_seq
+ NOTICE: snitch: BEFORE any CREATE TABLE cmd.foo
+ NOTICE: snitch: BEFORE any CREATE INDEX cmd.foo_pkey
+ NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo"
+ NOTICE: snitch: AFTER any CREATE INDEX cmd.foo_pkey
+ NOTICE: snitch: BEFORE any ALTER SEQUENCE cmd.foo_id_seq
+ NOTICE: snitch: AFTER ALTER SEQUENCE cmd.foo_id_seq
+ NOTICE: snitch: AFTER any ALTER SEQUENCE cmd.foo_id_seq
+ NOTICE: snitch: AFTER CREATE TABLE cmd.foo
+ NOTICE: snitch: AFTER any CREATE TABLE cmd.foo
+ create view cmd.v as select * from cmd.foo;
+ NOTICE: snitch: BEFORE any CREATE VIEW cmd.v
+ NOTICE: snitch: AFTER CREATE VIEW cmd.v
+ NOTICE: snitch: AFTER any CREATE VIEW cmd.v
+ alter table cmd.foo add column t text;
+ NOTICE: snitch: BEFORE any ALTER TABLE cmd.foo
+ NOTICE: snitch: AFTER ALTER TABLE cmd.foo
+ NOTICE: snitch: AFTER any ALTER TABLE cmd.foo
+ create table test9 (id int, stuff text);
+ NOTICE: snitch: BEFORE any CREATE TABLE public.test9
+ NOTICE: snitch: AFTER CREATE TABLE public.test9
+ NOTICE: snitch: AFTER any CREATE TABLE public.test9
+ alter table test9 rename to test;
+ NOTICE: snitch: BEFORE any ALTER TABLE public.test9
+ NOTICE: snitch: AFTER ALTER TABLE public.test
+ NOTICE: snitch: AFTER any ALTER TABLE public.test
+ alter table test set schema cmd;
+ NOTICE: snitch: BEFORE any ALTER TABLE public.test
+ NOTICE: snitch: AFTER ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER any ALTER TABLE cmd.test
+ alter table cmd.test rename column stuff to things;
+ NOTICE: snitch: BEFORE any ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER any ALTER TABLE cmd.test
+ alter table cmd.test add column alpha text;
+ NOTICE: snitch: BEFORE any ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER any ALTER TABLE cmd.test
+ alter table cmd.test alter column alpha set data type varchar(300);
+ NOTICE: snitch: BEFORE any ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER any ALTER TABLE cmd.test
+ alter table cmd.test alter column alpha set default 'test';
+ NOTICE: snitch: BEFORE any ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER any ALTER TABLE cmd.test
+ alter table cmd.test alter column alpha drop default;
+ NOTICE: snitch: BEFORE any ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER any ALTER TABLE cmd.test
+ alter table cmd.test alter column alpha set statistics 78;
+ NOTICE: snitch: BEFORE any ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER any ALTER TABLE cmd.test
+ alter table cmd.test alter column alpha set storage plain;
+ NOTICE: snitch: BEFORE any ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER any ALTER TABLE cmd.test
+ alter table cmd.test alter column alpha set not null;
+ NOTICE: snitch: BEFORE any ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER any ALTER TABLE cmd.test
+ alter table cmd.test alter column alpha drop not null;
+ NOTICE: snitch: BEFORE any ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER any ALTER TABLE cmd.test
+ alter table cmd.test alter column alpha set (n_distinct = -0.78);
+ NOTICE: snitch: BEFORE any ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER any ALTER TABLE cmd.test
+ alter table cmd.test alter column alpha reset (n_distinct);
+ NOTICE: snitch: BEFORE any ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER any ALTER TABLE cmd.test
+ alter table cmd.test drop column alpha;
+ NOTICE: snitch: BEFORE any ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER any ALTER TABLE cmd.test
+ alter table cmd.test add check (id > 2) not valid;
+ NOTICE: snitch: BEFORE any ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER any ALTER TABLE cmd.test
+ alter table cmd.test add check (id < 800000);
+ NOTICE: snitch: BEFORE any ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER any ALTER TABLE cmd.test
+ alter table cmd.test set without cluster;
+ NOTICE: snitch: BEFORE any ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER any ALTER TABLE cmd.test
+ alter table cmd.test set with oids;
+ NOTICE: snitch: BEFORE any ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER any ALTER TABLE cmd.test
+ alter table cmd.test set without oids;
+ NOTICE: snitch: BEFORE any ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER ALTER TABLE cmd.test
+ NOTICE: snitch: AFTER any ALTER TABLE cmd.test
+ create sequence test_seq_;
+ NOTICE: snitch: BEFORE any CREATE SEQUENCE public.test_seq_
+ NOTICE: snitch: AFTER CREATE SEQUENCE public.test_seq_
+ NOTICE: snitch: AFTER any CREATE SEQUENCE public.test_seq_
+ alter sequence test_seq_ owner to regbob;
+ NOTICE: snitch: BEFORE any ALTER SEQUENCE public.test_seq_
+ NOTICE: snitch: AFTER ALTER SEQUENCE public.test_seq_
+ NOTICE: snitch: AFTER any ALTER SEQUENCE public.test_seq_
+ alter sequence test_seq_ rename to test_seq;
+ NOTICE: snitch: BEFORE any ALTER SEQUENCE public.test_seq_
+ NOTICE: snitch: AFTER ALTER SEQUENCE public.test_seq
+ NOTICE: snitch: AFTER any ALTER SEQUENCE public.test_seq
+ alter sequence test_seq set schema cmd;
+ NOTICE: snitch: BEFORE any ALTER SEQUENCE public.test_seq
+ NOTICE: snitch: AFTER ALTER SEQUENCE cmd.test_seq
+ NOTICE: snitch: AFTER any ALTER SEQUENCE cmd.test_seq
+ alter sequence cmd.test_seq start with 3;
+ NOTICE: snitch: BEFORE any ALTER SEQUENCE cmd.test_seq
+ NOTICE: snitch: AFTER ALTER SEQUENCE cmd.test_seq
+ NOTICE: snitch: AFTER any ALTER SEQUENCE cmd.test_seq
+ alter sequence cmd.test_seq restart with 4;
+ NOTICE: snitch: BEFORE any ALTER SEQUENCE cmd.test_seq
+ NOTICE: snitch: AFTER ALTER SEQUENCE cmd.test_seq
+ NOTICE: snitch: AFTER any ALTER SEQUENCE cmd.test_seq
+ alter sequence cmd.test_seq minvalue 3;
+ NOTICE: snitch: BEFORE any ALTER SEQUENCE cmd.test_seq
+ NOTICE: snitch: AFTER ALTER SEQUENCE cmd.test_seq
+ NOTICE: snitch: AFTER any ALTER SEQUENCE cmd.test_seq
+ alter sequence cmd.test_seq no minvalue;
+ NOTICE: snitch: BEFORE any ALTER SEQUENCE cmd.test_seq
+ NOTICE: snitch: AFTER ALTER SEQUENCE cmd.test_seq
+ NOTICE: snitch: AFTER any ALTER SEQUENCE cmd.test_seq
+ alter sequence cmd.test_seq maxvalue 900000;
+ NOTICE: snitch: BEFORE any ALTER SEQUENCE cmd.test_seq
+ NOTICE: snitch: AFTER ALTER SEQUENCE cmd.test_seq
+ NOTICE: snitch: AFTER any ALTER SEQUENCE cmd.test_seq
+ alter sequence cmd.test_seq no maxvalue;
+ NOTICE: snitch: BEFORE any ALTER SEQUENCE cmd.test_seq
+ NOTICE: snitch: AFTER ALTER SEQUENCE cmd.test_seq
+ NOTICE: snitch: AFTER any ALTER SEQUENCE cmd.test_seq
+ alter sequence cmd.test_seq cache 876;
+ NOTICE: snitch: BEFORE any ALTER SEQUENCE cmd.test_seq
+ NOTICE: snitch: AFTER ALTER SEQUENCE cmd.test_seq
+ NOTICE: snitch: AFTER any ALTER SEQUENCE cmd.test_seq
+ alter sequence cmd.test_seq cycle;
+ NOTICE: snitch: BEFORE any ALTER SEQUENCE cmd.test_seq
+ NOTICE: snitch: AFTER ALTER SEQUENCE cmd.test_seq
+ NOTICE: snitch: AFTER any ALTER SEQUENCE cmd.test_seq
+ alter sequence cmd.test_seq no cycle;
+ NOTICE: snitch: BEFORE any ALTER SEQUENCE cmd.test_seq
+ NOTICE: snitch: AFTER ALTER SEQUENCE cmd.test_seq
+ NOTICE: snitch: AFTER any ALTER SEQUENCE cmd.test_seq
+ create view view_test as select id, things from cmd.test;
+ NOTICE: snitch: BEFORE any CREATE VIEW public.view_test
+ NOTICE: snitch: AFTER CREATE VIEW public.view_test
+ NOTICE: snitch: AFTER any CREATE VIEW public.view_test
+ alter view view_test owner to regbob;
+ NOTICE: snitch: BEFORE any ALTER VIEW public.view_test
+ NOTICE: snitch: AFTER ALTER VIEW public.view_test
+ NOTICE: snitch: AFTER any ALTER VIEW public.view_test
+ alter view view_test rename to view_test2;
+ NOTICE: snitch: BEFORE any ALTER VIEW public.view_test
+ NOTICE: snitch: AFTER ALTER VIEW public.view_test2
+ NOTICE: snitch: AFTER any ALTER VIEW public.view_test2
+ alter view view_test2 set schema cmd;
+ NOTICE: snitch: BEFORE any ALTER VIEW public.view_test2
+ NOTICE: snitch: AFTER ALTER VIEW cmd.view_test2
+ NOTICE: snitch: AFTER any ALTER VIEW cmd.view_test2
+ alter view cmd.view_test2 alter column id set default 9;
+ NOTICE: snitch: BEFORE any ALTER VIEW cmd.view_test2
+ NOTICE: snitch: AFTER ALTER VIEW cmd.view_test2
+ NOTICE: snitch: AFTER any ALTER VIEW cmd.view_test2
+ alter view cmd.view_test2 alter column id drop default;
+ NOTICE: snitch: BEFORE any ALTER VIEW cmd.view_test2
+ NOTICE: snitch: AFTER ALTER VIEW cmd.view_test2
+ NOTICE: snitch: AFTER any ALTER VIEW cmd.view_test2
+ cluster cmd.foo using foo_pkey;
+ NOTICE: snitch: BEFORE any CLUSTER cmd.foo
+ vacuum cmd.foo;
+ NOTICE: snitch: BEFORE any VACUUM cmd.foo
+ NOTICE: snitch: BEFORE VACUUM cmd.foo
+ vacuum;
+ NOTICE: snitch: BEFORE any VACUUM .
+ NOTICE: snitch: BEFORE VACUUM .
+ reindex table cmd.foo;
+ NOTICE: snitch: BEFORE any REINDEX cmd.foo
+ NOTICE: snitch: BEFORE REINDEX cmd.foo
+ NOTICE: snitch: AFTER any REINDEX cmd.foo
+ 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 cmd.idx_foo
+ NOTICE: snitch: AFTER any CREATE INDEX cmd.idx_foo
+ reindex index cmd.idx_foo;
+ NOTICE: snitch: BEFORE any REINDEX cmd.idx_foo
+ NOTICE: snitch: BEFORE REINDEX cmd.idx_foo
+ NOTICE: snitch: AFTER any REINDEX cmd.idx_foo
+ drop index cmd.idx_foo;
+ NOTICE: snitch: BEFORE any DROP INDEX cmd.idx_foo
+ NOTICE: snitch: AFTER any DROP INDEX cmd.idx_foo
+ create function fun(int) returns text language sql
+ as $$ select t from cmd.foo where id = $1; $$;
+ NOTICE: snitch: BEFORE any CREATE FUNCTION public.fun
+ NOTICE: snitch: AFTER CREATE FUNCTION public.fun
+ NOTICE: snitch: AFTER any CREATE FUNCTION public.fun
+ alter function fun(int) strict;
+ NOTICE: snitch: BEFORE any ALTER FUNCTION public.fun
+ NOTICE: snitch: BEFORE ALTER FUNCTION public.fun
+ NOTICE: snitch: AFTER ALTER FUNCTION public.fun
+ NOTICE: snitch: AFTER any ALTER FUNCTION public.fun
+ alter function fun(int) rename to notfun;
+ NOTICE: snitch: BEFORE any ALTER FUNCTION public.fun
+ NOTICE: snitch: BEFORE ALTER FUNCTION public.fun
+ NOTICE: snitch: AFTER ALTER FUNCTION public.notfun
+ NOTICE: snitch: AFTER any ALTER FUNCTION public.notfun
+ alter function notfun(int) set schema cmd;
+ NOTICE: snitch: BEFORE any ALTER FUNCTION public.notfun
+ NOTICE: snitch: BEFORE ALTER FUNCTION public.notfun
+ NOTICE: snitch: AFTER ALTER FUNCTION cmd.notfun
+ NOTICE: snitch: AFTER any ALTER FUNCTION cmd.notfun
+ alter function cmd.notfun(int) owner to regbob;
+ NOTICE: snitch: BEFORE any ALTER FUNCTION cmd.notfun
+ NOTICE: snitch: BEFORE ALTER FUNCTION cmd.notfun
+ NOTICE: snitch: AFTER ALTER FUNCTION cmd.notfun
+ NOTICE: snitch: AFTER any ALTER FUNCTION cmd.notfun
+ alter function cmd.notfun(int) cost 77;
+ NOTICE: snitch: BEFORE any ALTER FUNCTION cmd.notfun
+ NOTICE: snitch: BEFORE ALTER FUNCTION cmd.notfun
+ NOTICE: snitch: AFTER ALTER FUNCTION cmd.notfun
+ NOTICE: snitch: AFTER any ALTER FUNCTION cmd.notfun
+ drop function cmd.notfun(int);
+ NOTICE: snitch: BEFORE any DROP FUNCTION cmd.notfun
+ NOTICE: snitch: AFTER any DROP FUNCTION cmd.notfun
+ create function cmd.plus1(int) returns bigint language sql
+ as $$ select $1::bigint + 1; $$;
+ NOTICE: snitch: BEFORE any CREATE FUNCTION cmd.plus1
+ NOTICE: snitch: AFTER CREATE FUNCTION cmd.plus1
+ NOTICE: snitch: AFTER any CREATE FUNCTION cmd.plus1
+ create operator cmd.+!(procedure = cmd.plus1, leftarg = int);
+ NOTICE: snitch: BEFORE any CREATE OPERATOR cmd.+!
+ NOTICE: snitch: BEFORE CREATE OPERATOR cmd.+!
+ NOTICE: snitch: AFTER any CREATE OPERATOR cmd.+!
+ alter operator cmd.+!(int, NONE) set schema public;
+ NOTICE: snitch: BEFORE any ALTER OPERATOR cmd.+!
+ NOTICE: snitch: AFTER ALTER OPERATOR public.+!
+ NOTICE: snitch: AFTER any ALTER OPERATOR public.+!
+ drop operator public.+!(int, NONE);
+ NOTICE: snitch: BEFORE any DROP OPERATOR public.+!
+ NOTICE: snitch: AFTER any DROP OPERATOR public.+!
+ create aggregate cmd.avg (float8)
+ (
+ sfunc = float8_accum,
+ stype = float8[],
+ finalfunc = float8_avg,
+ initcond = '{0,0,0}'
+ );
+ NOTICE: snitch: BEFORE any CREATE AGGREGATE cmd.avg
+ NOTICE: snitch: BEFORE CREATE AGGREGATE cmd.avg
+ NOTICE: snitch: AFTER any CREATE AGGREGATE cmd.avg
+ alter aggregate cmd.avg(float8) set schema public;
+ NOTICE: snitch: BEFORE any ALTER AGGREGATE cmd.avg
+ NOTICE: snitch: AFTER any ALTER AGGREGATE public.avg
+ drop aggregate public.avg(float8);
+ NOTICE: snitch: BEFORE any DROP AGGREGATE public.avg
+ NOTICE: snitch: BEFORE DROP AGGREGATE public.avg
+ NOTICE: snitch: AFTER any DROP AGGREGATE public.avg
+ create collation cmd.french (LOCALE = 'fr_FR');
+ NOTICE: snitch: BEFORE any CREATE COLLATION cmd.french
+ NOTICE: snitch: AFTER CREATE COLLATION cmd.french
+ NOTICE: snitch: AFTER any CREATE COLLATION cmd.french
+ alter collation cmd.french rename to francais;
+ NOTICE: snitch: BEFORE any ALTER COLLATION cmd.french
+ NOTICE: snitch: BEFORE ALTER COLLATION cmd.french
+ NOTICE: snitch: AFTER any ALTER COLLATION cmd.francais
+ create type cmd.compfoo AS (f1 int, f2 text);
+ NOTICE: snitch: BEFORE any CREATE TYPE cmd.compfoo
+ NOTICE: snitch: BEFORE CREATE TYPE cmd.compfoo
+ NOTICE: snitch: AFTER any CREATE TYPE cmd.compfoo
+ alter type cmd.compfoo add attribute f3 text;
+ NOTICE: snitch: BEFORE any ALTER TYPE cmd.compfoo
+ NOTICE: snitch: BEFORE ALTER TYPE cmd.compfoo
+ NOTICE: snitch: AFTER any ALTER TYPE cmd.compfoo
+ create type cmd.type_test AS (a integer, b integer, c text);
+ NOTICE: snitch: BEFORE any CREATE TYPE cmd.type_test
+ NOTICE: snitch: BEFORE CREATE TYPE cmd.type_test
+ NOTICE: snitch: AFTER any CREATE TYPE cmd.type_test
+ alter type cmd.type_test owner to regbob;
+ NOTICE: snitch: BEFORE any ALTER TYPE cmd.type_test
+ NOTICE: snitch: BEFORE ALTER TYPE cmd.type_test
+ NOTICE: snitch: AFTER any ALTER TYPE cmd.type_test
+ alter type cmd.type_test rename to type_test2;
+ NOTICE: snitch: BEFORE any ALTER TYPE cmd.type_test
+ NOTICE: snitch: BEFORE ALTER TYPE cmd.type_test
+ NOTICE: snitch: AFTER any ALTER TYPE cmd.type_test2
+ alter type cmd.type_test2 set schema public;
+ NOTICE: snitch: BEFORE any ALTER TYPE cmd.type_test2
+ NOTICE: snitch: BEFORE ALTER TYPE cmd.type_test2
+ NOTICE: snitch: AFTER any ALTER TYPE public.type_test2
+ alter type public.type_test2 rename attribute a to z;
+ NOTICE: snitch: BEFORE any ALTER TYPE public.type_test2
+ NOTICE: snitch: BEFORE ALTER TYPE public.type_test2
+ NOTICE: snitch: AFTER any ALTER TYPE public.type_test2
+ alter type public.type_test2 add attribute alpha text;
+ NOTICE: snitch: BEFORE any ALTER TYPE public.type_test2
+ NOTICE: snitch: BEFORE ALTER TYPE public.type_test2
+ NOTICE: snitch: AFTER any ALTER TYPE public.type_test2
+ alter type public.type_test2 alter attribute alpha set data type char(90);
+ NOTICE: snitch: BEFORE any ALTER TYPE public.type_test2
+ NOTICE: snitch: BEFORE ALTER TYPE public.type_test2
+ NOTICE: snitch: AFTER any ALTER TYPE public.type_test2
+ alter type public.type_test2 drop attribute alpha;
+ NOTICE: snitch: BEFORE any ALTER TYPE public.type_test2
+ NOTICE: snitch: BEFORE ALTER TYPE public.type_test2
+ NOTICE: snitch: AFTER any ALTER TYPE public.type_test2
+ drop type cmd.compfoo;
+ NOTICE: snitch: BEFORE any DROP TYPE cmd.compfoo
+ NOTICE: snitch: AFTER any DROP TYPE cmd.compfoo
+ drop type public.type_test2;
+ NOTICE: snitch: BEFORE any DROP TYPE public.type_test2
+ NOTICE: snitch: AFTER any DROP TYPE public.type_test2
+ create type cmd.bug_status as enum ('new', 'open', 'closed');
+ NOTICE: snitch: BEFORE any CREATE TYPE cmd.bug_status
+ NOTICE: snitch: BEFORE CREATE TYPE cmd.bug_status
+ NOTICE: snitch: AFTER any CREATE TYPE cmd.bug_status
+ 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}$');
+ NOTICE: snitch: BEFORE any CREATE DOMAIN cmd.us_postal_code
+ NOTICE: snitch: AFTER CREATE DOMAIN cmd.us_postal_code
+ NOTICE: snitch: AFTER any CREATE DOMAIN cmd.us_postal_code
+ alter domain cmd.us_postal_code set not null;
+ NOTICE: snitch: BEFORE any ALTER DOMAIN cmd.us_postal_code
+ NOTICE: snitch: BEFORE ALTER DOMAIN cmd.us_postal_code
+ NOTICE: snitch: AFTER any ALTER DOMAIN cmd.us_postal_code
+ alter domain cmd.us_postal_code set default 90210;
+ NOTICE: snitch: BEFORE any ALTER DOMAIN cmd.us_postal_code
+ NOTICE: snitch: BEFORE ALTER DOMAIN cmd.us_postal_code
+ NOTICE: snitch: AFTER any ALTER DOMAIN cmd.us_postal_code
+ alter domain cmd.us_postal_code drop default;
+ NOTICE: snitch: BEFORE any ALTER DOMAIN cmd.us_postal_code
+ NOTICE: snitch: BEFORE ALTER DOMAIN cmd.us_postal_code
+ NOTICE: snitch: AFTER any ALTER DOMAIN cmd.us_postal_code
+ alter domain cmd.us_postal_code drop not null;
+ NOTICE: snitch: BEFORE any ALTER DOMAIN cmd.us_postal_code
+ NOTICE: snitch: BEFORE ALTER DOMAIN cmd.us_postal_code
+ NOTICE: snitch: AFTER any ALTER DOMAIN cmd.us_postal_code
+ alter domain cmd.us_postal_code add constraint dummy_constraint check (value ~ '^\d{8}$');
+ NOTICE: snitch: BEFORE any ALTER DOMAIN cmd.us_postal_code
+ NOTICE: snitch: BEFORE ALTER DOMAIN cmd.us_postal_code
+ NOTICE: snitch: AFTER any ALTER DOMAIN cmd.us_postal_code
+ alter domain cmd.us_postal_code drop constraint dummy_constraint;
+ NOTICE: snitch: BEFORE any ALTER DOMAIN cmd.us_postal_code
+ NOTICE: snitch: BEFORE ALTER DOMAIN cmd.us_postal_code
+ NOTICE: snitch: AFTER any ALTER DOMAIN cmd.us_postal_code
+ alter domain cmd.us_postal_code owner to regbob;
+ NOTICE: snitch: BEFORE any ALTER DOMAIN cmd.us_postal_code
+ NOTICE: snitch: BEFORE ALTER DOMAIN cmd.us_postal_code
+ NOTICE: snitch: AFTER any ALTER DOMAIN cmd.us_postal_code
+ alter domain cmd.us_postal_code set schema cmd2;
+ NOTICE: snitch: BEFORE any ALTER DOMAIN cmd.us_postal_code
+ NOTICE: snitch: BEFORE ALTER DOMAIN cmd.us_postal_code
+ NOTICE: snitch: AFTER any ALTER DOMAIN cmd2.us_postal_code
+ drop domain cmd2.us_postal_code;
+ NOTICE: snitch: BEFORE any DROP DOMAIN cmd2.us_postal_code
+ NOTICE: snitch: BEFORE DROP DOMAIN cmd2.us_postal_code
+ NOTICE: snitch: AFTER any DROP DOMAIN cmd2.us_postal_code
+ create function cmd.trigfunc() returns trigger language plpgsql as
+ $$ begin raise notice 'trigfunc'; end;$$;
+ NOTICE: snitch: BEFORE any CREATE FUNCTION cmd.trigfunc
+ NOTICE: snitch: AFTER CREATE FUNCTION cmd.trigfunc
+ NOTICE: snitch: AFTER any CREATE FUNCTION cmd.trigfunc
+ create trigger footg before update on cmd.foo for each row execute procedure cmd.trigfunc();
+ NOTICE: snitch: BEFORE any CREATE TRIGGER .footg
+ NOTICE: snitch: BEFORE CREATE TRIGGER .footg
+ NOTICE: snitch: AFTER any CREATE TRIGGER .footg
+ alter trigger footg on cmd.foo rename to foo_trigger;
+ NOTICE: snitch: BEFORE any ALTER TRIGGER cmd.footg
+ NOTICE: snitch: BEFORE ALTER TRIGGER cmd.footg
+ NOTICE: snitch: AFTER any ALTER TRIGGER cmd.foo_trigger
+ drop trigger foo_trigger on cmd.foo;
+ NOTICE: snitch: BEFORE any DROP TRIGGER .foo_trigger
+ NOTICE: snitch: BEFORE DROP TRIGGER .foo_trigger
+ NOTICE: snitch: AFTER any DROP TRIGGER .foo_trigger
+ create conversion test for 'utf8' to 'sjis' from utf8_to_sjis;
+ NOTICE: snitch: BEFORE any CREATE CONVERSION public.test
+ NOTICE: snitch: AFTER any CREATE CONVERSION public.test
+ create default conversion test2 for 'utf8' to 'sjis' from utf8_to_sjis;
+ NOTICE: snitch: BEFORE any CREATE CONVERSION public.test2
+ NOTICE: snitch: AFTER any CREATE CONVERSION public.test2
+ alter conversion test2 rename to test3;
+ NOTICE: snitch: BEFORE any ALTER CONVERSION public.test2
+ NOTICE: snitch: BEFORE ALTER CONVERSION public.test2
+ NOTICE: snitch: AFTER any ALTER CONVERSION public.test3
+ drop conversion test3;
+ NOTICE: snitch: BEFORE any DROP CONVERSION public.test3
+ NOTICE: snitch: AFTER any DROP CONVERSION public.test3
+ drop conversion test;
+ NOTICE: snitch: BEFORE any DROP CONVERSION public.test
+ NOTICE: snitch: AFTER any DROP CONVERSION public.test
+ create operator class test_op_class
+ for type anyenum using hash as
+ operator 1 =,
+ function 1 hashenum(anyenum);
+ NOTICE: snitch: BEFORE any CREATE OPERATOR CLASS public.test_op_class
+ NOTICE: snitch: AFTER CREATE OPERATOR CLASS public.test_op_class
+ NOTICE: snitch: AFTER any CREATE OPERATOR CLASS public.test_op_class
+ create text search configuration test (parser = "default");
+ NOTICE: snitch: BEFORE any CREATE TEXT SEARCH CONFIGURATION public.test
+ NOTICE: snitch: AFTER CREATE TEXT SEARCH CONFIGURATION public.test
+ NOTICE: snitch: AFTER any CREATE TEXT SEARCH CONFIGURATION public.test
+ create text search dictionary test_stem (
+ template = snowball,
+ language = 'english', stopwords = 'english'
+ );
+ NOTICE: snitch: BEFORE any CREATE TEXT SEARCH DICTIONARY public.test_stem
+ NOTICE: snitch: AFTER CREATE TEXT SEARCH DICTIONARY public.test_stem
+ NOTICE: snitch: AFTER any CREATE TEXT SEARCH DICTIONARY public.test_stem
+ alter text search dictionary test_stem (StopWords = dutch );
+ NOTICE: snitch: BEFORE any ALTER TEXT SEARCH DICTIONARY public.test_stem
+ NOTICE: snitch: AFTER any ALTER TEXT SEARCH DICTIONARY public.test_stem
+ create text search parser test_parser (
+ start = prsd_start,
+ gettoken = prsd_nexttoken,
+ end = prsd_end,
+ lextypes = prsd_lextype,
+ headline = prsd_headline
+ );
+ NOTICE: snitch: BEFORE any CREATE TEXT SEARCH PARSER public.test_parser
+ NOTICE: snitch: AFTER CREATE TEXT SEARCH PARSER public.test_parser
+ NOTICE: snitch: AFTER any CREATE TEXT SEARCH PARSER public.test_parser
+ create text search template test_template (
+ init = dsimple_init,
+ lexize = dsimple_lexize
+ );
+ NOTICE: snitch: BEFORE any CREATE TEXT SEARCH TEMPLATE public.test_template
+ NOTICE: snitch: AFTER CREATE TEXT SEARCH TEMPLATE public.test_template
+ NOTICE: snitch: AFTER any CREATE TEXT SEARCH TEMPLATE public.test_template
+ drop text search configuration test;
+ NOTICE: snitch: BEFORE any DROP TEXT SEARCH CONFIGURATION public.test
+ NOTICE: snitch: BEFORE DROP TEXT SEARCH CONFIGURATION public.test
+ NOTICE: snitch: AFTER any DROP TEXT SEARCH CONFIGURATION public.test
+ drop text search dictionary test_stem;
+ NOTICE: snitch: BEFORE any DROP TEXT SEARCH DICTIONARY public.test_stem
+ NOTICE: snitch: BEFORE DROP TEXT SEARCH DICTIONARY public.test_stem
+ NOTICE: snitch: AFTER any DROP TEXT SEARCH DICTIONARY public.test_stem
+ drop text search parser test_parser;
+ NOTICE: snitch: BEFORE any DROP TEXT SEARCH PARSER public.test_parser
+ NOTICE: snitch: BEFORE DROP TEXT SEARCH PARSER public.test_parser
+ NOTICE: snitch: AFTER any DROP TEXT SEARCH PARSER public.test_parser
+ drop text search template test_template;
+ NOTICE: snitch: BEFORE any DROP TEXT SEARCH TEMPLATE public.test_template
+ NOTICE: snitch: BEFORE DROP TEXT SEARCH TEMPLATE public.test_template
+ NOTICE: snitch: AFTER any DROP TEXT SEARCH TEMPLATE public.test_template
+ create function cmd.testcast(text) returns int4 language plpgsql as $$begin return 4::int4;end;$$;
+ NOTICE: snitch: BEFORE any CREATE FUNCTION cmd.testcast
+ NOTICE: snitch: AFTER CREATE FUNCTION cmd.testcast
+ NOTICE: snitch: AFTER any CREATE FUNCTION cmd.testcast
+ create cast (text as int4) with function cmd.testcast(text) as assignment;
+ NOTICE: snitch: BEFORE any CREATE CAST .
+ NOTICE: snitch: AFTER CREATE CAST .
+ NOTICE: snitch: AFTER any CREATE CAST .
+ alter schema cmd rename to cmd1;
+ NOTICE: snitch: BEFORE any ALTER SCHEMA .cmd
+ NOTICE: snitch: AFTER ALTER SCHEMA .cmd1
+ NOTICE: snitch: AFTER any ALTER SCHEMA .cmd1
+ drop schema cmd1 cascade;
+ NOTICE: snitch: BEFORE any DROP SCHEMA .cmd1
+ NOTICE: snitch: BEFORE DROP SCHEMA .cmd1
+ NOTICE: drop cascades to 12 other objects
+ DETAIL: drop cascades to table cmd1.foo
+ drop cascades to view cmd1.v
+ drop cascades to table cmd1.test
+ drop cascades to sequence cmd1.test_seq
+ drop cascades to view cmd1.view_test2
+ drop cascades to table cmd1.bar
+ drop cascades to function cmd1.plus1(integer)
+ drop cascades to collation francais
+ drop cascades to type cmd1.bug_status
+ drop cascades to function cmd1.trigfunc()
+ drop cascades to function cmd1.testcast(text)
+ drop cascades to cast from text to integer
+ NOTICE: snitch: AFTER any DROP SCHEMA .cmd1
+ drop schema cmd2 cascade;
+ NOTICE: snitch: BEFORE any DROP SCHEMA .cmd2
+ NOTICE: snitch: BEFORE DROP SCHEMA .cmd2
+ NOTICE: snitch: AFTER any DROP SCHEMA .cmd2
+ drop role regbob;
+ drop command trigger snitch_before;
+ drop command trigger snitch_after;
+ drop command trigger snitch_create_table;
+ drop command trigger snitch_create_seq;
+ drop command trigger snitch_create_view;
+ drop command trigger snitch_alter_table;
+ drop command trigger snitch_alter_seq;
+ drop command trigger snitch_alter_view;
+ drop command trigger snitch_drop_table;
+ drop command trigger snitch_create_function;
+ drop command trigger snitch_create_collation;
+ drop command trigger snitch_alter_operator;
+ drop command trigger snitch_create_domain;
+ drop command trigger snitch_alter_schema;
+ drop command trigger snitch_create_tsconfig;
+ drop command trigger snitch_create_tsdict;
+ drop command trigger snitch_create_tsparser;
+ drop command trigger snitch_create_tstmpl;
+ drop command trigger snitch_after_alter_function;
+ drop command trigger snitch_create_cast;
+ drop command trigger snitch_create_opclass;
+ drop command trigger snitch_create_trigger;
+ drop command trigger snitch_alter_trigger;
+ drop command trigger snitch_drop_trigger;
+ drop command trigger snitch_create_schema;
+ drop command trigger snitch_drop_schema;
+ drop command trigger snitch_create_aggregate;
+ drop command trigger snitch_alter_collation;
+ drop command trigger snitch_create_operator;
+ drop command trigger snitch_alter_domain;
+ drop command trigger snitch_create_type;
+ drop command trigger snitch_alter_type;
+ drop command trigger snitch_before_alter_function;
+ drop command trigger snitch_alter_conversion;
+ drop command trigger snitch_drop_agg;
+ drop command trigger snitch_drop_domain;
+ drop command trigger snitch_before_drop_tsconf;
+ drop command trigger snitch_before_drop_tsdict;
+ drop command trigger snitch_before_drop_tsparser;
+ drop command trigger snitch_before_drop_tstmpl;
+ drop command trigger snitch_vacuum;
+ drop command trigger snitch_reindex;
*** a/src/test/regress/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/expected/type_sanity.out
--- b/src/test/regress/expected/type_sanity.out
***************
*** 128,143 **** WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
-- As of 8.0, this check finds refcursor, which is borrowing
-- other types' I/O routines
SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
(p1.typelem != 0 AND p1.typlen < 0) AND NOT
(p2.prorettype = p1.oid AND NOT p2.proretset)
ORDER BY 1;
! oid | typname | oid | proname
! ------+-----------+-----+---------
! 1790 | refcursor | 46 | textin
! (1 row)
-- Varlena array types will point to array_in
-- Exception as of 8.1: int2vector and oidvector have their own I/O routines
--- 128,145 ----
-- As of 8.0, this check finds refcursor, which is borrowing
-- other types' I/O routines
+ -- As of 9.2, this also finds cmdtrigger.
SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
(p1.typelem != 0 AND p1.typlen < 0) AND NOT
(p2.prorettype = p1.oid AND NOT p2.proretset)
ORDER BY 1;
! oid | typname | oid | proname
! ------+-----------------+------+------------
! 1790 | refcursor | 46 | textin
! 3838 | command_trigger | 2300 | trigger_in
! (2 rows)
-- Varlena array types will point to array_in
-- Exception as of 8.1: int2vector and oidvector have their own I/O routines
***************
*** 169,174 **** ORDER BY 1;
--- 171,177 ----
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
-- other types' I/O routines
+ -- In 9.2 cmdtrigger is borrowing trigger routines
SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
***************
*** 177,186 **** WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
(p2.oid = 'array_out'::regproc AND
p1.typelem != 0 AND p1.typlen = -1)))
ORDER BY 1;
! oid | typname | oid | proname
! ------+-----------+-----+---------
! 1790 | refcursor | 47 | textout
! (1 row)
SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
--- 180,190 ----
(p2.oid = 'array_out'::regproc AND
p1.typelem != 0 AND p1.typlen = -1)))
ORDER BY 1;
! oid | typname | oid | proname
! ------+-----------------+------+-------------
! 1790 | refcursor | 47 | textout
! 3838 | command_trigger | 2301 | trigger_out
! (2 rows)
SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
*** 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,295 ----
+ --
+ -- COMMAND TRIGGERS
+ --
+ create or replace function any_snitch()
+ returns command_trigger
+ language plpgsql
+ as $$
+ begin
+ -- can't output tg_objectid here that would break pg_regress
+ raise notice 'snitch: % any % %.%', tg_when, tg_tag, tg_schemaname, tg_objectname;
+ end;
+ $$;
+
+ create or replace function snitch()
+ returns command_trigger
+ language plpgsql
+ as $$
+ begin
+ -- can't output tg_objectid here that would break pg_regress
+ raise notice 'snitch: % % %.%', tg_when, tg_tag, tg_schemaname, tg_objectname;
+ end;
+ $$;
+
+ create command trigger snitch_before before any command execute procedure any_snitch();
+ create command trigger snitch_after_ after any command execute procedure any_snitch();
+
+ alter command trigger snitch_before set disable;
+ alter command trigger snitch_before set enable;
+ alter command trigger snitch_after_ rename to snitch_after;
+
+ create command trigger snitch_create_table after create table execute procedure snitch();
+ create command trigger snitch_create_seq after create sequence execute procedure snitch();
+ create command trigger snitch_create_view after create view execute procedure snitch();
+ create command trigger snitch_alter_table after alter table execute procedure snitch();
+ create command trigger snitch_alter_seq after alter sequence execute procedure snitch();
+ create command trigger snitch_alter_view after alter view execute procedure snitch();
+ create command trigger snitch_drop_table after drop table execute procedure snitch();
+ create command trigger snitch_create_function after create function execute procedure snitch();
+ create command trigger snitch_create_collation after create collation execute procedure snitch();
+ create command trigger snitch_alter_operator after alter operator execute procedure snitch();
+ create command trigger snitch_create_domain after create domain execute procedure snitch();
+ create command trigger snitch_alter_schema after alter schema execute procedure snitch();
+ create command trigger snitch_create_tsconfig after create text search configuration execute procedure snitch();
+ create command trigger snitch_create_tsdict after create text search dictionary execute procedure snitch();
+ create command trigger snitch_create_tsparser after create text search parser execute procedure snitch();
+ create command trigger snitch_create_tstmpl after create text search template execute procedure snitch();
+ create command trigger snitch_after_alter_function after alter function execute procedure snitch();
+ create command trigger snitch_create_cast after create cast execute procedure snitch();
+ create command trigger snitch_create_opclass after create operator class execute procedure snitch();
+
+ create command trigger snitch_create_trigger before create trigger execute procedure snitch();
+ create command trigger snitch_alter_trigger before alter trigger execute procedure snitch();
+ create command trigger snitch_drop_trigger before drop trigger execute procedure snitch();
+ create command trigger snitch_create_schema before create schema execute procedure snitch();
+ create command trigger snitch_drop_schema before drop schema execute procedure snitch();
+ create command trigger snitch_create_aggregate before create aggregate execute procedure snitch();
+ create command trigger snitch_alter_collation before alter collation execute procedure snitch();
+ create command trigger snitch_create_operator before create operator execute procedure snitch();
+ create command trigger snitch_alter_domain before alter domain execute procedure snitch();
+ create command trigger snitch_create_type before create type execute procedure snitch();
+ create command trigger snitch_alter_type before alter type execute procedure snitch();
+ create command trigger snitch_before_alter_function before alter function execute procedure snitch();
+ create command trigger snitch_alter_conversion before alter conversion execute procedure snitch();
+ create command trigger snitch_drop_agg before drop aggregate execute procedure snitch();
+ create command trigger snitch_drop_domain before drop domain execute procedure snitch();
+
+ create command trigger snitch_before_drop_tsconf before drop text search configuration execute procedure snitch();
+ create command trigger snitch_before_drop_tsdict before drop text search dictionary execute procedure snitch();
+ create command trigger snitch_before_drop_tsparser before drop text search parser execute procedure snitch();
+ create command trigger snitch_before_drop_tstmpl before drop text search template execute procedure snitch();
+
+ create command trigger snitch_vacuum before vacuum execute procedure snitch();
+ create command trigger snitch_reindex before reindex execute procedure snitch();
+
+ create schema cmd;
+ create schema cmd2;
+ create role regbob;
+
+ create table cmd.foo(id bigserial primary key);
+ create view cmd.v as select * from cmd.foo;
+ alter table cmd.foo add column t text;
+
+ create table test9 (id int, stuff text);
+ alter table test9 rename to test;
+ alter table test set schema cmd;
+ alter table cmd.test rename column stuff to things;
+ alter table cmd.test add column alpha text;
+ alter table cmd.test alter column alpha set data type varchar(300);
+ alter table cmd.test alter column alpha set default 'test';
+ alter table cmd.test alter column alpha drop default;
+ alter table cmd.test alter column alpha set statistics 78;
+ alter table cmd.test alter column alpha set storage plain;
+ alter table cmd.test alter column alpha set not null;
+ alter table cmd.test alter column alpha drop not null;
+ alter table cmd.test alter column alpha set (n_distinct = -0.78);
+ alter table cmd.test alter column alpha reset (n_distinct);
+ alter table cmd.test drop column alpha;
+ alter table cmd.test add check (id > 2) not valid;
+ alter table cmd.test add check (id < 800000);
+ alter table cmd.test set without cluster;
+ alter table cmd.test set with oids;
+ alter table cmd.test set without oids;
+
+ create sequence test_seq_;
+ alter sequence test_seq_ owner to regbob;
+ alter sequence test_seq_ rename to test_seq;
+ alter sequence test_seq set schema cmd;
+ alter sequence cmd.test_seq start with 3;
+ alter sequence cmd.test_seq restart with 4;
+ alter sequence cmd.test_seq minvalue 3;
+ alter sequence cmd.test_seq no minvalue;
+ alter sequence cmd.test_seq maxvalue 900000;
+ alter sequence cmd.test_seq no maxvalue;
+ alter sequence cmd.test_seq cache 876;
+ alter sequence cmd.test_seq cycle;
+ alter sequence cmd.test_seq no cycle;
+
+ create view view_test as select id, things from cmd.test;
+ alter view view_test owner to regbob;
+ alter view view_test rename to view_test2;
+ alter view view_test2 set schema cmd;
+ alter view cmd.view_test2 alter column id set default 9;
+ alter view cmd.view_test2 alter column id drop default;
+
+ cluster cmd.foo using foo_pkey;
+ vacuum cmd.foo;
+ vacuum;
+ reindex table cmd.foo;
+
+ set session_replication_role to replica;
+ create table cmd.bar();
+ reset session_replication_role;
+
+ create index idx_foo on cmd.foo(t);
+ reindex index cmd.idx_foo;
+ drop index cmd.idx_foo;
+
+ create function fun(int) returns text language sql
+ as $$ select t from cmd.foo where id = $1; $$;
+
+ alter function fun(int) strict;
+ alter function fun(int) rename to notfun;
+ alter function notfun(int) set schema cmd;
+ alter function cmd.notfun(int) owner to regbob;
+ alter function cmd.notfun(int) cost 77;
+ drop function cmd.notfun(int);
+
+ create function cmd.plus1(int) returns bigint language sql
+ as $$ select $1::bigint + 1; $$;
+
+ create operator cmd.+!(procedure = cmd.plus1, leftarg = int);
+ alter operator cmd.+!(int, NONE) set schema public;
+ drop operator public.+!(int, NONE);
+
+ create aggregate cmd.avg (float8)
+ (
+ sfunc = float8_accum,
+ stype = float8[],
+ finalfunc = float8_avg,
+ initcond = '{0,0,0}'
+ );
+ alter aggregate cmd.avg(float8) set schema public;
+ drop aggregate public.avg(float8);
+
+ create collation cmd.french (LOCALE = 'fr_FR');
+ alter collation cmd.french rename to francais;
+
+ create type cmd.compfoo AS (f1 int, f2 text);
+ alter type cmd.compfoo add attribute f3 text;
+
+ create type cmd.type_test AS (a integer, b integer, c text);
+ alter type cmd.type_test owner to regbob;
+ alter type cmd.type_test rename to type_test2;
+ alter type cmd.type_test2 set schema public;
+ alter type public.type_test2 rename attribute a to z;
+ alter type public.type_test2 add attribute alpha text;
+ alter type public.type_test2 alter attribute alpha set data type char(90);
+ alter type public.type_test2 drop attribute alpha;
+
+ drop type cmd.compfoo;
+ drop type public.type_test2;
+
+ create type cmd.bug_status as enum ('new', 'open', 'closed');
+ alter type cmd.bug_status add value 'wontfix';
+
+ create domain cmd.us_postal_code as text check(value ~ '^\d{5}$' or value ~ '^\d{5}-\d{4}$');
+ alter domain cmd.us_postal_code set not null;
+ alter domain cmd.us_postal_code set default 90210;
+ alter domain cmd.us_postal_code drop default;
+ alter domain cmd.us_postal_code drop not null;
+ alter domain cmd.us_postal_code add constraint dummy_constraint check (value ~ '^\d{8}$');
+ alter domain cmd.us_postal_code drop constraint dummy_constraint;
+ alter domain cmd.us_postal_code owner to regbob;
+ alter domain cmd.us_postal_code set schema cmd2;
+ drop domain cmd2.us_postal_code;
+
+ create function cmd.trigfunc() returns trigger language plpgsql as
+ $$ begin raise notice 'trigfunc'; end;$$;
+
+ create trigger footg before update on cmd.foo for each row execute procedure cmd.trigfunc();
+ alter trigger footg on cmd.foo rename to foo_trigger;
+ drop trigger foo_trigger on cmd.foo;
+
+ create conversion test for 'utf8' to 'sjis' from utf8_to_sjis;
+ create default conversion test2 for 'utf8' to 'sjis' from utf8_to_sjis;
+ alter conversion test2 rename to test3;
+ drop conversion test3;
+ drop conversion test;
+
+ create operator class test_op_class
+ for type anyenum using hash as
+ operator 1 =,
+ function 1 hashenum(anyenum);
+
+ create text search configuration test (parser = "default");
+
+ create text search dictionary test_stem (
+ template = snowball,
+ language = 'english', stopwords = 'english'
+ );
+ alter text search dictionary test_stem (StopWords = dutch );
+
+ create text search parser test_parser (
+ start = prsd_start,
+ gettoken = prsd_nexttoken,
+ end = prsd_end,
+ lextypes = prsd_lextype,
+ headline = prsd_headline
+ );
+
+ create text search template test_template (
+ init = dsimple_init,
+ lexize = dsimple_lexize
+ );
+
+ drop text search configuration test;
+ drop text search dictionary test_stem;
+ drop text search parser test_parser;
+ drop text search template test_template;
+
+ create function cmd.testcast(text) returns int4 language plpgsql as $$begin return 4::int4;end;$$;
+ create cast (text as int4) with function cmd.testcast(text) as assignment;
+
+ alter schema cmd rename to cmd1;
+
+ drop schema cmd1 cascade;
+ drop schema cmd2 cascade;
+ drop role regbob;
+
+ drop command trigger snitch_before;
+ drop command trigger snitch_after;
+
+ drop command trigger snitch_create_table;
+ drop command trigger snitch_create_seq;
+ drop command trigger snitch_create_view;
+ drop command trigger snitch_alter_table;
+ drop command trigger snitch_alter_seq;
+ drop command trigger snitch_alter_view;
+ drop command trigger snitch_drop_table;
+ drop command trigger snitch_create_function;
+ drop command trigger snitch_create_collation;
+ drop command trigger snitch_alter_operator;
+ drop command trigger snitch_create_domain;
+ drop command trigger snitch_alter_schema;
+ drop command trigger snitch_create_tsconfig;
+ drop command trigger snitch_create_tsdict;
+ drop command trigger snitch_create_tsparser;
+ drop command trigger snitch_create_tstmpl;
+ drop command trigger snitch_after_alter_function;
+ drop command trigger snitch_create_cast;
+ drop command trigger snitch_create_opclass;
+
+ drop command trigger snitch_create_trigger;
+ drop command trigger snitch_alter_trigger;
+ drop command trigger snitch_drop_trigger;
+ drop command trigger snitch_create_schema;
+ drop command trigger snitch_drop_schema;
+ drop command trigger snitch_create_aggregate;
+ drop command trigger snitch_alter_collation;
+ drop command trigger snitch_create_operator;
+ drop command trigger snitch_alter_domain;
+ drop command trigger snitch_create_type;
+ drop command trigger snitch_alter_type;
+ drop command trigger snitch_before_alter_function;
+ drop command trigger snitch_alter_conversion;
+ drop command trigger snitch_drop_agg;
+ drop command trigger snitch_drop_domain;
+
+ drop command trigger snitch_before_drop_tsconf;
+ drop command trigger snitch_before_drop_tsdict;
+ drop command trigger snitch_before_drop_tsparser;
+ drop command trigger snitch_before_drop_tstmpl;
+
+ drop command trigger snitch_vacuum;
+ drop command trigger snitch_reindex;
*** a/src/test/regress/sql/triggers.sql
--- b/src/test/regress/sql/triggers.sql
***************
*** 962,968 **** SELECT * FROM city_view;
DROP TABLE city_table CASCADE;
DROP TABLE country_table;
-
-- Test pg_trigger_depth()
create table depth_a (id int not null primary key);
--- 962,967 ----
*** a/src/test/regress/sql/type_sanity.sql
--- b/src/test/regress/sql/type_sanity.sql
***************
*** 104,109 **** WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
--- 104,110 ----
-- As of 8.0, this check finds refcursor, which is borrowing
-- other types' I/O routines
+ -- As of 9.2, this also finds cmdtrigger.
SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
***************
*** 130,135 **** ORDER BY 1;
--- 131,137 ----
-- As of 8.0, this check finds refcursor, which is borrowing
-- other types' I/O routines
+ -- In 9.2 cmdtrigger is borrowing trigger routines
SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT