*** a/doc/src/sgml/ref/create_trigger.sgml
--- b/doc/src/sgml/ref/create_trigger.sgml
***************
*** 29,40 **** CREATE [ CONSTRAINT ] TRIGGER name
--- 29,109 ----
[ WHEN ( condition ) ]
EXECUTE PROCEDURE function_name ( arguments )
+ CREATE TRIGGER name { BEFORE | AFTER } ANY COMMAND
+ EXECUTE PROCEDURE function_name ()
+
+ CREATE TRIGGER name { BEFORE | AFTER } COMMAND command [, ... ]
+ EXECUTE PROCEDURE function_name ()
+
where event can be one of:
INSERT
UPDATE [ OF column_name [, ... ] ]
DELETE
TRUNCATE
+
+ and where command can be one of:
+
+ CREATE SCHEMA
+ CREATE EXTENSION
+ CREATE FUNCTION
+ CREATE TABLE
+ CREATE FOREIGN TABLE
+ CREATE INDEX
+ CREATE SEQUENCE
+ CREATE VIEW
+ CREATE AGGREGATE
+ CREATE OPERATOR
+ CREATE COLLATION
+ CREATE TEXT SEARCH PARSER
+ CREATE TEXT SEARCH DICTIONARY
+ CREATE TEXT SEARCH TEMPLATE
+ CREATE TEXT SEARCH CONFIGURATION
+ CREATE TYPE
+ CREATE DOMAIN
+ ALTER TABLE
+ ALTER SCHEMA
+ ALTER EXTENSION
+ ALTER FUNCTION
+ ALTER TABLE
+ ALTER AGGREGATE
+ ALTER OPERATOR
+ ALTER COLLATION
+ ALTER TEXT_P SEARCH PARSER
+ ALTER TEXT_P SEARCH DICTIONARY
+ ALTER TEXT_P SEARCH TEMPLATE
+ ALTER TEXT_P SEARCH CONFIGURATION
+ ALTER TYPE_P
+ ALTER DOMAIN_P
+ ALTER TRIGGER
+ DROP TABLE
+ DROP SEQUENCE
+ DROP VIEW
+ DROP INDEX
+ DROP FOREIGN TABLE
+ DROP TYPE
+ DROP DOMAIN
+ DROP COLLATION
+ DROP CONVERSION
+ DROP SCHEMA
+ DROP EXTENSION
+ DROP TEXT SEARCH PARSER
+ DROP TEXT SEARCH DICTIONARY
+ DROP TEXT SEARCH TEMPLATE
+ DROP TEXT SEARCH CONFIGURATION
+ DROP LANGUAGE
+ DROP FOREIGN DATA WRAPPER
+ DROP SERVER
+ DROP TRIGGER
+ DROP ASSERTION
+ DROP OPERATOR CLASS
+ DROP OPERATOR FAMILY
+ DROP FUNCTION
+ DROP AGGREGATE
+ DROP OPERATOR
+ DROP CAST
+ DROP RULE
+
***************
*** 42,51 **** CREATE [ CONSTRAINT ] TRIGGER name
Description
! CREATE TRIGGER creates a new trigger. The
! trigger will be associated with the specified table or view and will
! execute the specified function function_name when certain events occur.
--- 111,127 ----
Description
! CREATE TRIGGER creates a new trigger. The trigger will
! be associated with the specified table, view or command and will execute
! the specified
! function function_name when
! certain events occur.
!
!
!
! The command trigger can be specified to fire before or after the command
! is executed, or instead of executing the command. A command trigger's
! function must return void.
***************
*** 251,256 **** UPDATE OF column_name1 [, column_name2
--- 327,341 ----
+ command
+
+
+ The tag of the command the trigger is for.
+
+
+
+
+
referenced_table_name
***************
*** 334,340 **** UPDATE OF column_name1 [, column_name2
A user-supplied function that is declared as taking no arguments
and returning type trigger>, which is executed when
! the trigger fires.
--- 419,447 ----
A user-supplied function that is declared as taking no arguments
and returning type trigger>, which is executed when
! the trigger fires, for table and view triggers.
!
!
! In the case of a COMMAND trigger, the user-supplied
! function must be declared as taking 5 arguments of type text, text,
! oid, text, text and returning void.
!
!
! If your command trigger is implemented in C then it
! will be called with yet another argument, of
! type internal, which is a pointer to
! the Node * parse tree.
!
!
! The command trigger function is called with the
! parameters tg_when (which is set to either 'BEFORE'
! or 'AFTER'), command
! tag, objectid (can be null in case of a
! BEFORE CREATE or an AFTER DROP command trigger
! timing), schemaname (can be null for objects not
! living in a schema, and for sequences due to an implementation limit)
! and object name (can be null for any command
! triggers).
***************
*** 469,474 **** CREATE TRIGGER view_insert
--- 576,617 ----
FOR EACH ROW
EXECUTE PROCEDURE view_insert_row();
+
+ Forbids the execution of any DDL command:
+
+
+ CREATE OR REPLACE FUNCTION abort_any_command
+ (tg_when text, cmd_tag text, objectid oid, schemaname text, objectname text)
+ RETURNS void LANGUAGE plpgsql AS $$
+ BEGIN
+ RAISE EXCEPTION 'command % is disabled' % cmd_tag;
+ END;
+ $$;
+
+ CREATE TRIGGER abort_ddl
+ BEFORE COMMAND CREATE TABLE
+ EXECUTE PROCEDURE abort_any_command();
+
+
+ Execute the function enforce_local_style> each time
+ a CREATE TABLE command is run:
+
+
+ CREATE OR REPLACE FUNCTION enforce_local_style
+ (tg_when text, cmd_tag text, objectid oid, schemaname text, objectname text)
+ RETURNS bool LANGUAGE plpgsql AS $$
+ BEGIN
+ IF substring(objectname, 0, 4) NOT IN ('ab_', 'cz_', 'fr_')
+ THEN
+ RAISE EXCEPTION 'invalid relation name: %', objectname;
+ END IF;
+ END;
+ $$;
+
+ CREATE TRIGGER check_style
+ BEFORE COMMAND CREATE TABLE
+ EXECUTE PROCEDURE enforce_local_style();
+
***************
*** 531,536 **** CREATE TRIGGER view_insert
--- 674,684 ----
+ The ability to run triggers on commands is PostgreSQL>
+ extension of the SQL standard.
+
+
+
The ability to specify multiple actions for a single trigger using
OR is a PostgreSQL> extension of
the SQL standard.
***************
*** 548,553 **** CREATE TRIGGER view_insert
--- 696,707 ----
standard.
+
+ CREATE TRIGGER ON COMMAND is a
+ PostgreSQL extension of the SQL>
+ standard.
+
+
*** a/doc/src/sgml/ref/drop_trigger.sgml
--- b/doc/src/sgml/ref/drop_trigger.sgml
***************
*** 22,27 **** PostgreSQL documentation
--- 22,82 ----
DROP TRIGGER [ IF EXISTS ] name ON table [ CASCADE | RESTRICT ]
+ DROP TRIGGER [ IF EXISTS ] name ON COMMAND command [, ... ] [ CASCADE | RESTRICT ]
+ DROP TRIGGER [ IF EXISTS ] name ON ANY COMMAND [ CASCADE | RESTRICT ]
+
+ where command can be one of:
+
+ CREATE DATABASE
+ DROP DATABASE
+ CREATE SCHEMA
+ CREATE EXTENSION
+ CREATE TABLESPACE
+ CREATE FUNCTION
+ CREATE TABLE
+ CREATE FOREIGN TABLE
+ CREATE INDEX
+ CREATE SEQUENCE
+ CREATE VIEW
+ CREATE AGGREGATE
+ CREATE OPERATOR
+ CREATE COLLATION
+ CREATE TEXT SEARCH PARSER
+ CREATE TEXT SEARCH DICTIONARY
+ CREATE TEXT SEARCH TEMPLATE
+ CREATE TEXT SEARCH CONFIGURATION
+ CREATE TYPE
+ CREATE DOMAIN
+ ALTER TABLE
+ DROP TABLESPACE
+ DROP TABLE
+ DROP SEQUENCE
+ DROP VIEW
+ DROP INDEX
+ DROP FOREIGN TABLE
+ DROP TYPE
+ DROP DOMAIN
+ DROP COLLATION
+ DROP CONVERSION
+ DROP SCHEMA
+ DROP EXTENSION
+ DROP TEXT SEARCH PARSER
+ DROP TEXT SEARCH DICTIONARY
+ DROP TEXT SEARCH TEMPLATE
+ DROP TEXT SEARCH CONFIGURATION
+ DROP LANGUAGE
+ DROP FOREIGN DATA WRAPPER
+ DROP SERVER
+ DROP TRIGGER
+ DROP ASSERTION
+ DROP OPERATOR CLASS
+ DROP OPERATOR FAMILY
+ DROP FUNCTION
+ DROP AGGREGATE
+ DROP OPERATOR
+ DROP CAST
+ DROP RULE
+
***************
*** 29,37 **** DROP TRIGGER [ IF EXISTS ] name ON
Description
! DROP TRIGGER removes an existing
! trigger definition. To execute this command, the current
! user must be the owner of the table for which the trigger is defined.
--- 84,93 ----
Description
! DROP TRIGGER removes an existing trigger definition.
! To execute this command, the current user must be the owner of the table
! for which the trigger is defined, or a database owner in case of a
! command trigger.
*** a/src/backend/bootstrap/bootparse.y
--- b/src/backend/bootstrap/bootparse.y
***************
*** 291,297 **** Boot_DeclareIndexStmt:
$10,
NULL, NIL, NIL,
false, false, false, false, false,
! false, false, true, false, false);
do_end();
}
;
--- 291,297 ----
$10,
NULL, NIL, NIL,
false, false, false, false, false,
! false, false, true, false, false, NULL);
do_end();
}
;
***************
*** 310,316 **** Boot_DeclareUniqueIndexStmt:
$11,
NULL, NIL, NIL,
true, false, false, false, false,
! false, false, true, false, false);
do_end();
}
;
--- 310,316 ----
$11,
NULL, NIL, NIL,
true, false, false, false, false,
! false, false, true, false, false, NULL);
do_end();
}
;
*** a/src/backend/catalog/Makefile
--- b/src/backend/catalog/Makefile
***************
*** 31,37 **** POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \
pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \
! pg_statistic.h pg_rewrite.h pg_trigger.h pg_description.h \
pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \
pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \
pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
--- 31,37 ----
pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \
pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \
! pg_statistic.h pg_rewrite.h pg_trigger.h pg_cmdtrigger.h pg_description.h \
pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \
pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \
pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 25,30 ****
--- 25,31 ----
#include "catalog/pg_attrdef.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
+ #include "catalog/pg_cmdtrigger.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_collation_fn.h"
#include "catalog/pg_constraint.h"
***************
*** 52,57 ****
--- 53,59 ----
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+ #include "commands/cmdtrigger.h"
#include "commands/comment.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
***************
*** 157,163 **** static const Oid object_classes[MAX_OCLASS] = {
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
UserMappingRelationId, /* OCLASS_USER_MAPPING */
DefaultAclRelationId, /* OCLASS_DEFACL */
! ExtensionRelationId /* OCLASS_EXTENSION */
};
--- 159,166 ----
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
UserMappingRelationId, /* OCLASS_USER_MAPPING */
DefaultAclRelationId, /* OCLASS_DEFACL */
! ExtensionRelationId, /* OCLASS_EXTENSION */
! CmdTriggerRelationId /* OCLASS_CMDTRIGGER */
};
***************
*** 1067,1072 **** doDeletion(const ObjectAddress *object)
--- 1070,1079 ----
break;
}
+ case OCLASS_CMDTRIGGER:
+ RemoveCmdTriggerById(object->objectId);
+ break;
+
case OCLASS_PROC:
RemoveFunctionById(object->objectId);
break;
***************
*** 2188,2193 **** getObjectClass(const ObjectAddress *object)
--- 2195,2203 ----
case ExtensionRelationId:
return OCLASS_EXTENSION;
+
+ case CmdTriggerRelationId:
+ return OCLASS_CMDTRIGGER;
}
/* shouldn't get here */
***************
*** 2822,2827 **** getObjectDescription(const ObjectAddress *object)
--- 2832,2876 ----
break;
}
+ case OCLASS_CMDTRIGGER:
+ {
+ Relation trigDesc;
+ ScanKeyData skey[1];
+ SysScanDesc tgscan;
+ HeapTuple tup;
+ Form_pg_cmdtrigger trig;
+
+ trigDesc = heap_open(CmdTriggerRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ tgscan = systable_beginscan(trigDesc, CmdTriggerOidIndexId, true,
+ SnapshotNow, 1, skey);
+
+ tup = systable_getnext(tgscan);
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "could not find tuple for command trigger %u",
+ object->objectId);
+
+ trig = (Form_pg_cmdtrigger) GETSTRUCT(tup);
+
+ if (strcmp(NameStr(trig->ctgcommand), "ANY") == 0)
+ appendStringInfo(&buffer, _("trigger %s on any command"),
+ NameStr(trig->ctgname));
+ else
+ appendStringInfo(&buffer, _("trigger %s on command %s"),
+ NameStr(trig->ctgname),
+ NameStr(trig->ctgcommand));
+
+ systable_endscan(tgscan);
+ heap_close(trigDesc, AccessShareLock);
+ break;
+ }
+
default:
appendStringInfo(&buffer, "unrecognized object %u %u %d",
object->classId,
*** a/src/backend/catalog/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,
*** 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
***************
*** 1325,1335 **** 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:
--- 1325,1335 ----
switch (sdepForm->classid)
{
case CollationRelationId:
! AlterCollationOwner_oid(sdepForm->objid, newrole, NULL);
break;
case ConversionRelationId:
! AlterConversionOwner_oid(sdepForm->objid, newrole, NULL);
break;
case TypeRelationId:
***************
*** 1355,1361 **** shdepReassignOwned(List *roleids, Oid newrole)
break;
case ProcedureRelationId:
! AlterFunctionOwner_oid(sdepForm->objid, newrole);
break;
case LanguageRelationId:
--- 1355,1361 ----
break;
case ProcedureRelationId:
! AlterFunctionOwner_oid(sdepForm->objid, newrole, NULL);
break;
case LanguageRelationId:
*** a/src/backend/catalog/pg_type.c
--- b/src/backend/catalog/pg_type.c
***************
*** 676,682 **** GenerateTypeDependencies(Oid typeNamespace,
* ALTER TYPE RENAME TO command.
*/
void
! RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
{
Relation pg_type_desc;
HeapTuple tuple;
--- 676,683 ----
* ALTER TYPE RENAME TO command.
*/
void
! RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace,
! CommandContext cmd)
{
Relation pg_type_desc;
HeapTuple tuple;
***************
*** 703,708 **** RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
--- 704,719 ----
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("type \"%s\" already exists", newTypeName)));
+ /* Call BEFORE ALTER TYPE triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = typeOid;
+ cmd->objectname = NameStr(typ->typname);
+ cmd->schemaname = get_namespace_name(typeNamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* OK, do the rename --- tuple is a copy, so OK to scribble on it */
namestrcpy(&(typ->typname), newTypeName);
***************
*** 719,727 **** RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
{
char *arrname = makeArrayTypeName(newTypeName, typeNamespace);
! RenameTypeInternal(arrayOid, arrname, typeNamespace);
pfree(arrname);
}
}
--- 730,745 ----
{
char *arrname = makeArrayTypeName(newTypeName, typeNamespace);
! RenameTypeInternal(arrayOid, arrname, typeNamespace, NULL);
pfree(arrname);
}
+
+ /* Call AFTER ALTER TYPE triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectname = (char *)newTypeName;
+ ExecAfterCommandTriggers(cmd);
+ }
}
***************
*** 822,828 **** moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
newname = makeArrayTypeName(typeName, typeNamespace);
/* Apply the rename */
! RenameTypeInternal(typeOid, newname, typeNamespace);
/*
* We must bump the command counter so that any subsequent use of
--- 840,846 ----
newname = makeArrayTypeName(typeName, typeNamespace);
/* Apply the rename */
! RenameTypeInternal(typeOid, newname, typeNamespace, NULL);
/*
* We must bump the command counter so that any subsequent use of
*** a/src/backend/commands/Makefile
--- b/src/backend/commands/Makefile
***************
*** 12,19 **** subdir = src/backend/commands
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
! OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
! collationcmds.o constraint.o conversioncmds.o copy.o \
dbcommands.o define.o discard.o dropcmds.o explain.o extension.o \
foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
--- 12,19 ----
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
! OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o cmdtrigger.o \
! comment.o collationcmds.o constraint.o conversioncmds.o copy.o \
dbcommands.o define.o discard.o dropcmds.o explain.o extension.o \
foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
*** a/src/backend/commands/aggregatecmds.c
--- b/src/backend/commands/aggregatecmds.c
***************
*** 28,33 ****
--- 28,34 ----
#include "catalog/pg_aggregate.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
+ #include "commands/cmdtrigger.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
***************
*** 46,52 ****
* "args" defines the input type(s).
*/
void
! DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
{
char *aggName;
Oid aggNamespace;
--- 47,54 ----
* "args" defines the input type(s).
*/
void
! DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
! CommandContext cmd)
{
char *aggName;
Oid aggNamespace;
***************
*** 203,209 **** DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
finalfuncName, /* final function name */
sortoperatorName, /* sort operator name */
transTypeId, /* transition data type */
! initval); /* initial condition */
}
--- 205,212 ----
finalfuncName, /* final function name */
sortoperatorName, /* sort operator name */
transTypeId, /* transition data type */
! initval, /* initial condition */
! cmd);
}
***************
*** 212,218 **** DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
* Rename an aggregate.
*/
void
! RenameAggregate(List *name, List *args, const char *newname)
{
Oid procOid;
Oid namespaceOid;
--- 215,221 ----
* Rename an aggregate.
*/
void
! RenameAggregate(List *name, List *args, const char *newname, CommandContext cmd)
{
Oid procOid;
Oid namespaceOid;
***************
*** 258,263 **** RenameAggregate(List *name, List *args, const char *newname)
--- 261,276 ----
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
+ /* Call BEFORE ALTER AGGREGATE triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = NameStr(procForm->proname);
+ cmd->schemaname = get_namespace_name(namespaceOid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* rename */
namestrcpy(&(((Form_pg_proc) GETSTRUCT(tup))->proname), newname);
simple_heap_update(rel, &tup->t_self, tup);
***************
*** 265,277 **** RenameAggregate(List *name, List *args, const char *newname)
heap_close(rel, NoLock);
heap_freetuple(tup);
}
/*
* Change aggregate owner
*/
void
! AlterAggregateOwner(List *name, List *args, Oid newOwnerId)
{
Oid procOid;
--- 278,297 ----
heap_close(rel, NoLock);
heap_freetuple(tup);
+
+ /* Call AFTER ALTER AGGREGATE triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectname = (char *)newname;
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
* Change aggregate owner
*/
void
! AlterAggregateOwner(List *name, List *args, Oid newOwnerId, CommandContext cmd)
{
Oid procOid;
***************
*** 279,283 **** AlterAggregateOwner(List *name, List *args, Oid newOwnerId)
procOid = LookupAggNameTypeNames(name, args, false);
/* The rest is just like a function */
! AlterFunctionOwner_oid(procOid, newOwnerId);
}
--- 299,303 ----
procOid = LookupAggNameTypeNames(name, args, false);
/* The rest is just like a function */
! AlterFunctionOwner_oid(procOid, newOwnerId, cmd);
}
*** a/src/backend/commands/alter.c
--- b/src/backend/commands/alter.c
***************
*** 20,25 ****
--- 20,26 ----
#include "catalog/pg_largeobject.h"
#include "catalog/pg_namespace.h"
#include "commands/alter.h"
+ #include "commands/cmdtrigger.h"
#include "commands/collationcmds.h"
#include "commands/conversioncmds.h"
#include "commands/dbcommands.h"
***************
*** 47,64 ****
void
ExecRenameStmt(RenameStmt *stmt)
{
switch (stmt->renameType)
{
case OBJECT_AGGREGATE:
! RenameAggregate(stmt->object, stmt->objarg, stmt->newname);
break;
case OBJECT_COLLATION:
! RenameCollation(stmt->object, stmt->newname);
break;
case OBJECT_CONVERSION:
! RenameConversion(stmt->object, stmt->newname);
break;
case OBJECT_DATABASE:
--- 48,72 ----
void
ExecRenameStmt(RenameStmt *stmt)
{
+ CommandContextData cmd;
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
switch (stmt->renameType)
{
case OBJECT_AGGREGATE:
! RenameAggregate(stmt->object, stmt->objarg, stmt->newname, &cmd);
break;
case OBJECT_COLLATION:
! RenameCollation(stmt->object, stmt->newname, &cmd);
break;
case OBJECT_CONVERSION:
! RenameConversion(stmt->object, stmt->newname, &cmd);
! break;
!
! case OBJECT_CMDTRIGGER:
! RenameCmdTrigger(stmt->object, stmt->subname, stmt->newname);
break;
case OBJECT_DATABASE:
***************
*** 74,80 **** ExecRenameStmt(RenameStmt *stmt)
break;
case OBJECT_FUNCTION:
! RenameFunction(stmt->object, stmt->objarg, stmt->newname);
break;
case OBJECT_LANGUAGE:
--- 82,88 ----
break;
case OBJECT_FUNCTION:
! RenameFunction(stmt->object, stmt->objarg, stmt->newname, &cmd);
break;
case OBJECT_LANGUAGE:
***************
*** 94,100 **** ExecRenameStmt(RenameStmt *stmt)
break;
case OBJECT_SCHEMA:
! RenameSchema(stmt->subname, stmt->newname);
break;
case OBJECT_TABLESPACE:
--- 102,108 ----
break;
case OBJECT_SCHEMA:
! RenameSchema(stmt->subname, stmt->newname, &cmd);
break;
case OBJECT_TABLESPACE:
***************
*** 106,112 **** ExecRenameStmt(RenameStmt *stmt)
case OBJECT_VIEW:
case OBJECT_INDEX:
case OBJECT_FOREIGN_TABLE:
! RenameRelation(stmt);
break;
case OBJECT_COLUMN:
--- 114,120 ----
case OBJECT_VIEW:
case OBJECT_INDEX:
case OBJECT_FOREIGN_TABLE:
! RenameRelation(stmt, &cmd);
break;
case OBJECT_COLUMN:
***************
*** 115,142 **** ExecRenameStmt(RenameStmt *stmt)
break;
case OBJECT_TRIGGER:
! renametrig(stmt);
break;
case OBJECT_TSPARSER:
! RenameTSParser(stmt->object, stmt->newname);
break;
case OBJECT_TSDICTIONARY:
! RenameTSDictionary(stmt->object, stmt->newname);
break;
case OBJECT_TSTEMPLATE:
! RenameTSTemplate(stmt->object, stmt->newname);
break;
case OBJECT_TSCONFIGURATION:
! RenameTSConfiguration(stmt->object, stmt->newname);
break;
case OBJECT_DOMAIN:
case OBJECT_TYPE:
! RenameType(stmt);
break;
default:
--- 123,150 ----
break;
case OBJECT_TRIGGER:
! renametrig(stmt, &cmd);
break;
case OBJECT_TSPARSER:
! RenameTSParser(stmt->object, stmt->newname, &cmd);
break;
case OBJECT_TSDICTIONARY:
! RenameTSDictionary(stmt->object, stmt->newname, &cmd);
break;
case OBJECT_TSTEMPLATE:
! RenameTSTemplate(stmt->object, stmt->newname, &cmd);
break;
case OBJECT_TSCONFIGURATION:
! RenameTSConfiguration(stmt->object, stmt->newname, &cmd);
break;
case OBJECT_DOMAIN:
case OBJECT_TYPE:
! RenameType(stmt, &cmd);
break;
default:
***************
*** 152,191 **** ExecRenameStmt(RenameStmt *stmt)
void
ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
{
switch (stmt->objectType)
{
case OBJECT_AGGREGATE:
AlterFunctionNamespace(stmt->object, stmt->objarg, true,
! stmt->newschema);
break;
case OBJECT_COLLATION:
! AlterCollationNamespace(stmt->object, stmt->newschema);
break;
case OBJECT_CONVERSION:
! AlterConversionNamespace(stmt->object, stmt->newschema);
break;
case OBJECT_EXTENSION:
! AlterExtensionNamespace(stmt->object, stmt->newschema);
break;
case OBJECT_FUNCTION:
AlterFunctionNamespace(stmt->object, stmt->objarg, false,
! stmt->newschema);
break;
case OBJECT_OPERATOR:
! AlterOperatorNamespace(stmt->object, stmt->objarg, stmt->newschema);
break;
case OBJECT_OPCLASS:
! AlterOpClassNamespace(stmt->object, stmt->addname, stmt->newschema);
break;
case OBJECT_OPFAMILY:
! AlterOpFamilyNamespace(stmt->object, stmt->addname, stmt->newschema);
break;
case OBJECT_SEQUENCE:
--- 160,202 ----
void
ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
{
+ CommandContextData cmd;
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
switch (stmt->objectType)
{
case OBJECT_AGGREGATE:
AlterFunctionNamespace(stmt->object, stmt->objarg, true,
! stmt->newschema, &cmd);
break;
case OBJECT_COLLATION:
! AlterCollationNamespace(stmt->object, stmt->newschema, &cmd);
break;
case OBJECT_CONVERSION:
! AlterConversionNamespace(stmt->object, stmt->newschema, &cmd);
break;
case OBJECT_EXTENSION:
! AlterExtensionNamespace(stmt->object, stmt->newschema, &cmd);
break;
case OBJECT_FUNCTION:
AlterFunctionNamespace(stmt->object, stmt->objarg, false,
! stmt->newschema, &cmd);
break;
case OBJECT_OPERATOR:
! AlterOperatorNamespace(stmt->object, stmt->objarg, stmt->newschema, &cmd);
break;
case OBJECT_OPCLASS:
! AlterOpClassNamespace(stmt->object, stmt->addname, stmt->newschema, &cmd);
break;
case OBJECT_OPFAMILY:
! AlterOpFamilyNamespace(stmt->object, stmt->addname, stmt->newschema, &cmd);
break;
case OBJECT_SEQUENCE:
***************
*** 196,219 **** ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
break;
case OBJECT_TSPARSER:
! AlterTSParserNamespace(stmt->object, stmt->newschema);
break;
case OBJECT_TSDICTIONARY:
! AlterTSDictionaryNamespace(stmt->object, stmt->newschema);
break;
case OBJECT_TSTEMPLATE:
! AlterTSTemplateNamespace(stmt->object, stmt->newschema);
break;
case OBJECT_TSCONFIGURATION:
! AlterTSConfigurationNamespace(stmt->object, stmt->newschema);
break;
case OBJECT_TYPE:
case OBJECT_DOMAIN:
! AlterTypeNamespace(stmt->object, stmt->newschema, stmt->objectType);
break;
default:
--- 207,230 ----
break;
case OBJECT_TSPARSER:
! AlterTSParserNamespace(stmt->object, stmt->newschema, &cmd);
break;
case OBJECT_TSDICTIONARY:
! AlterTSDictionaryNamespace(stmt->object, stmt->newschema, &cmd);
break;
case OBJECT_TSTEMPLATE:
! AlterTSTemplateNamespace(stmt->object, stmt->newschema, &cmd);
break;
case OBJECT_TSCONFIGURATION:
! AlterTSConfigurationNamespace(stmt->object, stmt->newschema, &cmd);
break;
case OBJECT_TYPE:
case OBJECT_DOMAIN:
! AlterTypeNamespace(stmt->object, stmt->newschema, stmt->objectType, &cmd);
break;
default:
***************
*** 235,240 **** ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
--- 246,254 ----
*
* Returns the OID of the object's previous namespace, or InvalidOid if
* object doesn't have a schema.
+ *
+ * Doesn't run any command trigger for those sub-commands, so just pass a NULL
+ * CommandContext to functions implementing the ALTER.
*/
Oid
AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid)
***************
*** 271,285 **** AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid)
}
case OCLASS_PROC:
! oldNspOid = AlterFunctionNamespace_oid(objid, nspOid);
break;
case OCLASS_TYPE:
! oldNspOid = AlterTypeNamespace_oid(objid, nspOid);
break;
case OCLASS_COLLATION:
! oldNspOid = AlterCollationNamespace_oid(objid, nspOid);
break;
case OCLASS_CONVERSION:
--- 285,299 ----
}
case OCLASS_PROC:
! oldNspOid = AlterFunctionNamespace_oid(objid, nspOid, NULL);
break;
case OCLASS_TYPE:
! oldNspOid = AlterTypeNamespace_oid(objid, nspOid, NULL);
break;
case OCLASS_COLLATION:
! oldNspOid = AlterCollationNamespace_oid(objid, nspOid, NULL);
break;
case OCLASS_CONVERSION:
***************
*** 349,355 **** Oid
AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId,
Oid objid, Oid nspOid,
int Anum_name, int Anum_namespace, int Anum_owner,
! AclObjectKind acl_kind)
{
Oid classId = RelationGetRelid(rel);
Oid oldNspOid;
--- 363,369 ----
AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId,
Oid objid, Oid nspOid,
int Anum_name, int Anum_namespace, int Anum_owner,
! AclObjectKind acl_kind, CommandContext cmd)
{
Oid classId = RelationGetRelid(rel);
Oid oldNspOid;
***************
*** 419,424 **** AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId,
--- 433,448 ----
getObjectDescriptionOids(classId, objid),
get_namespace_name(nspOid))));
+ /* Call BEFORE ALTER OBJECT triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = objid;
+ cmd->objectname = NameStr(*(DatumGetName(name)));
+ cmd->schemaname = get_namespace_name(oldNspOid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* Build modified tuple */
values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum));
nulls = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
***************
*** 441,446 **** AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId,
--- 465,476 ----
changeDependencyFor(classId, objid,
NamespaceRelationId, oldNspOid, nspOid);
+ /* Call AFTER ALTER OBJECT triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->schemaname = get_namespace_name(nspOid);
+ ExecAfterCommandTriggers(cmd);
+ }
return oldNspOid;
}
***************
*** 453,471 **** void
ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
{
Oid newowner = get_role_oid(stmt->newowner, false);
switch (stmt->objectType)
{
case OBJECT_AGGREGATE:
! AlterAggregateOwner(stmt->object, stmt->objarg, newowner);
break;
case OBJECT_COLLATION:
! AlterCollationOwner(stmt->object, newowner);
break;
case OBJECT_CONVERSION:
! AlterConversionOwner(stmt->object, newowner);
break;
case OBJECT_DATABASE:
--- 483,504 ----
ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
{
Oid newowner = get_role_oid(stmt->newowner, false);
+ CommandContextData cmd;
+
+ InitCommandContext(&cmd, (Node *)stmt, false);
switch (stmt->objectType)
{
case OBJECT_AGGREGATE:
! AlterAggregateOwner(stmt->object, stmt->objarg, newowner, &cmd);
break;
case OBJECT_COLLATION:
! AlterCollationOwner(stmt->object, newowner, &cmd);
break;
case OBJECT_CONVERSION:
! AlterConversionOwner(stmt->object, newowner, &cmd);
break;
case OBJECT_DATABASE:
***************
*** 473,479 **** ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
break;
case OBJECT_FUNCTION:
! AlterFunctionOwner(stmt->object, stmt->objarg, newowner);
break;
case OBJECT_LANGUAGE:
--- 506,512 ----
break;
case OBJECT_FUNCTION:
! AlterFunctionOwner(stmt->object, stmt->objarg, newowner, &cmd);
break;
case OBJECT_LANGUAGE:
***************
*** 489,495 **** ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
AlterOperatorOwner(stmt->object,
(TypeName *) linitial(stmt->objarg),
(TypeName *) lsecond(stmt->objarg),
! newowner);
break;
case OBJECT_OPCLASS:
--- 522,528 ----
AlterOperatorOwner(stmt->object,
(TypeName *) linitial(stmt->objarg),
(TypeName *) lsecond(stmt->objarg),
! newowner, &cmd);
break;
case OBJECT_OPCLASS:
***************
*** 501,507 **** ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
break;
case OBJECT_SCHEMA:
! AlterSchemaOwner(strVal(linitial(stmt->object)), newowner);
break;
case OBJECT_TABLESPACE:
--- 534,540 ----
break;
case OBJECT_SCHEMA:
! AlterSchemaOwner(strVal(linitial(stmt->object)), newowner, &cmd);
break;
case OBJECT_TABLESPACE:
***************
*** 510,524 **** ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
case OBJECT_TYPE:
case OBJECT_DOMAIN: /* same as TYPE */
! AlterTypeOwner(stmt->object, newowner, stmt->objectType);
break;
case OBJECT_TSDICTIONARY:
! AlterTSDictionaryOwner(stmt->object, newowner);
break;
case OBJECT_TSCONFIGURATION:
! AlterTSConfigurationOwner(stmt->object, newowner);
break;
case OBJECT_FDW:
--- 543,557 ----
case OBJECT_TYPE:
case OBJECT_DOMAIN: /* same as TYPE */
! AlterTypeOwner(stmt->object, newowner, stmt->objectType, &cmd);
break;
case OBJECT_TSDICTIONARY:
! AlterTSDictionaryOwner(stmt->object, newowner, &cmd);
break;
case OBJECT_TSCONFIGURATION:
! AlterTSConfigurationOwner(stmt->object, newowner, &cmd);
break;
case OBJECT_FDW:
*** a/src/backend/commands/cluster.c
--- b/src/backend/commands/cluster.c
***************
*** 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);
}
--- 1486,1498 ----
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,731 ----
+ /*-------------------------------------------------------------------------
+ *
+ * cmdtrigger.c
+ * PostgreSQL COMMAND TRIGGER support code.
+ *
+ * Portions Copyright (c) 2011, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/backend/commands/cmdtrigger.c
+ *
+ *-------------------------------------------------------------------------
+ */
+ #include "postgres.h"
+
+ #include "access/heapam.h"
+ #include "access/sysattr.h"
+ #include "catalog/catalog.h"
+ #include "catalog/dependency.h"
+ #include "catalog/indexing.h"
+ #include "catalog/objectaccess.h"
+ #include "catalog/pg_cmdtrigger.h"
+ #include "catalog/pg_language.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_trigger.h"
+ #include "catalog/pg_type.h"
+ #include "commands/cmdtrigger.h"
+ #include "commands/dbcommands.h"
+ #include "commands/trigger.h"
+ #include "parser/parse_func.h"
+ #include "pgstat.h"
+ #include "miscadmin.h"
+ #include "utils/acl.h"
+ #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
+ #include "utils/lsyscache.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ #include "utils/tqual.h"
+ #include "utils/syscache.h"
+ #include "tcop/utility.h"
+
+ static void check_cmdtrigger_name(const char *command, const char *trigname, Relation tgrel);
+
+ /*
+ * Check permission: command triggers are only available for superusers. Raise
+ * an exception when requirements are not fullfilled.
+ *
+ * It's not clear how to accept that database owners be able to create command
+ * triggers, a superuser could run a command that fires a trigger's procedure
+ * written by the database owner and now running with superuser privileges.
+ */
+ static void
+ CheckCmdTriggerPrivileges()
+ {
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("must be superuser to use command triggers"))));
+ }
+
+ /*
+ * Insert Command Trigger Tuple
+ *
+ * Insert the new pg_cmdtrigger row, and return the OID assigned to the new
+ * row.
+ */
+ static Oid
+ InsertCmdTriggerTuple(Relation tgrel,
+ char *command, char *trigname, Oid funcoid, char ctgtype)
+ {
+ Oid trigoid;
+ HeapTuple tuple;
+ Datum values[Natts_pg_trigger];
+ bool nulls[Natts_pg_trigger];
+ ObjectAddress myself, referenced;
+
+ /*
+ * Build the new pg_trigger tuple.
+ */
+ memset(nulls, false, sizeof(nulls));
+
+ values[Anum_pg_cmdtrigger_ctgcommand - 1] = NameGetDatum(command);
+ values[Anum_pg_cmdtrigger_ctgname - 1] = NameGetDatum(trigname);
+ values[Anum_pg_cmdtrigger_ctgfoid - 1] = ObjectIdGetDatum(funcoid);
+ values[Anum_pg_cmdtrigger_ctgtype - 1] = CharGetDatum(ctgtype);
+ values[Anum_pg_cmdtrigger_ctgenabled - 1] = CharGetDatum(TRIGGER_FIRES_ON_ORIGIN);
+
+ tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
+
+ simple_heap_insert(tgrel, tuple);
+
+ CatalogUpdateIndexes(tgrel, tuple);
+
+ /* remember oid for record dependencies */
+ trigoid = HeapTupleGetOid(tuple);
+
+ heap_freetuple(tuple);
+
+ /*
+ * Record dependencies for trigger. Always place a normal dependency on
+ * the function.
+ */
+ myself.classId = CmdTriggerRelationId;
+ myself.objectId = trigoid;
+ myself.objectSubId = 0;
+
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = funcoid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ return trigoid;
+ }
+
+ /*
+ * Create a trigger. Returns the OID of the created trigger.
+ */
+ void
+ CreateCmdTrigger(CreateCmdTrigStmt *stmt, const char *queryString)
+ {
+ Relation tgrel;
+ ListCell *c;
+ /* cmd trigger args: when, cmd_tag, objectId, schemaname, objectname [,parsetree] */
+ Oid fargtypes[5] = {TEXTOID, TEXTOID, OIDOID, TEXTOID, TEXTOID};
+ Oid fargtypes_c[6] = {TEXTOID, TEXTOID, OIDOID, TEXTOID, TEXTOID, INTERNALOID};
+ Oid funcoid;
+ Oid funcrettype;
+
+ CheckCmdTriggerPrivileges();
+
+ /*
+ * Find and validate the trigger function. When the function is coded in C
+ * it receives an internal argument which is the parse tree as a Node *.
+ *
+ * Only C coded functions can accept an argument of type internal, so we
+ * don't have to explicitely check about the prolang here.
+ */
+ funcoid = LookupFuncName(stmt->funcname, 6, fargtypes_c, true);
+ if (funcoid == InvalidOid)
+ funcoid = LookupFuncName(stmt->funcname, 5, fargtypes, false);
+
+ /* we need the trigger type to validate the return type */
+ funcrettype = get_func_rettype(funcoid);
+
+ /*
+ * Generate the trigger's OID now, so that we can use it in the name if
+ * needed.
+ */
+ tgrel = heap_open(CmdTriggerRelationId, RowExclusiveLock);
+
+ foreach(c, stmt->command)
+ {
+ Oid trigoid;
+ A_Const *con = (A_Const *) lfirst(c);
+ char *command = strVal(&con->val);
+
+ /*
+ * Add some restrictions. We don't allow for AFTER command triggers on
+ * commands that do their own transaction management, such as VACUUM and
+ * CREATE INDEX CONCURRENTLY, because RAISE EXCEPTION at this point is
+ * meaningless, the work as already been commited.
+ *
+ * CREATE INDEX CONCURRENTLY has no specific command tag and can not be
+ * captured here, so we just document that not AFTER command trigger
+ * will get run.
+ */
+ if (stmt->timing == CMD_TRIGGER_FIRED_AFTER
+ && (strcmp(command, "VACUUM") == 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("AFTER VACUUM command triggers are not implemented")));
+
+ if (stmt->timing == CMD_TRIGGER_FIRED_AFTER
+ && (strcmp(command, "CREATE INDEX") == 0))
+ ereport(WARNING,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("CREATE INDEX CONCURRENTLY is not supported"),
+ errdetail("The command trigger will not get fired.")));
+
+ /*
+ * Scan pg_cmdtrigger for existing triggers on command. We do this only
+ * to give a nice error message if there's already a trigger of the
+ * same name. (The unique index on ctgcommand/ctgname would complain
+ * anyway.)
+ *
+ * NOTE that this is cool only because we have AccessExclusiveLock on
+ * the relation, so the trigger set won't be changing underneath us.
+ */
+ check_cmdtrigger_name(command, stmt->trigname, tgrel);
+
+ if (funcrettype != VOIDOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("function \"%s\" must return type \"void\"",
+ NameListToString(stmt->funcname))));
+
+ trigoid = InsertCmdTriggerTuple(tgrel, command, stmt->trigname, funcoid, stmt->timing);
+ }
+ heap_close(tgrel, RowExclusiveLock);
+ }
+
+ /*
+ * DropTrigger - drop an individual trigger by name
+ */
+ void
+ DropCmdTrigger(DropCmdTrigStmt *stmt)
+ {
+ ListCell *c;
+
+ CheckCmdTriggerPrivileges();
+
+ foreach(c, stmt->command)
+ {
+ ObjectAddress object;
+ A_Const *con = (A_Const *) lfirst(c);
+ char *command = strVal(&con->val);
+
+ object.classId = CmdTriggerRelationId;
+ object.objectId = get_cmdtrigger_oid(command, stmt->trigname,
+ stmt->missing_ok);
+ object.objectSubId = 0;
+
+ if (!OidIsValid(object.objectId))
+ {
+ ereport(NOTICE,
+ (errmsg("trigger \"%s\" for command \"%s\" does not exist, skipping",
+ stmt->trigname, command)));
+ break;
+ }
+
+ /*
+ * Do the deletion
+ */
+ performDeletion(&object, stmt->behavior, 0);
+ }
+ }
+
+ /*
+ * Guts of command trigger deletion.
+ */
+ void
+ RemoveCmdTriggerById(Oid trigOid)
+ {
+ Relation tgrel;
+ SysScanDesc tgscan;
+ ScanKeyData skey[1];
+ HeapTuple tup;
+
+ tgrel = heap_open(CmdTriggerRelationId, RowExclusiveLock);
+
+ /*
+ * Find the trigger to delete.
+ */
+ ScanKeyInit(&skey[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(trigOid));
+
+ tgscan = systable_beginscan(tgrel, CmdTriggerOidIndexId, true,
+ SnapshotNow, 1, skey);
+
+ tup = systable_getnext(tgscan);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "could not find tuple for command trigger %u", trigOid);
+
+ /*
+ * Delete the pg_cmdtrigger tuple.
+ */
+ simple_heap_delete(tgrel, &tup->t_self);
+
+ systable_endscan(tgscan);
+ heap_close(tgrel, RowExclusiveLock);
+ }
+
+ /*
+ * ALTER TRIGGER foo ON COMMAND ... ENABLE|DISABLE|ENABLE ALWAYS|REPLICA
+ */
+ void
+ AlterCmdTrigger(AlterCmdTrigStmt *stmt)
+ {
+ Relation tgrel;
+ SysScanDesc tgscan;
+ ScanKeyData skey[2];
+ HeapTuple tup;
+ Form_pg_cmdtrigger cmdForm;
+ char tgenabled = pstrdup(stmt->tgenabled)[0]; /* works with gram.y */
+
+ CheckCmdTriggerPrivileges();
+
+ tgrel = heap_open(CmdTriggerRelationId, RowExclusiveLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_cmdtrigger_ctgcommand,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(stmt->command));
+ ScanKeyInit(&skey[1],
+ Anum_pg_cmdtrigger_ctgname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(stmt->trigname));
+
+ tgscan = systable_beginscan(tgrel, CmdTriggerCommandNameIndexId, true,
+ SnapshotNow, 2, skey);
+
+ tup = systable_getnext(tgscan);
+
+ if (!HeapTupleIsValid(tup))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("trigger \"%s\" for command \"%s\" does not exist, skipping",
+ stmt->trigname, stmt->command)));
+
+ /* Copy tuple so we can modify it below */
+ tup = heap_copytuple(tup);
+ cmdForm = (Form_pg_cmdtrigger) GETSTRUCT(tup);
+
+ systable_endscan(tgscan);
+
+ cmdForm->ctgenabled = tgenabled;
+
+ simple_heap_update(tgrel, &tup->t_self, tup);
+ CatalogUpdateIndexes(tgrel, tup);
+
+ heap_close(tgrel, RowExclusiveLock);
+ heap_freetuple(tup);
+ }
+
+
+ /*
+ * Rename command trigger
+ */
+ void
+ RenameCmdTrigger(List *name, const char *trigname, const char *newname)
+ {
+ SysScanDesc tgscan;
+ ScanKeyData skey[2];
+ HeapTuple tup;
+ Relation rel;
+ Form_pg_cmdtrigger cmdForm;
+ char *command;
+
+ CheckCmdTriggerPrivileges();
+
+ Assert(list_length(name) == 1);
+ command = strVal((Value *)linitial(name));
+
+ rel = heap_open(CmdTriggerRelationId, RowExclusiveLock);
+
+ //FIXME: need a row level lock here
+ /* newname must be available */
+ check_cmdtrigger_name(command, newname, rel);
+
+ /* get existing tuple */
+ ScanKeyInit(&skey[0],
+ Anum_pg_cmdtrigger_ctgcommand,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(command));
+ ScanKeyInit(&skey[1],
+ Anum_pg_cmdtrigger_ctgname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(trigname));
+
+ tgscan = systable_beginscan(rel, CmdTriggerCommandNameIndexId, true,
+ SnapshotNow, 2, skey);
+
+ tup = systable_getnext(tgscan);
+
+ if (!HeapTupleIsValid(tup))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("trigger \"%s\" for command \"%s\" does not exist, skipping",
+ trigname, command)));
+
+ /* Copy tuple so we can modify it below */
+ tup = heap_copytuple(tup);
+ cmdForm = (Form_pg_cmdtrigger) GETSTRUCT(tup);
+
+ systable_endscan(tgscan);
+
+ /* rename */
+ namestrcpy(&(cmdForm->ctgname), newname);
+ simple_heap_update(rel, &tup->t_self, tup);
+ CatalogUpdateIndexes(rel, tup);
+
+ heap_freetuple(tup);
+ heap_close(rel, NoLock);
+ }
+
+ /*
+ * get_cmdtrigger_oid - Look up a trigger by name to find its OID.
+ *
+ * If missing_ok is false, throw an error if trigger not found. If
+ * true, just return InvalidOid.
+ */
+ Oid
+ get_cmdtrigger_oid(const char *command, const char *trigname, bool missing_ok)
+ {
+ Relation tgrel;
+ ScanKeyData skey[2];
+ SysScanDesc tgscan;
+ HeapTuple tup;
+ Oid oid;
+
+ /*
+ * Find the trigger, verify permissions, set up object address
+ */
+ tgrel = heap_open(CmdTriggerRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_cmdtrigger_ctgcommand,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(command));
+ ScanKeyInit(&skey[1],
+ Anum_pg_cmdtrigger_ctgname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(trigname));
+
+ tgscan = systable_beginscan(tgrel, CmdTriggerCommandNameIndexId, true,
+ SnapshotNow, 1, skey);
+
+ tup = systable_getnext(tgscan);
+
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("trigger \"%s\" for command \"%s\" does not exist, skipping",
+ trigname, command)));
+ oid = InvalidOid;
+ }
+ else
+ {
+ oid = HeapTupleGetOid(tup);
+ }
+
+ systable_endscan(tgscan);
+ heap_close(tgrel, AccessShareLock);
+ return oid;
+ }
+
+ /*
+ * Scan pg_cmdtrigger for existing triggers on command. We do this only to
+ * give a nice error message if there's already a trigger of the same name.
+ */
+ void
+ check_cmdtrigger_name(const char *command, const char *trigname, Relation tgrel)
+ {
+ SysScanDesc tgscan;
+ ScanKeyData skey[2];
+ HeapTuple tuple;
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_cmdtrigger_ctgcommand,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(command));
+ ScanKeyInit(&skey[1],
+ Anum_pg_cmdtrigger_ctgname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(trigname));
+
+ tgscan = systable_beginscan(tgrel, CmdTriggerCommandNameIndexId, true,
+ SnapshotNow, 2, skey);
+
+ tuple = systable_getnext(tgscan);
+
+ elog(DEBUG1, "check_cmdtrigger_name(%s, %s)", command, trigname);
+
+ if (HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("trigger \"%s\" for command \"%s\" already exists",
+ trigname, command)));
+ systable_endscan(tgscan);
+ }
+
+ /*
+ * Functions to execute the command triggers.
+ *
+ * We call the functions that matches the command triggers definitions in
+ * alphabetical order, and give them those arguments:
+ *
+ * command tag, text
+ * objectId, oid
+ * schemaname, text
+ * objectname, text
+ *
+ */
+
+ /*
+ * Scan the catalogs and fill in the CommandContext procedures that we will
+ * have to call before and after the command.
+ */
+ bool
+ ListCommandTriggers(CommandContext cmd)
+ {
+ int count = 0;
+ Relation rel, irel;
+ SysScanDesc scandesc;
+ HeapTuple tuple;
+ ScanKeyData entry[1];
+
+ cmd->before = cmd->after = NIL;
+
+ rel = heap_open(CmdTriggerRelationId, AccessShareLock);
+ irel = index_open(CmdTriggerCommandNameIndexId, AccessShareLock);
+
+ ScanKeyInit(&entry[0],
+ Anum_pg_cmdtrigger_ctgcommand,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(cmd->tag));
+
+ scandesc = systable_beginscan_ordered(rel, irel, SnapshotNow, 1, entry);
+
+ while (HeapTupleIsValid(tuple = systable_getnext_ordered(scandesc, ForwardScanDirection)))
+ {
+ Form_pg_cmdtrigger form = (Form_pg_cmdtrigger) GETSTRUCT(tuple);
+
+ if (form->ctgenabled == TRIGGER_DISABLED)
+ {
+ continue;
+ }
+ else if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
+ {
+ if (form->ctgenabled == TRIGGER_FIRES_ON_ORIGIN)
+ continue;
+ }
+ else /* ORIGIN or LOCAL role */
+ {
+ if (form->ctgenabled == TRIGGER_FIRES_ON_REPLICA)
+ continue;
+ }
+
+ switch (form->ctgtype)
+ {
+ case CMD_TRIGGER_FIRED_BEFORE:
+ cmd->before = lappend_oid(cmd->before, form->ctgfoid);
+ break;
+
+ case CMD_TRIGGER_FIRED_AFTER:
+ cmd->after = lappend_oid(cmd->after, form->ctgfoid);
+ break;
+ }
+ count++;
+ }
+ systable_endscan_ordered(scandesc);
+
+ index_close(irel, AccessShareLock);
+ heap_close(rel, AccessShareLock);
+
+ return count > 0;
+ }
+
+ static bool
+ call_cmdtrigger_procedure(CommandContext cmd,
+ RegProcedure proc,
+ const char *when,
+ MemoryContext per_command_context)
+ {
+ FmgrInfo flinfo;
+ FunctionCallInfoData fcinfo;
+ PgStat_FunctionCallUsage fcusage;
+ Datum result;
+ HeapTuple procedureTuple;
+ Form_pg_proc procedureStruct;
+ int nargs = 5;
+
+ fmgr_info_cxt(proc, &flinfo, per_command_context);
+
+ /* we need the procedure's language here to know how many args to call it
+ * with
+ */
+ procedureTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(proc));
+ if (!HeapTupleIsValid(procedureTuple))
+ elog(ERROR, "cache lookup failed for function %u", proc);
+ procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
+
+ if (procedureStruct->prolang == ClanguageId)
+ nargs = 6;
+
+ ReleaseSysCache(procedureTuple);
+
+ /* Can't use OidFunctionCallN because we might get a NULL result */
+ InitFunctionCallInfoData(fcinfo, &flinfo, nargs, InvalidOid, NULL, NULL);
+
+ fcinfo.arg[0] = PointerGetDatum(cstring_to_text(pstrdup(when)));
+
+ /* We support triggers ON ANY COMMAND so all fields here are nullable. */
+ if (cmd->tag != NULL)
+ fcinfo.arg[1] = PointerGetDatum(cstring_to_text(pstrdup(cmd->tag)));
+
+ fcinfo.arg[2] = ObjectIdGetDatum(cmd->objectId);
+
+ if (cmd->schemaname != NULL)
+ fcinfo.arg[3] = PointerGetDatum(cstring_to_text(pstrdup(cmd->schemaname)));
+
+ if (cmd->objectname != NULL)
+ fcinfo.arg[4] = PointerGetDatum(cstring_to_text(pstrdup(cmd->objectname)));
+
+ fcinfo.argnull[0] = false;
+ fcinfo.argnull[1] = cmd->tag == NULL;
+ fcinfo.argnull[2] = cmd->objectId == InvalidOid;
+ fcinfo.argnull[3] = cmd->schemaname == NULL;
+ fcinfo.argnull[4] = cmd->objectname == NULL;
+
+ if (nargs == 6)
+ {
+ fcinfo.arg[5] = PointerGetDatum(cmd->parsetree);
+ fcinfo.argnull[5] = false;
+ }
+
+ pgstat_init_function_usage(&fcinfo, &fcusage);
+
+ result = FunctionCallInvoke(&fcinfo);
+
+ pgstat_end_function_usage(&fcusage, true);
+
+
+ if (!fcinfo.isnull && DatumGetBool(result) == false)
+ return false;
+ return true;
+ }
+
+ /*
+ * A BEFORE command trigger can choose to "abort" the command by returning
+ * false. This function is called by ExecBeforeOrInsteadOfCommandTriggers() so
+ * is not exposed to other modules.
+ */
+ static void
+ exec_command_triggers_internal(CommandContext cmd, List *procs, const char *when)
+ {
+ MemoryContext per_command_context, oldContext;
+ ListCell *cell;
+
+ /*
+ * Do the functions evaluation in a per-command memory context, so that
+ * leaked memory will be reclaimed once per command.
+ */
+ per_command_context =
+ AllocSetContextCreate(CurrentMemoryContext,
+ "CommandTriggerContext",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+
+ oldContext = MemoryContextSwitchTo(per_command_context);
+ MemoryContextReset(per_command_context);
+
+ foreach(cell, procs)
+ {
+ Oid proc = lfirst_oid(cell);
+ call_cmdtrigger_procedure(cmd, (RegProcedure)proc, when, per_command_context);
+ }
+ MemoryContextSwitchTo(oldContext);
+ }
+
+ /*
+ * Routine to call to setup a CommandContextData structure.
+ *
+ * This ensures that cmd->before and cmd->after are set to meaningful values,
+ * always NIL when list_triggers is false.
+ *
+ * In case of ANY trigger init we don't want to list triggers associated with
+ * the real command tag, we have another API to do that, see
+ * ExecBeforeAnyCommandTriggers() and ExecAfterAnyCommandTriggers().
+ */
+ void
+ InitCommandContext(CommandContext cmd, const Node *stmt, bool list_any_triggers)
+ {
+ cmd->tag = (char *) CreateCommandTag((Node *)stmt);
+ cmd->parsetree = (Node *)stmt;
+ cmd->objectId = InvalidOid;
+ cmd->objectname = NULL;
+ cmd->schemaname = NULL;
+ cmd->before = NIL;
+ cmd->after = NIL;
+
+ if (list_any_triggers)
+ {
+ /* list procedures for "ANY" command */
+ char *tag = cmd->tag;
+
+ cmd->tag = "ANY";
+ ListCommandTriggers(cmd);
+ cmd->tag = tag;
+ }
+ else
+ ListCommandTriggers(cmd);
+ }
+
+ /*
+ * InitCommandContext() must have been called when CommandFiresTriggers() is
+ * called. When CommandFiresTriggers() returns false, cmd structure needs not
+ * be initialized further.
+ *
+ * There's no place where we can skip BEFORE command trigger initialization
+ * when we have an AFTER command triggers to run, because objectname and
+ * schemaname are needed in both places, so we check both here.
+ */
+ bool
+ CommandFiresTriggers(CommandContext cmd)
+ {
+ return cmd != NULL && (cmd->before != NIL || cmd->after != NIL);
+ }
+
+ /*
+ * It's still interresting to avoid preparing the Command Context for AFTER
+ * command triggers when we have none to Execute, so we provide this API too.
+ */
+ bool
+ CommandFiresAfterTriggers(CommandContext cmd)
+ {
+ return cmd != NULL && cmd->after != NIL;
+ }
+
+ /*
+ * In the various Exec...CommandTriggers functions, we still protect against
+ * and empty procedure list so as not to create a MemoryContext then switch to
+ * it unnecessarily.
+ */
+ void
+ ExecBeforeCommandTriggers(CommandContext cmd)
+ {
+ if (cmd != NULL && cmd->before != NIL)
+ exec_command_triggers_internal(cmd, cmd->before, "BEFORE");
+ }
+
+ void
+ ExecAfterCommandTriggers(CommandContext cmd)
+ {
+ if (cmd != NULL && cmd->after != NIL)
+ exec_command_triggers_internal(cmd, cmd->after, "AFTER");
+ }
*** a/src/backend/commands/collationcmds.c
--- b/src/backend/commands/collationcmds.c
***************
*** 22,27 ****
--- 22,28 ----
#include "catalog/pg_collation.h"
#include "catalog/pg_collation_fn.h"
#include "commands/alter.h"
+ #include "commands/cmdtrigger.h"
#include "commands/collationcmds.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
***************
*** 34,46 ****
#include "utils/syscache.h"
static void AlterCollationOwner_internal(Relation rel, Oid collationOid,
! Oid newOwnerId);
/*
* CREATE COLLATION
*/
void
! DefineCollation(List *names, List *parameters)
{
char *collName;
Oid collNamespace;
--- 35,47 ----
#include "utils/syscache.h"
static void AlterCollationOwner_internal(Relation rel, Oid collationOid,
! Oid newOwnerId, CommandContext cmd);
/*
* CREATE COLLATION
*/
void
! DefineCollation(List *names, List *parameters, CommandContext cmd)
{
char *collName;
Oid collNamespace;
***************
*** 137,154 **** DefineCollation(List *names, List *parameters)
GetUserId(),
GetDatabaseEncoding(),
collcollate,
! collctype);
! /* check that the locales can be loaded */
! CommandCounterIncrement();
! (void) pg_newlocale_from_collation(newoid);
}
/*
* Rename collation
*/
void
! RenameCollation(List *name, const char *newname)
{
Oid collationOid;
Oid namespaceOid;
--- 138,160 ----
GetUserId(),
GetDatabaseEncoding(),
collcollate,
! collctype,
! cmd);
! /* before or instead of command trigger might have cancelled the command */
! if (OidIsValid(newoid))
! {
! /* check that the locales can be loaded */
! CommandCounterIncrement();
! (void) pg_newlocale_from_collation(newoid);
! }
}
/*
* Rename collation
*/
void
! RenameCollation(List *name, const char *newname, CommandContext cmd)
{
Oid collationOid;
Oid namespaceOid;
***************
*** 200,220 **** RenameCollation(List *name, const char *newname)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
/* rename */
namestrcpy(&(((Form_pg_collation) GETSTRUCT(tup))->collname), newname);
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
heap_freetuple(tup);
-
heap_close(rel, RowExclusiveLock);
}
/*
* Change collation owner, by name
*/
void
! AlterCollationOwner(List *name, Oid newOwnerId)
{
Oid collationOid;
Relation rel;
--- 206,242 ----
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
+ /* Call BEFORE ALTER COLLATION triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = NameStr((((Form_pg_collation) GETSTRUCT(tup))->collname));
+ cmd->schemaname = get_namespace_name(namespaceOid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* rename */
namestrcpy(&(((Form_pg_collation) GETSTRUCT(tup))->collname), newname);
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
heap_freetuple(tup);
heap_close(rel, RowExclusiveLock);
+
+ /* Call AFTER ALTER COLLATION triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectname = (char *)newname;
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
* Change collation owner, by name
*/
void
! AlterCollationOwner(List *name, Oid newOwnerId, CommandContext cmd)
{
Oid collationOid;
Relation rel;
***************
*** 223,229 **** AlterCollationOwner(List *name, Oid newOwnerId)
collationOid = get_collation_oid(name, false);
! AlterCollationOwner_internal(rel, collationOid, newOwnerId);
heap_close(rel, RowExclusiveLock);
}
--- 245,251 ----
collationOid = get_collation_oid(name, false);
! AlterCollationOwner_internal(rel, collationOid, newOwnerId, cmd);
heap_close(rel, RowExclusiveLock);
}
***************
*** 232,244 **** AlterCollationOwner(List *name, Oid newOwnerId)
* Change collation owner, by oid
*/
void
! AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId)
{
Relation rel;
rel = heap_open(CollationRelationId, RowExclusiveLock);
! AlterCollationOwner_internal(rel, collationOid, newOwnerId);
heap_close(rel, RowExclusiveLock);
}
--- 254,266 ----
* Change collation owner, by oid
*/
void
! AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId, CommandContext cmd)
{
Relation rel;
rel = heap_open(CollationRelationId, RowExclusiveLock);
! AlterCollationOwner_internal(rel, collationOid, newOwnerId, cmd);
heap_close(rel, RowExclusiveLock);
}
***************
*** 250,256 **** AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId)
* open and suitably locked; it will not be closed.
*/
static void
! AlterCollationOwner_internal(Relation rel, Oid collationOid, Oid newOwnerId)
{
Form_pg_collation collForm;
HeapTuple tup;
--- 272,279 ----
* open and suitably locked; it will not be closed.
*/
static void
! AlterCollationOwner_internal(Relation rel, Oid collationOid, Oid newOwnerId,
! CommandContext cmd)
{
Form_pg_collation collForm;
HeapTuple tup;
***************
*** 291,296 **** AlterCollationOwner_internal(Relation rel, Oid collationOid, Oid newOwnerId)
--- 314,329 ----
get_namespace_name(collForm->collnamespace));
}
+ /* Call BEFORE ALTER COLLATION triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = NameStr(collForm->collname);
+ cmd->schemaname = get_namespace_name(collForm->collnamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/*
* Modify the owner --- okay to scribble on tup because it's a copy
*/
***************
*** 303,310 **** AlterCollationOwner_internal(Relation rel, Oid collationOid, Oid newOwnerId)
/* Update owner dependency reference */
changeDependencyOnOwner(CollationRelationId, collationOid,
newOwnerId);
- }
heap_freetuple(tup);
}
--- 336,346 ----
/* Update owner dependency reference */
changeDependencyOnOwner(CollationRelationId, collationOid,
newOwnerId);
+ /* Call AFTER ALTER COLLATION triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
+ }
heap_freetuple(tup);
}
***************
*** 312,318 **** AlterCollationOwner_internal(Relation rel, Oid collationOid, Oid newOwnerId)
* Execute ALTER COLLATION SET SCHEMA
*/
void
! AlterCollationNamespace(List *name, const char *newschema)
{
Oid collOid,
nspOid;
--- 348,354 ----
* Execute ALTER COLLATION SET SCHEMA
*/
void
! AlterCollationNamespace(List *name, const char *newschema, CommandContext cmd)
{
Oid collOid,
nspOid;
***************
*** 321,334 **** AlterCollationNamespace(List *name, const char *newschema)
nspOid = LookupCreationNamespace(newschema);
! AlterCollationNamespace_oid(collOid, nspOid);
}
/*
* Change collation schema, by oid
*/
Oid
! AlterCollationNamespace_oid(Oid collOid, Oid newNspOid)
{
Oid oldNspOid;
Relation rel;
--- 357,370 ----
nspOid = LookupCreationNamespace(newschema);
! AlterCollationNamespace_oid(collOid, nspOid, cmd);
}
/*
* Change collation schema, by oid
*/
Oid
! AlterCollationNamespace_oid(Oid collOid, Oid newNspOid, CommandContext cmd)
{
Oid oldNspOid;
Relation rel;
***************
*** 374,380 **** AlterCollationNamespace_oid(Oid collOid, Oid newNspOid)
Anum_pg_collation_collname,
Anum_pg_collation_collnamespace,
Anum_pg_collation_collowner,
! ACL_KIND_COLLATION);
heap_close(rel, RowExclusiveLock);
--- 410,416 ----
Anum_pg_collation_collname,
Anum_pg_collation_collnamespace,
Anum_pg_collation_collowner,
! ACL_KIND_COLLATION, cmd);
heap_close(rel, RowExclusiveLock);
*** a/src/backend/commands/conversioncmds.c
--- b/src/backend/commands/conversioncmds.c
***************
*** 31,37 ****
#include "utils/syscache.h"
static void AlterConversionOwner_internal(Relation rel, Oid conversionOid,
! Oid newOwnerId);
/*
* CREATE CONVERSION
--- 31,37 ----
#include "utils/syscache.h"
static void AlterConversionOwner_internal(Relation rel, Oid conversionOid,
! Oid newOwnerId, CommandContext cmd);
/*
* CREATE CONVERSION
***************
*** 121,127 **** CreateConversionCommand(CreateConversionStmt *stmt)
* Rename conversion
*/
void
! RenameConversion(List *name, const char *newname)
{
Oid conversionOid;
Oid namespaceOid;
--- 121,127 ----
* Rename conversion
*/
void
! RenameConversion(List *name, const char *newname, CommandContext cmd)
{
Oid conversionOid;
Oid namespaceOid;
***************
*** 159,164 **** RenameConversion(List *name, const char *newname)
--- 159,174 ----
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
+ /* Call BEFORE ALTER CONVERSION triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = NameStr((((Form_pg_conversion) GETSTRUCT(tup))->conname));
+ cmd->schemaname = get_namespace_name(namespaceOid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* rename */
namestrcpy(&(((Form_pg_conversion) GETSTRUCT(tup))->conname), newname);
simple_heap_update(rel, &tup->t_self, tup);
***************
*** 166,178 **** RenameConversion(List *name, const char *newname)
heap_close(rel, NoLock);
heap_freetuple(tup);
}
/*
* Change conversion owner, by name
*/
void
! AlterConversionOwner(List *name, Oid newOwnerId)
{
Oid conversionOid;
Relation rel;
--- 176,195 ----
heap_close(rel, NoLock);
heap_freetuple(tup);
+
+ /* Call AFTER ALTER CONVERSION triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectname = (char *)newname;
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
* Change conversion owner, by name
*/
void
! AlterConversionOwner(List *name, Oid newOwnerId, CommandContext cmd)
{
Oid conversionOid;
Relation rel;
***************
*** 181,187 **** AlterConversionOwner(List *name, Oid newOwnerId)
conversionOid = get_conversion_oid(name, false);
! AlterConversionOwner_internal(rel, conversionOid, newOwnerId);
heap_close(rel, NoLock);
}
--- 198,204 ----
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);
}
--- 207,219 ----
* 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;
--- 225,232 ----
* 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)
--- 267,282 ----
get_namespace_name(convForm->connamespace));
}
+ /* Call BEFORE ALTER CONVERSION triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = NameStr(convForm->conname);
+ cmd->schemaname = get_namespace_name(convForm->connamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/*
* Modify the owner --- okay to scribble on tup because it's a copy
*/
***************
*** 261,266 **** AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId)
--- 289,298 ----
/* 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;
--- 302,308 ----
* 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);
}
--- 320,326 ----
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);
--- 341,347 ----
Anum_pg_conversion_conname,
Anum_pg_conversion_connamespace,
Anum_pg_conversion_conowner,
! ACL_KIND_CONVERSION, NULL);
heap_close(rel, RowExclusiveLock);
*** a/src/backend/commands/dbcommands.c
--- b/src/backend/commands/dbcommands.c
***************
*** 49,54 ****
--- 49,55 ----
#include "storage/ipc.h"
#include "storage/procarray.h"
#include "storage/smgr.h"
+ #include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
***************
*** 732,739 **** createdb_failure_callback(int code, Datum arg)
* DROP DATABASE
*/
void
! dropdb(const char *dbname, bool missing_ok)
{
Oid db_id;
bool db_istemplate;
Relation pgdbrel;
--- 733,742 ----
* DROP DATABASE
*/
void
! dropdb(const DropdbStmt *stmt)
{
+ const char *dbname = stmt->dbname;
+ bool missing_ok = stmt->missing_ok;
Oid db_id;
bool db_istemplate;
Relation pgdbrel;
*** a/src/backend/commands/dropcmds.c
--- b/src/backend/commands/dropcmds.c
***************
*** 21,32 ****
--- 21,35 ----
#include "catalog/objectaddress.h"
#include "catalog/pg_class.h"
#include "catalog/pg_proc.h"
+ #include "commands/cmdtrigger.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_type.h"
+ #include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+ #include "utils/lsyscache.h"
#include "utils/syscache.h"
static void does_not_exist_skipping(ObjectType objtype,
***************
*** 49,54 **** RemoveObjects(DropStmt *stmt)
--- 52,59 ----
ObjectAddresses *objects;
ListCell *cell1;
ListCell *cell2 = NULL;
+ int i = 0, n = list_length(stmt->objects);
+ CommandContext *cmds = (CommandContext *) palloc(n * sizeof(CommandContext));
objects = new_object_addresses();
***************
*** 59,64 **** RemoveObjects(DropStmt *stmt)
--- 64,70 ----
List *objargs = NIL;
Relation relation = NULL;
Oid namespaceId;
+ CommandContextData cmd;
if (stmt->arguments)
{
***************
*** 77,82 **** RemoveObjects(DropStmt *stmt)
--- 83,89 ----
if (!OidIsValid(address.objectId))
{
does_not_exist_skipping(stmt->removeType, objname, objargs);
+ cmds[i++] = NULL;
continue;
}
***************
*** 115,126 **** RemoveObjects(DropStmt *stmt)
--- 122,157 ----
if (relation)
heap_close(relation, NoLock);
+ /*
+ * Call BEFORE DROP command triggers
+ */
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = address.objectId;
+ cmd.objectname = strVal(list_nth(objname, list_length(objname)-1));
+ cmd.schemaname = get_namespace_name(namespaceId);
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+ cmds[i++] = &cmd;
+
add_exact_object_address(&address, objects);
}
/* Here we really delete them. */
performMultipleDeletions(objects, stmt->behavior, 0);
+ /* Call AFTER DROP command triggers */
+ for(i = 0; iobjectId = InvalidOid;
+ ExecAfterCommandTriggers(cmds[i]);
+ }
+ }
free_object_addresses(objects);
}
*** a/src/backend/commands/extension.c
--- b/src/backend/commands/extension.c
***************
*** 39,44 ****
--- 39,45 ----
#include "catalog/pg_namespace.h"
#include "catalog/pg_type.h"
#include "commands/alter.h"
+ #include "commands/cmdtrigger.h"
#include "commands/comment.h"
#include "commands/extension.h"
#include "commands/schemacmds.h"
***************
*** 1190,1200 **** CreateExtension(CreateExtensionStmt *stmt)
--- 1191,1215 ----
List *requiredSchemas;
Oid extensionOid;
ListCell *lc;
+ CommandContextData cmd;
/* Check extension name validity before any filesystem access */
check_valid_extension_name(stmt->extname);
/*
+ * Call BEFORE CREATE EXTENSION triggers
+ */
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = InvalidOid;
+ cmd.objectname = stmt->extname;
+ cmd.schemaname = NULL;
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+ /*
* Check for duplicate extension name. The unique index on
* pg_extension.extname would catch this anyway, and serves as a backstop
* in case of race conditions; but this is a friendlier error message, and
***************
*** 1467,1472 **** CreateExtension(CreateExtensionStmt *stmt)
--- 1482,1494 ----
*/
ApplyExtensionUpdates(extensionOid, pcontrol,
versionName, updateVersions);
+
+ /* Call AFTER CREATE EXTENSION triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = extensionOid;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
/*
***************
*** 2186,2192 **** pg_extension_config_dump(PG_FUNCTION_ARGS)
* Execute ALTER EXTENSION SET SCHEMA
*/
void
! AlterExtensionNamespace(List *names, const char *newschema)
{
char *extensionName;
Oid extensionOid;
--- 2208,2214 ----
* Execute ALTER EXTENSION SET SCHEMA
*/
void
! AlterExtensionNamespace(List *names, const char *newschema, CommandContext cmd)
{
char *extensionName;
Oid extensionOid;
***************
*** 2247,2252 **** AlterExtensionNamespace(List *names, const char *newschema)
--- 2269,2284 ----
systable_endscan(extScan);
+ /* Call BEFORE ALTER EXTENSION triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = extensionOid;
+ cmd->objectname = extensionName;
+ cmd->schemaname = NULL;
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/*
* If the extension is already in the target schema, just silently do
* nothing.
***************
*** 2342,2347 **** AlterExtensionNamespace(List *names, const char *newschema)
--- 2374,2383 ----
/* update dependencies to point to the new schema */
changeDependencyFor(ExtensionRelationId, extensionOid,
NamespaceRelationId, oldNspOid, nspOid);
+
+ /* Call AFTER ALTER EXTENSION triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
/*
***************
*** 2363,2368 **** ExecAlterExtensionStmt(AlterExtensionStmt *stmt)
--- 2399,2405 ----
Datum datum;
bool isnull;
ListCell *lc;
+ CommandContextData cmd;
/*
* We use global variables to track the extension being created, so we can
***************
*** 2415,2420 **** ExecAlterExtensionStmt(AlterExtensionStmt *stmt)
--- 2452,2470 ----
stmt->extname);
/*
+ * Call BEFORE ALTER EXTENSION triggers
+ */
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = extensionOid;
+ cmd.objectname = stmt->extname;
+ cmd.schemaname = NULL;
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+ /*
* Read the primary control file. Note we assume that it does not contain
* any non-ASCII data, so there is no need to worry about encoding at this
* point.
***************
*** 2480,2485 **** ExecAlterExtensionStmt(AlterExtensionStmt *stmt)
--- 2530,2539 ----
*/
ApplyExtensionUpdates(extensionOid, control,
oldVersionName, updateVersions);
+
+ /* Call AFTER ALTER EXTENSION triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ ExecAfterCommandTriggers(&cmd);
}
/*
*** a/src/backend/commands/functioncmds.c
--- b/src/backend/commands/functioncmds.c
***************
*** 55,60 ****
--- 55,61 ----
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "parser/parse_type.h"
+ #include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
***************
*** 66,72 ****
static void AlterFunctionOwner_internal(Relation rel, HeapTuple tup,
! Oid newOwnerId);
/*
--- 67,73 ----
static void AlterFunctionOwner_internal(Relation rel, HeapTuple tup,
! Oid newOwnerId, CommandContext cmd);
/*
***************
*** 802,807 **** CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
--- 803,809 ----
{
char *probin_str;
char *prosrc_str;
+ Oid procOid;
Oid prorettype;
bool returnsSet;
char *language;
***************
*** 827,832 **** CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
--- 829,835 ----
HeapTuple languageTuple;
Form_pg_language languageStruct;
List *as_clause;
+ CommandContextData cmd;
/* Convert list of names to a name and namespace */
namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname,
***************
*** 970,1001 **** CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
errmsg("ROWS is not applicable when function does not return a set")));
/*
* And now that we have all the parameters, and know we're permitted to do
* so, go ahead and create the function.
*/
! ProcedureCreate(funcname,
! namespaceId,
! stmt->replace,
! returnsSet,
! prorettype,
! languageOid,
! languageValidator,
! prosrc_str, /* converted to text later */
! probin_str, /* converted to text later */
! false, /* not an aggregate */
! isWindowFunc,
! security,
! isLeakProof,
! isStrict,
! volatility,
! parameterTypes,
! PointerGetDatum(allParameterTypes),
! PointerGetDatum(parameterModes),
! PointerGetDatum(parameterNames),
! parameterDefaults,
! PointerGetDatum(proconfig),
! procost,
! prorows);
}
--- 973,1025 ----
errmsg("ROWS is not applicable when function does not return a set")));
/*
+ * Call BEFORE CREATE FUNCTION triggers
+ */
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = InvalidOid;
+ cmd.objectname = (char *)funcname;
+ cmd.schemaname = get_namespace_name(namespaceId);
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+ /*
* And now that we have all the parameters, and know we're permitted to do
* so, go ahead and create the function.
*/
! procOid =
! ProcedureCreate(funcname,
! namespaceId,
! stmt->replace,
! returnsSet,
! prorettype,
! languageOid,
! languageValidator,
! prosrc_str, /* converted to text later */
! probin_str, /* converted to text later */
! false, /* not an aggregate */
! isWindowFunc,
! security,
! isLeakProof,
! isStrict,
! volatility,
! parameterTypes,
! PointerGetDatum(allParameterTypes),
! PointerGetDatum(parameterModes),
! PointerGetDatum(parameterNames),
! parameterDefaults,
! PointerGetDatum(proconfig),
! procost,
! prorows);
!
! /* Call AFTER CREATE FUNCTION triggers */
! if (CommandFiresAfterTriggers(&cmd))
! {
! cmd.objectId = procOid;
! ExecAfterCommandTriggers(&cmd);
! }
}
***************
*** 1053,1059 **** RemoveFunctionById(Oid funcOid)
* Rename function
*/
void
! RenameFunction(List *name, List *argtypes, const char *newname)
{
Oid procOid;
Oid namespaceOid;
--- 1077,1083 ----
* Rename function
*/
void
! RenameFunction(List *name, List *argtypes, const char *newname, CommandContext cmd)
{
Oid procOid;
Oid namespaceOid;
***************
*** 1107,1112 **** RenameFunction(List *name, List *argtypes, const char *newname)
--- 1131,1146 ----
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
+ /* Call BEFORE ALTER FUNCTION triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = procOid;
+ cmd->objectname = NameListToString(name);
+ cmd->schemaname = get_namespace_name(namespaceOid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* rename */
namestrcpy(&(procForm->proname), newname);
simple_heap_update(rel, &tup->t_self, tup);
***************
*** 1114,1126 **** RenameFunction(List *name, List *argtypes, const char *newname)
heap_close(rel, NoLock);
heap_freetuple(tup);
}
/*
* Change function owner by name and args
*/
void
! AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId)
{
Relation rel;
Oid procOid;
--- 1148,1168 ----
heap_close(rel, NoLock);
heap_freetuple(tup);
+
+ /* Call AFTER ALTER FUNCTION triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectname = (char *)newname;
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
* Change function owner by name and args
*/
void
! AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId,
! CommandContext cmd)
{
Relation rel;
Oid procOid;
***************
*** 1141,1147 **** AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId)
NameListToString(name)),
errhint("Use ALTER AGGREGATE to change owner of aggregate functions.")));
! AlterFunctionOwner_internal(rel, tup, newOwnerId);
heap_close(rel, NoLock);
}
--- 1183,1189 ----
NameListToString(name)),
errhint("Use ALTER AGGREGATE to change owner of aggregate functions.")));
! AlterFunctionOwner_internal(rel, tup, newOwnerId, cmd);
heap_close(rel, NoLock);
}
***************
*** 1150,1156 **** AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId)
* Change function owner by Oid
*/
void
! AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId)
{
Relation rel;
HeapTuple tup;
--- 1192,1198 ----
* Change function owner by Oid
*/
void
! AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId, CommandContext cmd)
{
Relation rel;
HeapTuple tup;
***************
*** 1160,1172 **** AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId)
tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procOid));
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for function %u", procOid);
! AlterFunctionOwner_internal(rel, tup, newOwnerId);
heap_close(rel, NoLock);
}
static void
! AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
{
Form_pg_proc procForm;
AclResult aclresult;
--- 1202,1215 ----
tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procOid));
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for function %u", procOid);
! AlterFunctionOwner_internal(rel, tup, newOwnerId, cmd);
heap_close(rel, NoLock);
}
static void
! AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId,
! CommandContext cmd)
{
Form_pg_proc procForm;
AclResult aclresult;
***************
*** 1212,1217 **** AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
--- 1255,1269 ----
get_namespace_name(procForm->pronamespace));
}
+ /* Call BEFORE ALTER FUNCTION triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = procOid;
+ cmd->objectname = NameStr(procForm->proname);
+ cmd->schemaname = get_namespace_name(procForm->pronamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
memset(repl_null, false, sizeof(repl_null));
memset(repl_repl, false, sizeof(repl_repl));
***************
*** 1243,1248 **** AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
--- 1295,1304 ----
/* Update owner dependency reference */
changeDependencyOnOwner(ProcedureRelationId, procOid, newOwnerId);
+
+ /* Call AFTER ALTER FUNCTION triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
ReleaseSysCache(tup);
***************
*** 1822,1828 **** DropCastById(Oid castOid)
*/
void
AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
! const char *newschema)
{
Oid procOid;
Oid nspOid;
--- 1878,1884 ----
*/
void
AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
! const char *newschema, CommandContext cmd)
{
Oid procOid;
Oid nspOid;
***************
*** 1836,1846 **** AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
/* get schema OID and check its permissions */
nspOid = LookupCreationNamespace(newschema);
! AlterFunctionNamespace_oid(procOid, nspOid);
}
Oid
! AlterFunctionNamespace_oid(Oid procOid, Oid nspOid)
{
Oid oldNspOid;
HeapTuple tup;
--- 1892,1902 ----
/* get schema OID and check its permissions */
nspOid = LookupCreationNamespace(newschema);
! AlterFunctionNamespace_oid(procOid, nspOid, cmd);
}
Oid
! AlterFunctionNamespace_oid(Oid procOid, Oid nspOid, CommandContext cmd)
{
Oid oldNspOid;
HeapTuple tup;
***************
*** 1875,1880 **** AlterFunctionNamespace_oid(Oid procOid, Oid nspOid)
--- 1931,1946 ----
NameStr(proc->proname),
get_namespace_name(nspOid))));
+ /* Call BEFORE ALTER FUNCTION triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = procOid;
+ cmd->objectname = NameStr(proc->proname);
+ cmd->schemaname = get_namespace_name(oldNspOid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* OK, modify the pg_proc row */
/* tup is a copy, so we can scribble directly on it */
***************
*** 1893,1898 **** AlterFunctionNamespace_oid(Oid procOid, Oid nspOid)
--- 1959,1969 ----
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;
}
*** a/src/backend/commands/opclasscmds.c
--- b/src/backend/commands/opclasscmds.c
***************
*** 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;
--- 1919,1926 ----
* 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);
}
--- 1942,1948 ----
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);
--- 1961,1967 ----
Anum_pg_opclass_opcname,
Anum_pg_opclass_opcnamespace,
Anum_pg_opclass_opcowner,
! ACL_KIND_OPCLASS, NULL);
heap_close(rel, RowExclusiveLock);
***************
*** 2128,2134 **** get_am_oid(const char *amname, bool missing_ok)
* ALTER OPERATOR FAMILY any_name USING access_method SET SCHEMA name
*/
void
! AlterOpFamilyNamespace(List *name, char *access_method, const char *newschema)
{
Oid amOid;
Relation rel;
--- 2129,2136 ----
* 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);
}
--- 2152,2158 ----
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);
--- 2171,2177 ----
Anum_pg_opfamily_opfname,
Anum_pg_opfamily_opfnamespace,
Anum_pg_opfamily_opfowner,
! ACL_KIND_OPFAMILY, NULL);
heap_close(rel, RowExclusiveLock);
*** a/src/backend/commands/operatorcmds.c
--- b/src/backend/commands/operatorcmds.c
***************
*** 51,57 ****
#include "utils/syscache.h"
! static void AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId);
/*
* DefineOperator
--- 51,58 ----
#include "utils/syscache.h"
! static void AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId,
! CommandContext cmd);
/*
* DefineOperator
***************
*** 62,68 **** static void AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerI
* 'parameters' is a list of DefElem
*/
void
! DefineOperator(List *names, List *parameters)
{
char *oprName;
Oid oprNamespace;
--- 63,69 ----
* 'parameters' is a list of DefElem
*/
void
! DefineOperator(List *names, List *parameters, CommandContext cmd)
{
char *oprName;
Oid oprNamespace;
***************
*** 310,316 **** DefineOperator(List *names, List *parameters)
restrictionOid, /* optional restrict. sel. procedure */
joinOid, /* optional join sel. procedure name */
canMerge, /* operator merges */
! canHash); /* operator hashes */
}
--- 311,318 ----
restrictionOid, /* optional restrict. sel. procedure */
joinOid, /* optional join sel. procedure name */
canMerge, /* operator merges */
! canHash, /* operator hashes */
! cmd);
}
***************
*** 343,349 **** AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId)
rel = heap_open(OperatorRelationId, RowExclusiveLock);
! AlterOperatorOwner_internal(rel, operOid, newOwnerId);
heap_close(rel, NoLock);
}
--- 345,351 ----
rel = heap_open(OperatorRelationId, RowExclusiveLock);
! AlterOperatorOwner_internal(rel, operOid, newOwnerId, NULL);
heap_close(rel, NoLock);
}
***************
*** 353,359 **** AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId)
*/
void
AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2,
! Oid newOwnerId)
{
Oid operOid;
Relation rel;
--- 355,361 ----
*/
void
AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2,
! Oid newOwnerId, CommandContext cmd)
{
Oid operOid;
Relation rel;
***************
*** 364,376 **** AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2,
typeName1, typeName2,
false, -1);
! AlterOperatorOwner_internal(rel, operOid, newOwnerId);
heap_close(rel, NoLock);
}
static void
! AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId)
{
HeapTuple tup;
AclResult aclresult;
--- 366,379 ----
typeName1, typeName2,
false, -1);
! AlterOperatorOwner_internal(rel, operOid, newOwnerId, cmd);
heap_close(rel, NoLock);
}
static void
! AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId,
! CommandContext cmd)
{
HeapTuple tup;
AclResult aclresult;
***************
*** 410,415 **** AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId)
--- 413,428 ----
get_namespace_name(oprForm->oprnamespace));
}
+ /* Call BEFORE ALTER OPERATOR triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = operOid;
+ cmd->objectname = NameStr(oprForm->oprname);
+ cmd->schemaname = get_namespace_name(oprForm->oprnamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/*
* Modify the owner --- okay to scribble on tup because it's a copy
*/
***************
*** 424,436 **** AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId)
}
heap_freetuple(tup);
}
/*
* Execute ALTER OPERATOR SET SCHEMA
*/
void
! AlterOperatorNamespace(List *names, List *argtypes, const char *newschema)
{
List *operatorName = names;
TypeName *typeName1 = (TypeName *) linitial(argtypes);
--- 437,454 ----
}
heap_freetuple(tup);
+
+ /* Call AFTER ALTER OPERATOR triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
/*
* Execute ALTER OPERATOR SET SCHEMA
*/
void
! AlterOperatorNamespace(List *names, List *argtypes, const char *newschema,
! CommandContext cmd)
{
List *operatorName = names;
TypeName *typeName1 = (TypeName *) linitial(argtypes);
***************
*** 454,460 **** AlterOperatorNamespace(List *names, List *argtypes, const char *newschema)
Anum_pg_operator_oprname,
Anum_pg_operator_oprnamespace,
Anum_pg_operator_oprowner,
! ACL_KIND_OPER);
heap_close(rel, RowExclusiveLock);
}
--- 472,478 ----
Anum_pg_operator_oprname,
Anum_pg_operator_oprnamespace,
Anum_pg_operator_oprowner,
! ACL_KIND_OPER, cmd);
heap_close(rel, RowExclusiveLock);
}
***************
*** 472,478 **** AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid)
Anum_pg_operator_oprname,
Anum_pg_operator_oprnamespace,
Anum_pg_operator_oprowner,
! ACL_KIND_OPER);
heap_close(rel, RowExclusiveLock);
--- 490,496 ----
Anum_pg_operator_oprname,
Anum_pg_operator_oprnamespace,
Anum_pg_operator_oprowner,
! ACL_KIND_OPER, NULL);
heap_close(rel, RowExclusiveLock);
*** a/src/backend/commands/schemacmds.c
--- b/src/backend/commands/schemacmds.c
***************
*** 20,25 ****
--- 20,26 ----
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_namespace.h"
+ #include "commands/cmdtrigger.h"
#include "commands/dbcommands.h"
#include "commands/schemacmds.h"
#include "miscadmin.h"
***************
*** 31,37 ****
#include "utils/syscache.h"
! static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId);
/*
* CREATE SCHEMA
--- 32,39 ----
#include "utils/syscache.h"
! static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel,
! Oid newOwnerId, CommandContext cmd);
/*
* CREATE SCHEMA
***************
*** 49,54 **** CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
--- 51,57 ----
Oid saved_uid;
int save_sec_context;
AclResult aclresult;
+ CommandContextData cmd;
GetUserIdAndSecContext(&saved_uid, &save_sec_context);
***************
*** 82,87 **** CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
--- 85,103 ----
errdetail("The prefix \"pg_\" is reserved for system schemas.")));
/*
+ * Call BEFORE CREATE SCHEMA triggers (before changing authorization)
+ */
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = InvalidOid;
+ cmd.objectname = (char *)schemaName;
+ cmd.schemaname = NULL; /* a schema does not live in another schema */
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+ /*
* If the requested authorization is different from the current user,
* temporarily set the current user so that the object(s) will be created
* with the correct ownership.
***************
*** 144,149 **** CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
--- 160,172 ----
/* Reset current user and security context */
SetUserIdAndSecContext(saved_uid, save_sec_context);
+
+ /* Call AFTER CREATE SCHEMA triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = namespaceId;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
/*
***************
*** 174,180 **** RemoveSchemaById(Oid schemaOid)
* Rename schema
*/
void
! RenameSchema(const char *oldname, const char *newname)
{
HeapTuple tup;
Relation rel;
--- 197,203 ----
* Rename schema
*/
void
! RenameSchema(const char *oldname, const char *newname, CommandContext cmd)
{
HeapTuple tup;
Relation rel;
***************
*** 211,216 **** RenameSchema(const char *oldname, const char *newname)
--- 234,249 ----
errmsg("unacceptable schema name \"%s\"", newname),
errdetail("The prefix \"pg_\" is reserved for system schemas.")));
+ /* Call BEFORE ALTER SCHEMA triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = (char *)oldname;
+ cmd->schemaname = NULL;
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* rename */
namestrcpy(&(((Form_pg_namespace) GETSTRUCT(tup))->nspname), newname);
simple_heap_update(rel, &tup->t_self, tup);
***************
*** 218,223 **** RenameSchema(const char *oldname, const char *newname)
--- 251,263 ----
heap_close(rel, NoLock);
heap_freetuple(tup);
+
+ /* Call AFTER ALTER SCHEMA triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectname = (char *)newname;
+ ExecAfterCommandTriggers(cmd);
+ }
}
void
***************
*** 232,238 **** AlterSchemaOwner_oid(Oid oid, Oid newOwnerId)
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for schema %u", oid);
! AlterSchemaOwner_internal(tup, rel, newOwnerId);
ReleaseSysCache(tup);
--- 272,278 ----
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for schema %u", oid);
! AlterSchemaOwner_internal(tup, rel, newOwnerId, NULL);
ReleaseSysCache(tup);
***************
*** 244,250 **** AlterSchemaOwner_oid(Oid oid, Oid newOwnerId)
* Change schema owner
*/
void
! AlterSchemaOwner(const char *name, Oid newOwnerId)
{
HeapTuple tup;
Relation rel;
--- 284,290 ----
* Change schema owner
*/
void
! AlterSchemaOwner(const char *name, Oid newOwnerId, CommandContext cmd)
{
HeapTuple tup;
Relation rel;
***************
*** 257,263 **** AlterSchemaOwner(const char *name, Oid newOwnerId)
(errcode(ERRCODE_UNDEFINED_SCHEMA),
errmsg("schema \"%s\" does not exist", name)));
! AlterSchemaOwner_internal(tup, rel, newOwnerId);
ReleaseSysCache(tup);
--- 297,303 ----
(errcode(ERRCODE_UNDEFINED_SCHEMA),
errmsg("schema \"%s\" does not exist", name)));
! AlterSchemaOwner_internal(tup, rel, newOwnerId, cmd);
ReleaseSysCache(tup);
***************
*** 265,271 **** AlterSchemaOwner(const char *name, Oid newOwnerId)
}
static void
! AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
{
Form_pg_namespace nspForm;
--- 305,312 ----
}
static void
! AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId,
! CommandContext cmd)
{
Form_pg_namespace nspForm;
***************
*** 312,317 **** AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
--- 353,368 ----
aclcheck_error(aclresult, ACL_KIND_DATABASE,
get_database_name(MyDatabaseId));
+ /* Call BEFORE ALTER SCHEMA triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(tup);
+ cmd->objectname = NameStr(nspForm->nspname);
+ cmd->schemaname = NULL;
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
memset(repl_null, false, sizeof(repl_null));
memset(repl_repl, false, sizeof(repl_repl));
***************
*** 343,348 **** AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
/* Update owner dependency reference */
changeDependencyOnOwner(NamespaceRelationId, HeapTupleGetOid(tup),
newOwnerId);
- }
}
--- 394,402 ----
/* Update owner dependency reference */
changeDependencyOnOwner(NamespaceRelationId, HeapTupleGetOid(tup),
newOwnerId);
+ /* Call AFTER ALTER SCHEMA triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
+ }
}
*** a/src/backend/commands/sequence.c
--- b/src/backend/commands/sequence.c
***************
*** 20,25 ****
--- 20,26 ----
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
+ #include "commands/cmdtrigger.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
#include "funcapi.h"
***************
*** 28,33 ****
--- 29,35 ----
#include "storage/lmgr.h"
#include "storage/proc.h"
#include "storage/smgr.h"
+ #include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
***************
*** 115,127 **** DefineSequence(CreateSeqStmt *seq)
bool null[SEQ_COL_LASTCOL];
int i;
NameData name;
/* Unlogged sequences are not implemented -- not clear if useful. */
if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unlogged sequences are not supported")));
-
/* Check and set all option values */
init_params(seq->options, true, &new, &owned_by);
--- 117,129 ----
bool null[SEQ_COL_LASTCOL];
int i;
NameData name;
+ CommandContextData cmd;
/* Unlogged sequences are not implemented -- not clear if useful. */
if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unlogged sequences are not supported")));
/* Check and set all option values */
init_params(seq->options, true, &new, &owned_by);
***************
*** 211,216 **** DefineSequence(CreateSeqStmt *seq)
--- 213,232 ----
stmt->tablespacename = NULL;
stmt->if_not_exists = false;
+ /*
+ * Call BEFORE CREATE SEQUENCE triggers
+ */
+ InitCommandContext(&cmd, (Node *)seq, false);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = InvalidOid;
+ cmd.objectname = NameStr(name);
+ cmd.schemaname = NULL; /* can't publish it easily enough here */
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
seqoid = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId);
Assert(seqoid != InvalidOid);
***************
*** 226,231 **** DefineSequence(CreateSeqStmt *seq)
--- 242,254 ----
process_owned_by(rel, owned_by);
heap_close(rel, NoLock);
+
+ /* Call AFTER CREATE SEQUENCE triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = seqoid;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
/*
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 41,46 ****
--- 41,47 ----
#include "catalog/pg_type_fn.h"
#include "catalog/storage.h"
#include "catalog/toasting.h"
+ #include "commands/cmdtrigger.h"
#include "commands/cluster.h"
#include "commands/comment.h"
#include "commands/defrem.h"
***************
*** 73,78 ****
--- 74,80 ----
#include "storage/lock.h"
#include "storage/predicate.h"
#include "storage/smgr.h"
+ #include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
***************
*** 735,740 **** RemoveRelations(DropStmt *drop)
--- 737,744 ----
ObjectAddresses *objects;
char relkind;
ListCell *cell;
+ int i = 0, n = list_length(drop->objects);
+ CommandContext *cmds = (CommandContext *) palloc(n * sizeof(CommandContext));
/*
* First we identify all the relations, then we delete them in a single
***************
*** 781,786 **** RemoveRelations(DropStmt *drop)
--- 785,791 ----
Oid relOid;
ObjectAddress obj;
struct DropRelationCallbackState state;
+ CommandContextData cmd;
/*
* These next few steps are a great deal like relation_openrv, but we
***************
*** 806,814 **** RemoveRelations(DropStmt *drop)
--- 811,835 ----
if (!OidIsValid(relOid))
{
DropErrorMsgNonExistent(rel->relname, relkind, drop->missing_ok);
+ cmds[i++] = NULL;
continue;
}
+ /*
+ * Call BEFORE DROP command triggers
+ */
+ InitCommandContext(&cmd, (Node *)drop, false);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = relOid;
+ cmd.objectname = get_rel_name(relOid);
+ cmd.schemaname = get_namespace_name(get_rel_namespace(relOid));
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+ cmds[i++] = &cmd;
+
/* OK, we're ready to delete this one */
obj.classId = RelationRelationId;
obj.objectId = relOid;
***************
*** 819,824 **** RemoveRelations(DropStmt *drop)
--- 840,855 ----
performMultipleDeletions(objects, drop->behavior, 0);
+ /* Call AFTER DROP command triggers */
+ for(i = 0; iobjectId = InvalidOid;
+ ExecAfterCommandTriggers(cmds[i]);
+ }
+ }
+
free_object_addresses(objects);
}
***************
*** 2328,2334 **** renameatt(RenameStmt *stmt)
* Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE RENAME
*/
void
! RenameRelation(RenameStmt *stmt)
{
Oid relid;
--- 2359,2365 ----
* Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE RENAME
*/
void
! RenameRelation(RenameStmt *stmt, CommandContext cmd)
{
Oid relid;
***************
*** 2353,2359 **** RenameRelation(RenameStmt *stmt)
}
/* Do the work */
! RenameRelationInternal(relid, stmt->newname);
}
/*
--- 2384,2390 ----
}
/* Do the work */
! RenameRelationInternal(relid, stmt->newname, cmd);
}
/*
***************
*** 2366,2372 **** RenameRelation(RenameStmt *stmt)
* sequence, AFAIK there's no need for it to be there.
*/
void
! RenameRelationInternal(Oid myrelid, const char *newrelname)
{
Relation targetrelation;
Relation relrelation; /* for RELATION relation */
--- 2397,2403 ----
* sequence, AFAIK there's no need for it to be there.
*/
void
! RenameRelationInternal(Oid myrelid, const char *newrelname, CommandContext cmd)
{
Relation targetrelation;
Relation relrelation; /* for RELATION relation */
***************
*** 2397,2402 **** RenameRelationInternal(Oid myrelid, const char *newrelname)
--- 2428,2443 ----
errmsg("relation \"%s\" already exists",
newrelname)));
+ /* Call BEFORE ALTER relation triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = HeapTupleGetOid(reltup);
+ cmd->objectname = NameStr(relform->relname);
+ cmd->schemaname = get_namespace_name(namespaceId);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/*
* Update pg_class tuple with new relname. (Scribbling on reltup is OK
* because it's a copy...)
***************
*** 2416,2422 **** RenameRelationInternal(Oid myrelid, const char *newrelname)
*/
if (OidIsValid(targetrelation->rd_rel->reltype))
RenameTypeInternal(targetrelation->rd_rel->reltype,
! newrelname, namespaceId);
/*
* Also rename the associated constraint, if any.
--- 2457,2463 ----
*/
if (OidIsValid(targetrelation->rd_rel->reltype))
RenameTypeInternal(targetrelation->rd_rel->reltype,
! newrelname, namespaceId, NULL);
/*
* Also rename the associated constraint, if any.
***************
*** 2433,2438 **** RenameRelationInternal(Oid myrelid, const char *newrelname)
--- 2474,2486 ----
* Close rel, but keep exclusive lock!
*/
relation_close(targetrelation, NoLock);
+
+ /* Call AFTER ALTER relation triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectname = (char *)newrelname;
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
***************
*** 5331,5337 **** ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
check_rights,
skip_build,
quiet,
! false);
/*
* If TryReuseIndex() stashed a relfilenode for us, we used it for the new
--- 5379,5385 ----
check_rights,
skip_build,
quiet,
! false, NULL);
/*
* If TryReuseIndex() stashed a relfilenode for us, we used it for the new
***************
*** 5390,5396 **** ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
ereport(NOTICE,
(errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
indexName, constraintName)));
! RenameRelationInternal(index_oid, constraintName);
}
/* Extra checks needed if making primary key */
--- 5438,5444 ----
ereport(NOTICE,
(errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
indexName, constraintName)));
! RenameRelationInternal(index_oid, constraintName, NULL);
}
/* Extra checks needed if making primary key */
***************
*** 9479,9484 **** AlterTableNamespace(AlterObjectSchemaStmt *stmt)
--- 9527,9533 ----
Oid nspOid;
Relation classRel;
RangeVar *newrv;
+ CommandContextData cmd;
relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
stmt->missing_ok, false,
***************
*** 9519,9531 **** AlterTableNamespace(AlterObjectSchemaStmt *stmt)
/* common checks on switching namespaces */
CheckSetNamespace(oldNspOid, nspOid, RelationRelationId, relid);
/* OK, modify the pg_class row and pg_depend entry */
classRel = heap_open(RelationRelationId, RowExclusiveLock);
AlterRelationNamespaceInternal(classRel, relid, oldNspOid, nspOid, true);
/* Fix the table's row type too */
! AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid, false, false);
/* Fix other dependent stuff */
if (rel->rd_rel->relkind == RELKIND_RELATION)
--- 9568,9592 ----
/* common checks on switching namespaces */
CheckSetNamespace(oldNspOid, nspOid, RelationRelationId, relid);
+ /* Call BEFORE ALTER TABLE triggers */
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = relid;
+ cmd.objectname = stmt->relation->relname;
+ cmd.schemaname = get_namespace_name(oldNspOid);
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
/* OK, modify the pg_class row and pg_depend entry */
classRel = heap_open(RelationRelationId, RowExclusiveLock);
AlterRelationNamespaceInternal(classRel, relid, oldNspOid, nspOid, true);
/* Fix the table's row type too */
! AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid, false, false, NULL);
/* Fix other dependent stuff */
if (rel->rd_rel->relkind == RELKIND_RELATION)
***************
*** 9540,9545 **** AlterTableNamespace(AlterObjectSchemaStmt *stmt)
--- 9601,9613 ----
/* close rel, but keep lock until commit */
relation_close(rel, NoLock);
+
+ /* Call AFTER ALTER TABLE triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.schemaname = get_namespace_name(nspOid);
+ ExecAfterCommandTriggers(&cmd);
+ }
}
/*
***************
*** 9687,9693 **** AlterSeqNamespaces(Relation classRel, Relation rel,
* them to the new namespace, too.
*/
AlterTypeNamespaceInternal(RelationGetForm(seqRel)->reltype,
! newNspOid, false, false);
/* Now we can close it. Keep the lock till end of transaction. */
relation_close(seqRel, NoLock);
--- 9755,9761 ----
* them to the new namespace, too.
*/
AlterTypeNamespaceInternal(RelationGetForm(seqRel)->reltype,
! newNspOid, false, false, NULL);
/* Now we can close it. Keep the lock till end of transaction. */
relation_close(seqRel, NoLock);
*** a/src/backend/commands/tablespace.c
--- b/src/backend/commands/tablespace.c
***************
*** 67,72 ****
--- 67,73 ----
#include "postmaster/bgwriter.h"
#include "storage/fd.h"
#include "storage/standby.h"
+ #include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
***************
*** 365,370 **** CreateTableSpace(CreateTableSpaceStmt *stmt)
--- 366,372 ----
/* We keep the lock on pg_tablespace until commit */
heap_close(rel, NoLock);
+
#else /* !HAVE_SYMLINK */
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
***************
*** 518,523 **** DropTableSpace(DropTableSpaceStmt *stmt)
--- 520,526 ----
/* We keep the lock on pg_tablespace until commit */
heap_close(rel, NoLock);
+
#else /* !HAVE_SYMLINK */
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
***************
*** 812,817 **** RenameTableSpace(const char *oldname, const char *newname)
--- 815,821 ----
HeapScanDesc scan;
HeapTuple tup;
HeapTuple newtuple;
+ Oid oid;
Form_pg_tablespace newform;
/* Search pg_tablespace */
***************
*** 829,834 **** RenameTableSpace(const char *oldname, const char *newname)
--- 833,839 ----
errmsg("tablespace \"%s\" does not exist",
oldname)));
+ oid = HeapTupleGetOid(tup);
newtuple = heap_copytuple(tup);
newform = (Form_pg_tablespace) GETSTRUCT(newtuple);
*** a/src/backend/commands/trigger.c
--- b/src/backend/commands/trigger.c
***************
*** 144,149 **** CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
--- 144,150 ----
Oid constrrelid = InvalidOid;
ObjectAddress myself,
referenced;
+ CommandContextData cmd;
rel = heap_openrv(stmt->relation, AccessExclusiveLock);
***************
*** 419,424 **** CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
--- 420,442 ----
}
/*
+ * Call BEFORE CREATE TRIGGER triggers
+ */
+ if (!isInternal)
+ {
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = InvalidOid;
+ cmd.objectname = stmt->trigname;
+ cmd.schemaname = get_namespace_name(RelationGetNamespace(rel));
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+ }
+
+ /*
* If it's a user-entered CREATE CONSTRAINT TRIGGER command, make a
* corresponding pg_constraint entry.
*/
***************
*** 754,759 **** CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
--- 772,783 ----
/* 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;
}
***************
*** 1201,1207 **** RangeVarCallbackForRenameTrigger(const RangeVar *rv, Oid relid, Oid oldrelid,
* update row in catalog
*/
void
! renametrig(RenameStmt *stmt)
{
Relation targetrel;
Relation tgrel;
--- 1225,1231 ----
* update row in catalog
*/
void
! renametrig(RenameStmt *stmt, CommandContext cmd)
{
Relation targetrel;
Relation tgrel;
***************
*** 1268,1273 **** renametrig(RenameStmt *stmt)
--- 1292,1307 ----
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.
*/
***************
*** 1304,1309 **** renametrig(RenameStmt *stmt)
--- 1338,1350 ----
* Close rel, but keep exclusive lock!
*/
relation_close(targetrel, NoLock);
+
+ /* Call AFTER ALTER TRIGGER triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectname = stmt->newname;
+ ExecAfterCommandTriggers(cmd);
+ }
}
*** a/src/backend/commands/tsearchcmds.c
--- b/src/backend/commands/tsearchcmds.c
***************
*** 32,37 ****
--- 32,38 ----
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "commands/alter.h"
+ #include "commands/cmdtrigger.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
***************
*** 167,173 **** makeParserDependencies(HeapTuple tuple)
* CREATE TEXT SEARCH PARSER
*/
void
! DefineTSParser(List *names, List *parameters)
{
char *prsname;
ListCell *pl;
--- 168,174 ----
* CREATE TEXT SEARCH PARSER
*/
void
! DefineTSParser(List *names, List *parameters, CommandContext cmd)
{
char *prsname;
ListCell *pl;
***************
*** 258,263 **** DefineTSParser(List *names, List *parameters)
--- 259,276 ----
errmsg("text search parser lextypes method is required")));
/*
+ * Call BEFORE CREATE TEXT SEARCH PARSER triggers
+ */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = InvalidOid;
+ cmd->objectname = NameStr(pname);
+ cmd->schemaname = get_namespace_name(namespaceoid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
+ /*
* Looks good, insert
*/
prsRel = heap_open(TSParserRelationId, RowExclusiveLock);
***************
*** 276,281 **** DefineTSParser(List *names, List *parameters)
--- 289,301 ----
heap_freetuple(tup);
heap_close(prsRel, RowExclusiveLock);
+
+ /* Call AFTER CREATE TEXT SEARCH PARSER triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectId = prsOid;
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
***************
*** 305,311 **** RemoveTSParserById(Oid prsId)
* ALTER TEXT SEARCH PARSER RENAME
*/
void
! RenameTSParser(List *oldname, const char *newname)
{
HeapTuple tup;
Relation rel;
--- 325,331 ----
* ALTER TEXT SEARCH PARSER RENAME
*/
void
! RenameTSParser(List *oldname, const char *newname, CommandContext cmd)
{
HeapTuple tup;
Relation rel;
***************
*** 336,354 **** RenameTSParser(List *oldname, const char *newname)
errmsg("text search parser \"%s\" already exists",
newname)));
namestrcpy(&(((Form_pg_ts_parser) GETSTRUCT(tup))->prsname), newname);
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
heap_close(rel, NoLock);
heap_freetuple(tup);
}
/*
* ALTER TEXT SEARCH PARSER any_name SET SCHEMA name
*/
void
! AlterTSParserNamespace(List *name, const char *newschema)
{
Oid prsId,
nspOid;
--- 356,391 ----
errmsg("text search parser \"%s\" already exists",
newname)));
+ /* Call BEFORE ALTER TEXT SEARCH PARSER triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = prsId;
+ cmd->objectname = NameStr(((Form_pg_ts_parser) GETSTRUCT(tup))->prsname);
+ cmd->schemaname = get_namespace_name(namespaceOid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
namestrcpy(&(((Form_pg_ts_parser) GETSTRUCT(tup))->prsname), newname);
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
heap_close(rel, NoLock);
heap_freetuple(tup);
+
+ /* Call AFTER ALTER TEXT SEARCH PARSER triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectname = (char *)newname;
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
* ALTER TEXT SEARCH PARSER any_name SET SCHEMA name
*/
void
! AlterTSParserNamespace(List *name, const char *newschema, CommandContext cmd)
{
Oid prsId,
nspOid;
***************
*** 365,371 **** AlterTSParserNamespace(List *name, const char *newschema)
prsId, nspOid,
Anum_pg_ts_parser_prsname,
Anum_pg_ts_parser_prsnamespace,
! -1, -1);
heap_close(rel, RowExclusiveLock);
}
--- 402,408 ----
prsId, nspOid,
Anum_pg_ts_parser_prsname,
Anum_pg_ts_parser_prsnamespace,
! -1, -1, cmd);
heap_close(rel, RowExclusiveLock);
}
***************
*** 383,389 **** AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid)
prsId, newNspOid,
Anum_pg_ts_parser_prsname,
Anum_pg_ts_parser_prsnamespace,
! -1, -1);
heap_close(rel, RowExclusiveLock);
--- 420,426 ----
prsId, newNspOid,
Anum_pg_ts_parser_prsname,
Anum_pg_ts_parser_prsnamespace,
! -1, -1, NULL);
heap_close(rel, RowExclusiveLock);
***************
*** 484,490 **** verify_dictoptions(Oid tmplId, List *dictoptions)
* CREATE TEXT SEARCH DICTIONARY
*/
void
! DefineTSDictionary(List *names, List *parameters)
{
ListCell *pl;
Relation dictRel;
--- 521,527 ----
* CREATE TEXT SEARCH DICTIONARY
*/
void
! DefineTSDictionary(List *names, List *parameters, CommandContext cmd)
{
ListCell *pl;
Relation dictRel;
***************
*** 537,542 **** DefineTSDictionary(List *names, List *parameters)
--- 574,591 ----
verify_dictoptions(templId, dictoptions);
/*
+ * Call BEFORE CREATE TEXT SEARCH DICTIONARY triggers
+ */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = InvalidOid;
+ cmd->objectname = NameStr(dname);
+ cmd->schemaname = get_namespace_name(namespaceoid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
+ /*
* Looks good, insert
*/
memset(values, 0, sizeof(values));
***************
*** 570,582 **** DefineTSDictionary(List *names, List *parameters)
heap_freetuple(tup);
heap_close(dictRel, RowExclusiveLock);
}
/*
* ALTER TEXT SEARCH DICTIONARY RENAME
*/
void
! RenameTSDictionary(List *oldname, const char *newname)
{
HeapTuple tup;
Relation rel;
--- 619,638 ----
heap_freetuple(tup);
heap_close(dictRel, RowExclusiveLock);
+
+ /* Call AFTER CREATE TEXT SEARCH DICTIONARY triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectId = dictOid;
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
* ALTER TEXT SEARCH DICTIONARY RENAME
*/
void
! RenameTSDictionary(List *oldname, const char *newname, CommandContext cmd)
{
HeapTuple tup;
Relation rel;
***************
*** 615,633 **** RenameTSDictionary(List *oldname, const char *newname)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
namestrcpy(&(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname), newname);
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
heap_close(rel, NoLock);
heap_freetuple(tup);
}
/*
* ALTER TEXT SEARCH DICTIONARY any_name SET SCHEMA name
*/
void
! AlterTSDictionaryNamespace(List *name, const char *newschema)
{
Oid dictId,
nspOid;
--- 671,706 ----
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
+ /* Call BEFORE ALTER TEXT SEARCH DICTIONARY triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = dictId;
+ cmd->objectname = NameStr(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname);
+ cmd->schemaname = get_namespace_name(namespaceOid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
namestrcpy(&(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname), newname);
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
heap_close(rel, NoLock);
heap_freetuple(tup);
+
+ /* Call AFTER ALTER TEXT SEARCH DICTIONARY triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectname = (char *)newname;
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
* ALTER TEXT SEARCH DICTIONARY any_name SET SCHEMA name
*/
void
! AlterTSDictionaryNamespace(List *name, const char *newschema, CommandContext cmd)
{
Oid dictId,
nspOid;
***************
*** 645,651 **** AlterTSDictionaryNamespace(List *name, const char *newschema)
Anum_pg_ts_dict_dictname,
Anum_pg_ts_dict_dictnamespace,
Anum_pg_ts_dict_dictowner,
! ACL_KIND_TSDICTIONARY);
heap_close(rel, RowExclusiveLock);
}
--- 718,724 ----
Anum_pg_ts_dict_dictname,
Anum_pg_ts_dict_dictnamespace,
Anum_pg_ts_dict_dictowner,
! ACL_KIND_TSDICTIONARY, cmd);
heap_close(rel, RowExclusiveLock);
}
***************
*** 664,670 **** AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid)
Anum_pg_ts_dict_dictname,
Anum_pg_ts_dict_dictnamespace,
Anum_pg_ts_dict_dictowner,
! ACL_KIND_TSDICTIONARY);
heap_close(rel, RowExclusiveLock);
--- 737,743 ----
Anum_pg_ts_dict_dictname,
Anum_pg_ts_dict_dictnamespace,
Anum_pg_ts_dict_dictowner,
! ACL_KIND_TSDICTIONARY, NULL);
heap_close(rel, RowExclusiveLock);
***************
*** 812,818 **** AlterTSDictionary(AlterTSDictionaryStmt *stmt)
* ALTER TEXT SEARCH DICTIONARY OWNER
*/
void
! AlterTSDictionaryOwner(List *name, Oid newOwnerId)
{
HeapTuple tup;
Relation rel;
--- 885,891 ----
* ALTER TEXT SEARCH DICTIONARY OWNER
*/
void
! AlterTSDictionaryOwner(List *name, Oid newOwnerId, CommandContext cmd)
{
HeapTuple tup;
Relation rel;
***************
*** 854,859 **** AlterTSDictionaryOwner(List *name, Oid newOwnerId)
--- 927,942 ----
get_namespace_name(namespaceOid));
}
+ /* Call BEFORE ALTER TEXT SEARCH DICTIONARY triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = dictId;
+ cmd->objectname = NameStr(form->dictname);
+ cmd->schemaname = get_namespace_name(namespaceOid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
form->dictowner = newOwnerId;
simple_heap_update(rel, &tup->t_self, tup);
***************
*** 862,867 **** AlterTSDictionaryOwner(List *name, Oid newOwnerId)
--- 945,954 ----
/* Update owner dependency reference */
changeDependencyOnOwner(TSDictionaryRelationId, HeapTupleGetOid(tup),
newOwnerId);
+
+ /* Call AFTER ALTER TEXT SEARCH DICTIONARY triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ ExecAfterCommandTriggers(cmd);
}
heap_close(rel, NoLock);
***************
*** 956,962 **** makeTSTemplateDependencies(HeapTuple tuple)
* CREATE TEXT SEARCH TEMPLATE
*/
void
! DefineTSTemplate(List *names, List *parameters)
{
ListCell *pl;
Relation tmplRel;
--- 1043,1049 ----
* CREATE TEXT SEARCH TEMPLATE
*/
void
! DefineTSTemplate(List *names, List *parameters, CommandContext cmd)
{
ListCell *pl;
Relation tmplRel;
***************
*** 1022,1027 **** DefineTSTemplate(List *names, List *parameters)
--- 1109,1126 ----
errmsg("text search template lexize method is required")));
/*
+ * Call BEFORE CREATE TEXT SEARCH TEMPLATE triggers
+ */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = InvalidOid;
+ cmd->objectname = NameStr(dname);
+ cmd->schemaname = get_namespace_name(namespaceoid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
+ /*
* Looks good, insert
*/
***************
*** 1041,1053 **** DefineTSTemplate(List *names, List *parameters)
heap_freetuple(tup);
heap_close(tmplRel, RowExclusiveLock);
}
/*
* ALTER TEXT SEARCH TEMPLATE RENAME
*/
void
! RenameTSTemplate(List *oldname, const char *newname)
{
HeapTuple tup;
Relation rel;
--- 1140,1159 ----
heap_freetuple(tup);
heap_close(tmplRel, RowExclusiveLock);
+
+ /* Call AFTER CREATE TEXT SEARCH TEMPLATE triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectId = dictOid;
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
* ALTER TEXT SEARCH TEMPLATE RENAME
*/
void
! RenameTSTemplate(List *oldname, const char *newname, CommandContext cmd)
{
HeapTuple tup;
Relation rel;
***************
*** 1079,1097 **** RenameTSTemplate(List *oldname, const char *newname)
errmsg("text search template \"%s\" already exists",
newname)));
namestrcpy(&(((Form_pg_ts_template) GETSTRUCT(tup))->tmplname), newname);
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
heap_close(rel, NoLock);
heap_freetuple(tup);
}
/*
* ALTER TEXT SEARCH TEMPLATE any_name SET SCHEMA name
*/
void
! AlterTSTemplateNamespace(List *name, const char *newschema)
{
Oid tmplId,
nspOid;
--- 1185,1220 ----
errmsg("text search template \"%s\" already exists",
newname)));
+ /* Call BEFORE ALTER TEXT SEARCH TEMPLATE triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = tmplId;
+ cmd->objectname = NameStr(((Form_pg_ts_template) GETSTRUCT(tup))->tmplname);
+ cmd->schemaname = get_namespace_name(namespaceOid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
namestrcpy(&(((Form_pg_ts_template) GETSTRUCT(tup))->tmplname), newname);
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
heap_close(rel, NoLock);
heap_freetuple(tup);
+
+ /* Call AFTER ALTER TEXT SEARCH TEMPLATE triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectname = (char *)newname;
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
* ALTER TEXT SEARCH TEMPLATE any_name SET SCHEMA name
*/
void
! AlterTSTemplateNamespace(List *name, const char *newschema, CommandContext cmd)
{
Oid tmplId,
nspOid;
***************
*** 1108,1114 **** AlterTSTemplateNamespace(List *name, const char *newschema)
tmplId, nspOid,
Anum_pg_ts_template_tmplname,
Anum_pg_ts_template_tmplnamespace,
! -1, -1);
heap_close(rel, RowExclusiveLock);
}
--- 1231,1237 ----
tmplId, nspOid,
Anum_pg_ts_template_tmplname,
Anum_pg_ts_template_tmplnamespace,
! -1, -1, cmd);
heap_close(rel, RowExclusiveLock);
}
***************
*** 1126,1132 **** AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid)
tmplId, newNspOid,
Anum_pg_ts_template_tmplname,
Anum_pg_ts_template_tmplnamespace,
! -1, -1);
heap_close(rel, RowExclusiveLock);
--- 1249,1255 ----
tmplId, newNspOid,
Anum_pg_ts_template_tmplname,
Anum_pg_ts_template_tmplnamespace,
! -1, -1, NULL);
heap_close(rel, RowExclusiveLock);
***************
*** 1274,1280 **** makeConfigurationDependencies(HeapTuple tuple, bool removeOld,
* CREATE TEXT SEARCH CONFIGURATION
*/
void
! DefineTSConfiguration(List *names, List *parameters)
{
Relation cfgRel;
Relation mapRel = NULL;
--- 1397,1403 ----
* CREATE TEXT SEARCH CONFIGURATION
*/
void
! DefineTSConfiguration(List *names, List *parameters, CommandContext cmd)
{
Relation cfgRel;
Relation mapRel = NULL;
***************
*** 1351,1356 **** DefineTSConfiguration(List *names, List *parameters)
--- 1474,1491 ----
errmsg("text search parser is required")));
/*
+ * Call BEFORE CREATE TEXT SEARCH CONFIGURATION triggers
+ */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = InvalidOid;
+ cmd->objectname = NameStr(cname);
+ cmd->schemaname = get_namespace_name(namespaceoid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
+ /*
* Looks good, build tuple and insert
*/
memset(values, 0, sizeof(values));
***************
*** 1426,1438 **** DefineTSConfiguration(List *names, List *parameters)
if (mapRel)
heap_close(mapRel, RowExclusiveLock);
heap_close(cfgRel, RowExclusiveLock);
}
/*
* ALTER TEXT SEARCH CONFIGURATION RENAME
*/
void
! RenameTSConfiguration(List *oldname, const char *newname)
{
HeapTuple tup;
Relation rel;
--- 1561,1580 ----
if (mapRel)
heap_close(mapRel, RowExclusiveLock);
heap_close(cfgRel, RowExclusiveLock);
+
+ /* Call AFTER CREATE TEXT SEARCH PARSER triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectId = cfgOid;
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
* ALTER TEXT SEARCH CONFIGURATION RENAME
*/
void
! RenameTSConfiguration(List *oldname, const char *newname, CommandContext cmd)
{
HeapTuple tup;
Relation rel;
***************
*** 1470,1488 **** RenameTSConfiguration(List *oldname, const char *newname)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
namestrcpy(&(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname), newname);
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
heap_close(rel, NoLock);
heap_freetuple(tup);
}
/*
* ALTER TEXT SEARCH CONFIGURATION any_name SET SCHEMA name
*/
void
! AlterTSConfigurationNamespace(List *name, const char *newschema)
{
Oid cfgId,
nspOid;
--- 1612,1647 ----
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
+ /* Call BEFORE ALTER TEXT SEARCH CONFIGURATION triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = cfgId;
+ cmd->objectname = NameStr(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname);
+ cmd->schemaname = get_namespace_name(namespaceOid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
namestrcpy(&(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname), newname);
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
heap_close(rel, NoLock);
heap_freetuple(tup);
+
+ /* Call AFTER ALTER TEXT SEARCH CONFIGURATION triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectname = (char *)newname;
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
* ALTER TEXT SEARCH CONFIGURATION any_name SET SCHEMA name
*/
void
! AlterTSConfigurationNamespace(List *name, const char *newschema, CommandContext cmd)
{
Oid cfgId,
nspOid;
***************
*** 1500,1506 **** AlterTSConfigurationNamespace(List *name, const char *newschema)
Anum_pg_ts_config_cfgname,
Anum_pg_ts_config_cfgnamespace,
Anum_pg_ts_config_cfgowner,
! ACL_KIND_TSCONFIGURATION);
heap_close(rel, RowExclusiveLock);
}
--- 1659,1665 ----
Anum_pg_ts_config_cfgname,
Anum_pg_ts_config_cfgnamespace,
Anum_pg_ts_config_cfgowner,
! ACL_KIND_TSCONFIGURATION, cmd);
heap_close(rel, RowExclusiveLock);
}
***************
*** 1519,1525 **** AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid)
Anum_pg_ts_config_cfgname,
Anum_pg_ts_config_cfgnamespace,
Anum_pg_ts_config_cfgowner,
! ACL_KIND_TSCONFIGURATION);
heap_close(rel, RowExclusiveLock);
--- 1678,1684 ----
Anum_pg_ts_config_cfgname,
Anum_pg_ts_config_cfgnamespace,
Anum_pg_ts_config_cfgowner,
! ACL_KIND_TSCONFIGURATION, NULL);
heap_close(rel, RowExclusiveLock);
***************
*** 1578,1584 **** RemoveTSConfigurationById(Oid cfgId)
* ALTER TEXT SEARCH CONFIGURATION OWNER
*/
void
! AlterTSConfigurationOwner(List *name, Oid newOwnerId)
{
HeapTuple tup;
Relation rel;
--- 1737,1743 ----
* ALTER TEXT SEARCH CONFIGURATION OWNER
*/
void
! AlterTSConfigurationOwner(List *name, Oid newOwnerId, CommandContext cmd)
{
HeapTuple tup;
Relation rel;
***************
*** 1620,1625 **** AlterTSConfigurationOwner(List *name, Oid newOwnerId)
--- 1779,1794 ----
get_namespace_name(namespaceOid));
}
+ /* Call BEFORE ALTER TEXT SEARCH CONFIGURATION triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = cfgId;
+ cmd->objectname = NameStr(form->cfgname);
+ cmd->schemaname = get_namespace_name(namespaceOid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
form->cfgowner = newOwnerId;
simple_heap_update(rel, &tup->t_self, tup);
***************
*** 1628,1635 **** AlterTSConfigurationOwner(List *name, Oid newOwnerId)
/* Update owner dependency reference */
changeDependencyOnOwner(TSConfigRelationId, HeapTupleGetOid(tup),
newOwnerId);
- }
heap_close(rel, NoLock);
heap_freetuple(tup);
}
--- 1797,1807 ----
/* 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);
}
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
***************
*** 49,54 ****
--- 49,55 ----
#include "catalog/pg_range.h"
#include "catalog/pg_type.h"
#include "catalog/pg_type_fn.h"
+ #include "commands/cmdtrigger.h"
#include "commands/defrem.h"
#include "commands/tablecmds.h"
#include "commands/typecmds.h"
***************
*** 62,67 ****
--- 63,69 ----
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "parser/parse_type.h"
+ #include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
***************
*** 111,117 **** static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
* Registers a new base type.
*/
void
! DefineType(List *names, List *parameters)
{
char *typeName;
Oid typeNamespace;
--- 113,119 ----
* Registers a new base type.
*/
void
! DefineType(List *names, List *parameters, CommandContext cmd)
{
char *typeName;
Oid typeNamespace;
***************
*** 542,547 **** DefineType(List *names, List *parameters)
--- 544,560 ----
NameListToString(analyzeName));
#endif
+ /*
+ * Call BEFORE CREATE TYPE triggers
+ */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = InvalidOid;
+ cmd->objectname = (char *)typeName;
+ cmd->schemaname = get_namespace_name(typeNamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
array_oid = AssignTypeArrayOid();
/*
***************
*** 625,630 **** DefineType(List *names, List *parameters)
--- 638,650 ----
collation); /* type's collation */
pfree(array_type);
+
+ /* Call AFTER CREATE AGGREGATE triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectId = typoid;
+ ExecAfterCommandTriggers(cmd);
+ }
}
/*
***************
*** 706,711 **** DefineDomain(CreateDomainStmt *stmt)
--- 726,732 ----
Form_pg_type baseType;
int32 basetypeMod;
Oid baseColl;
+ CommandContextData cmd;
/* Convert list of names to a name and namespace */
domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
***************
*** 969,974 **** DefineDomain(CreateDomainStmt *stmt)
--- 990,1010 ----
}
}
+
+ /*
+ * Call BEFORE CREATE DOMAIN triggers
+ */
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = InvalidOid;
+ cmd.objectname = (char *)domainName;
+ cmd.schemaname = get_namespace_name(domainNamespace);
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
/*
* Have TypeCreate do all the real work.
*/
***************
*** 1036,1041 **** DefineDomain(CreateDomainStmt *stmt)
--- 1072,1084 ----
* Now we can clean up.
*/
ReleaseSysCache(typeTup);
+
+ /* Call AFTER CREATE DOMAIN triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = domainoid;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
***************
*** 1053,1058 **** DefineEnum(CreateEnumStmt *stmt)
--- 1096,1102 ----
AclResult aclresult;
Oid old_type_oid;
Oid enumArrayOid;
+ CommandContextData cmd;
/* Convert list of names to a name and namespace */
enumNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
***************
*** 1079,1084 **** DefineEnum(CreateEnumStmt *stmt)
--- 1123,1142 ----
errmsg("type \"%s\" already exists", enumName)));
}
+ /*
+ * Call BEFORE CREATE (enum) TYPE triggers
+ */
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = InvalidOid;
+ cmd.objectname = enumName;
+ cmd.schemaname = get_namespace_name(enumNamespace);
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
enumArrayOid = AssignTypeArrayOid();
/* Create the pg_type entry */
***************
*** 1156,1161 **** DefineEnum(CreateEnumStmt *stmt)
--- 1214,1226 ----
InvalidOid); /* type's collation */
pfree(enumArrayName);
+
+ /* Call AFTER CREATE (enum) TYPE triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = enumTypeOid;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
/*
***************
*** 1240,1245 **** DefineRange(CreateRangeStmt *stmt)
--- 1305,1311 ----
char alignment;
AclResult aclresult;
ListCell *lc;
+ CommandContextData cmd;
/* Convert list of names to a name and namespace */
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
***************
*** 1387,1392 **** DefineRange(CreateRangeStmt *stmt)
--- 1453,1472 ----
/* alignment must be 'i' or 'd' for ranges */
alignment = (subtypalign == 'd') ? 'd' : 'i';
+ /*
+ * Call BEFORE CREATE EXTENSION triggers
+ */
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = InvalidOid;
+ cmd.objectname = typeName;
+ cmd.schemaname = get_namespace_name(typeNamespace);
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
/* Allocate OID for array type */
rangeArrayOid = AssignTypeArrayOid();
***************
*** 1469,1474 **** DefineRange(CreateRangeStmt *stmt)
--- 1549,1561 ----
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
+
+ /* Call AFTER CREATE (range) TYPE triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = typoid;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
/*
***************
*** 1980,1986 **** AssignTypeArrayOid(void)
*-------------------------------------------------------------------
*/
Oid
! DefineCompositeType(const RangeVar *typevar, List *coldeflist)
{
CreateStmt *createStmt = makeNode(CreateStmt);
Oid old_type_oid;
--- 2067,2074 ----
*-------------------------------------------------------------------
*/
Oid
! DefineCompositeType(const RangeVar *typevar, List *coldeflist,
! CommandContext cmd)
{
CreateStmt *createStmt = makeNode(CreateStmt);
Oid old_type_oid;
***************
*** 2022,2031 **** DefineCompositeType(const RangeVar *typevar, List *coldeflist)
--- 2110,2138 ----
}
/*
+ * Call BEFORE CREATE (composite) TYPE triggers
+ */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = InvalidOid;
+ cmd->objectname = createStmt->relation->relname;
+ cmd->schemaname = get_namespace_name(typeNamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
+ /*
* Finally create the relation. This also creates the type.
*/
relid = DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid);
Assert(relid != InvalidOid);
+
+ /* Call AFTER CREATE (composite) TYPE triggers */
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->objectId = relid;
+ ExecAfterCommandTriggers(cmd);
+ }
return relid;
}
***************
*** 3091,3097 **** GetDomainConstraints(Oid typeOid)
* Execute ALTER TYPE RENAME
*/
void
! RenameType(RenameStmt *stmt)
{
List *names = stmt->object;
const char *newTypeName = stmt->newname;
--- 3198,3204 ----
* 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);
--- 3260,3269 ----
* 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;
--- 3273,3280 ----
* 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)
--- 3360,3375 ----
get_namespace_name(typTup->typnamespace));
}
+ /* Call BEFORE ALTER TYPE triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = typeOid;
+ cmd->objectname = (char *)typename;
+ cmd->schemaname = get_namespace_name(typTup->typnamespace);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/*
* If it's a composite type, invoke ATExecChangeOwner so that we fix
* up the pg_class entry properly. That will call back to
***************
*** 3279,3284 **** AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype)
--- 3397,3406 ----
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;
--- 3458,3465 ----
* 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;
--- 3479,3489 ----
/* 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);
}
/*
--- 3503,3509 ----
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;
--- 3524,3531 ----
Oid
AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
bool isImplicitArray,
! bool errorOnTableType,
! CommandContext cmd)
{
Relation rel;
HeapTuple tup;
***************
*** 3447,3452 **** AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
--- 3571,3586 ----
format_type_be(typeOid)),
errhint("Use ALTER TABLE instead.")));
+ /* Call BEFORE ALTER TYPE triggers */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = typeOid;
+ cmd->objectname = NameStr(typform->typname);
+ cmd->schemaname = get_namespace_name(oldNspOid);
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
/* OK, modify the pg_type row */
/* tup is a copy, so we can scribble directly on it */
***************
*** 3504,3510 **** AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
/* Recursively alter the associated array type, if any */
if (OidIsValid(arrayOid))
! AlterTypeNamespaceInternal(arrayOid, nspOid, true, true);
return oldNspOid;
}
--- 3638,3649 ----
/* Recursively alter the associated array type, if any */
if (OidIsValid(arrayOid))
! AlterTypeNamespaceInternal(arrayOid, nspOid, true, true, NULL);
+ if (CommandFiresAfterTriggers(cmd))
+ {
+ cmd->schemaname = get_namespace_name(nspOid);
+ ExecAfterCommandTriggers(cmd);
+ }
return oldNspOid;
}
*** a/src/backend/commands/vacuum.c
--- b/src/backend/commands/vacuum.c
***************
*** 30,35 ****
--- 30,36 ----
#include "catalog/namespace.h"
#include "catalog/pg_database.h"
#include "catalog/pg_namespace.h"
+ #include "commands/cmdtrigger.h"
#include "commands/cluster.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
***************
*** 122,127 **** vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
--- 123,144 ----
else
in_outer_xact = IsInTransactionChain(isTopLevel);
+ /* CAll BEFORE VACUUM command triggers */
+ if (!IsAutoVacuumWorkerProcess() && vacstmt->relation != NULL)
+ {
+ CommandContextData cmd;
+ InitCommandContext(&cmd, (Node *)vacstmt, false);
+
+ if (CommandFiresTriggers(&cmd))
+ {
+ cmd.objectId = relid;
+ cmd.objectname = vacstmt->relation->relname;
+ cmd.schemaname = vacstmt->relation->schemaname;
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+ }
+
/*
* Send info about dead objects to the statistics collector, unless we are
* in autovacuum --- autovacuum.c does this for itself.
*** a/src/backend/commands/view.c
--- b/src/backend/commands/view.c
***************
*** 28,33 ****
--- 28,34 ----
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteManip.h"
#include "rewrite/rewriteSupport.h"
+ #include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
***************
*** 99,105 **** isViewOnTempTable_walker(Node *node, void *context)
*/
static Oid
DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
! List *options)
{
Oid viewOid;
LOCKMODE lockmode;
--- 100,106 ----
*/
static Oid
DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
! List *options, CommandContext cmd)
{
Oid viewOid;
LOCKMODE lockmode;
***************
*** 168,173 **** DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
--- 169,186 ----
lockmode = replace ? AccessExclusiveLock : NoLock;
(void) RangeVarGetAndCheckCreationNamespace(relation, lockmode, &viewOid);
+ /*
+ * Call BEFORE CREATE VIEW triggers
+ */
+ if (CommandFiresTriggers(cmd))
+ {
+ cmd->objectId = InvalidOid;
+ cmd->objectname = relation->relname;
+ cmd->schemaname = relation->schemaname;
+
+ ExecBeforeCommandTriggers(cmd);
+ }
+
if (OidIsValid(viewOid) && replace)
{
Relation rel;
***************
*** 426,431 **** DefineView(ViewStmt *stmt, const char *queryString)
--- 439,445 ----
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
--- 525,545 ----
}
/*
+ * Prepare BEFORE CREATE VIEW triggers
+ */
+ InitCommandContext(&cmd, (Node *)stmt, false);
+
+ /*
* Create the view relation
*
* NOTE: if it already exists and replace is false, the xact will be
* aborted.
*/
viewOid = DefineVirtualRelation(view, viewParse->targetList,
! stmt->replace, stmt->options, &cmd);
!
! if (!OidIsValid(viewOid))
! return;
/*
* The relation we have just created is not visible to any other commands
***************
*** 536,539 **** DefineView(ViewStmt *stmt, const char *queryString)
--- 558,568 ----
* Now create the rules associated with the view.
*/
DefineViewRules(viewOid, viewParse, stmt->replace);
+
+ /* Call AFTER CREATE VIEW triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = viewOid;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 3457,3462 **** _copyCreateTrigStmt(const CreateTrigStmt *from)
--- 3457,3514 ----
return newnode;
}
+ static DropPropertyStmt *
+ _copyDropPropertyStmt(const DropPropertyStmt *from)
+ {
+ DropPropertyStmt *newnode = makeNode(DropPropertyStmt);
+
+ COPY_NODE_FIELD(relation);
+ COPY_STRING_FIELD(property);
+ COPY_SCALAR_FIELD(removeType);
+ COPY_SCALAR_FIELD(behavior);
+ COPY_SCALAR_FIELD(missing_ok);
+
+ return newnode;
+ }
+
+ static CreateCmdTrigStmt *
+ _copyCreateCmdTrigStmt(const CreateCmdTrigStmt *from)
+ {
+ CreateCmdTrigStmt *newnode = makeNode(CreateCmdTrigStmt);
+
+ COPY_NODE_FIELD(command);
+ COPY_STRING_FIELD(trigname);
+ COPY_SCALAR_FIELD(timing);
+ COPY_NODE_FIELD(funcname);
+
+ return newnode;
+ }
+
+ static DropCmdTrigStmt *
+ _copyDropCmdTrigStmt(const DropCmdTrigStmt *from)
+ {
+ DropCmdTrigStmt *newnode = makeNode(DropCmdTrigStmt);
+
+ COPY_NODE_FIELD(command);
+ COPY_STRING_FIELD(trigname);
+ COPY_SCALAR_FIELD(behavior);
+ COPY_SCALAR_FIELD(missing_ok);
+
+ return newnode;
+ }
+
+ static AlterCmdTrigStmt *
+ _copyAlterCmdTrigStmt(const AlterCmdTrigStmt *from)
+ {
+ AlterCmdTrigStmt *newnode = makeNode(AlterCmdTrigStmt);
+
+ COPY_STRING_FIELD(command);
+ COPY_STRING_FIELD(trigname);
+ COPY_STRING_FIELD(tgenabled);
+
+ return newnode;
+ }
+
static CreatePLangStmt *
_copyCreatePLangStmt(const CreatePLangStmt *from)
{
***************
*** 3686,3692 **** _copyAlterTSConfigurationStmt(const AlterTSConfigurationStmt *from)
/*
* Perform a deep copy of the specified list, using copyObject(). The
* list MUST be of type T_List; T_IntList and T_OidList nodes don't
! * need deep copies, so they should be copied via list_copy()
*/
#define COPY_NODE_CELL(new, old) \
(new) = (ListCell *) palloc(sizeof(ListCell)); \
--- 3738,3744 ----
/*
* Perform a deep copy of the specified list, using copyObject(). The
* list MUST be of type T_List; T_IntList and T_OidList nodes don't
! * need deep copies, so they should be copied via list_copy(const )
*/
#define COPY_NODE_CELL(new, old) \
(new) = (ListCell *) palloc(sizeof(ListCell)); \
***************
*** 4309,4314 **** copyObject(const void *from)
--- 4361,4378 ----
case T_CreateTrigStmt:
retval = _copyCreateTrigStmt(from);
break;
+ case T_DropPropertyStmt:
+ retval = _copyDropPropertyStmt(from);
+ break;
+ case T_CreateCmdTrigStmt:
+ retval = _copyCreateCmdTrigStmt(from);
+ break;
+ case T_DropCmdTrigStmt:
+ retval = _copyDropCmdTrigStmt(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,1827 ----
}
static bool
+ _equalDropPropertyStmt(const DropPropertyStmt *a, const DropPropertyStmt *b)
+ {
+ COMPARE_NODE_FIELD(relation);
+ COMPARE_STRING_FIELD(property);
+ COMPARE_SCALAR_FIELD(removeType);
+ COMPARE_SCALAR_FIELD(behavior);
+ COMPARE_SCALAR_FIELD(missing_ok);
+
+ return true;
+ }
+
+ static bool
+ _equalCreateCmdTrigStmt(const CreateCmdTrigStmt *a, const CreateCmdTrigStmt *b)
+ {
+ COMPARE_NODE_FIELD(command);
+ COMPARE_STRING_FIELD(trigname);
+ COMPARE_SCALAR_FIELD(timing);
+ COMPARE_NODE_FIELD(funcname);
+
+ return true;
+ }
+
+ static bool
+ _equalDropCmdTrigStmt(const DropCmdTrigStmt *a, const DropCmdTrigStmt *b)
+ {
+ COMPARE_NODE_FIELD(command);
+ COMPARE_STRING_FIELD(trigname);
+ COMPARE_SCALAR_FIELD(behavior);
+ COMPARE_SCALAR_FIELD(missing_ok);
+
+ return true;
+ }
+
+ static bool
+ _equalAlterCmdTrigStmt(const AlterCmdTrigStmt *a, const AlterCmdTrigStmt *b)
+ {
+ COMPARE_STRING_FIELD(command);
+ COMPARE_STRING_FIELD(trigname);
+ COMPARE_STRING_FIELD(tgenabled);
+
+ return true;
+ }
+
+ static bool
_equalCreatePLangStmt(const CreatePLangStmt *a, const CreatePLangStmt *b)
{
COMPARE_SCALAR_FIELD(replace);
***************
*** 2852,2857 **** equal(const void *a, const void *b)
--- 2896,2913 ----
case T_CreateTrigStmt:
retval = _equalCreateTrigStmt(a, b);
break;
+ case T_DropPropertyStmt:
+ retval = _equalDropPropertyStmt(a, b);
+ break;
+ case T_CreateCmdTrigStmt:
+ retval = _equalCreateCmdTrigStmt(a, b);
+ break;
+ case T_DropCmdTrigStmt:
+ retval = _equalDropCmdTrigStmt(a, b);
+ break;
+ case T_AlterCmdTrigStmt:
+ retval = _equalAlterCmdTrigStmt(a, b);
+ break;
case T_CreatePLangStmt:
retval = _equalCreatePLangStmt(a, b);
break;
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
***************
*** 1934,1939 **** _outPlannerParamItem(StringInfo str, const PlannerParamItem *node)
--- 1934,2070 ----
*****************************************************************************/
static void
+ _outInsertStmt(StringInfo str, const InsertStmt *node)
+ {
+ WRITE_NODE_TYPE("INSERTSTMT");
+
+ WRITE_NODE_FIELD(relation);
+ WRITE_NODE_FIELD(cols);
+ WRITE_NODE_FIELD(selectStmt);
+ WRITE_NODE_FIELD(returningList);
+ WRITE_NODE_FIELD(withClause);
+ }
+
+ static void
+ _outDeleteStmt(StringInfo str, const DeleteStmt *node)
+ {
+ WRITE_NODE_TYPE("DELETESTMT");
+
+ WRITE_NODE_FIELD(relation);
+ WRITE_NODE_FIELD(usingClause);
+ WRITE_NODE_FIELD(whereClause);
+ WRITE_NODE_FIELD(returningList);
+ WRITE_NODE_FIELD(withClause);
+ }
+
+ static void
+ _outUpdateStmt(StringInfo str, const UpdateStmt *node)
+ {
+ WRITE_NODE_TYPE("UPDATESTMT");
+
+ WRITE_NODE_FIELD(relation);
+ WRITE_NODE_FIELD(targetList);
+ WRITE_NODE_FIELD(whereClause);
+ WRITE_NODE_FIELD(fromClause);
+ WRITE_NODE_FIELD(returningList);
+ WRITE_NODE_FIELD(withClause);
+ }
+
+ static void
+ _outAlterTableStmt(StringInfo str, const AlterTableStmt *node)
+ {
+ WRITE_NODE_TYPE("ALTERTABLESTMT");
+
+ WRITE_NODE_FIELD(relation);
+ WRITE_NODE_FIELD(cmds);
+ WRITE_CHAR_FIELD(relkind);
+ }
+
+ static void
+ _outAlterTableCmd(StringInfo str, const AlterTableCmd *node)
+ {
+ WRITE_NODE_TYPE("ALTERTABLECMD");
+
+ WRITE_ENUM_FIELD(subtype, AlterTableType);
+ WRITE_STRING_FIELD(name);
+ WRITE_NODE_FIELD(def);
+ WRITE_ENUM_FIELD(behavior, DropBehavior);
+ WRITE_BOOL_FIELD(missing_ok);
+ }
+
+ static void
+ _outAlterDomainStmt(StringInfo str, const AlterDomainStmt *node)
+ {
+ WRITE_NODE_TYPE("ALTERDOMAINSTMT");
+
+ WRITE_CHAR_FIELD(subtype);
+ WRITE_NODE_FIELD(typeName);
+ WRITE_STRING_FIELD(name);
+ WRITE_NODE_FIELD(def);
+ WRITE_ENUM_FIELD(behavior, DropBehavior);
+ }
+
+ static void
+ _outGrantStmt(StringInfo str, const GrantStmt *node)
+ {
+ WRITE_NODE_TYPE("GRANTSTMT");
+
+ WRITE_BOOL_FIELD(is_grant);
+ WRITE_ENUM_FIELD(targtype, GrantTargetType);
+ WRITE_ENUM_FIELD(objtype, GrantObjectType);
+ WRITE_NODE_FIELD(objects);
+ WRITE_NODE_FIELD(privileges);
+ WRITE_NODE_FIELD(grantees);
+ WRITE_BOOL_FIELD(grant_option);
+ WRITE_ENUM_FIELD(behavior, DropBehavior);
+ }
+
+ static void
+ _outGrantRoleStmt(StringInfo str, const GrantRoleStmt *node)
+ {
+ WRITE_NODE_TYPE("GRANTROLESTMT");
+
+ WRITE_NODE_FIELD(granted_roles);
+ WRITE_NODE_FIELD(grantee_roles);
+ WRITE_BOOL_FIELD(is_grant);
+ WRITE_BOOL_FIELD(admin_opt);
+ WRITE_STRING_FIELD(grantor);
+ WRITE_ENUM_FIELD(behavior, DropBehavior);
+ }
+
+ static void
+ _outAlterDefaultPrivilegesStmt(StringInfo str, const AlterDefaultPrivilegesStmt *node)
+ {
+ WRITE_NODE_TYPE("ALTERDEFAULTPRIVILEGESSTMT");
+
+ WRITE_NODE_FIELD(options);
+ WRITE_NODE_FIELD(action);
+ }
+
+ static void
+ _outClusterStmt(StringInfo str, const ClusterStmt *node)
+ {
+ WRITE_NODE_TYPE("CLUSTERSTMT");
+
+ WRITE_NODE_FIELD(relation);
+ WRITE_STRING_FIELD(indexname);
+ WRITE_BOOL_FIELD(verbose);
+ }
+
+ static void
+ _outCopyStmt(StringInfo str, const CopyStmt *node)
+ {
+ WRITE_NODE_TYPE("COPYSTMT");
+
+ WRITE_NODE_FIELD(relation);
+ WRITE_NODE_FIELD(query);
+ WRITE_NODE_FIELD(attlist);
+ WRITE_BOOL_FIELD(is_from);
+ WRITE_STRING_FIELD(filename);
+ WRITE_NODE_FIELD(options);
+ }
+
+ static void
_outCreateStmt(StringInfo str, const CreateStmt *node)
{
WRITE_NODE_TYPE("CREATESTMT");
***************
*** 1950,1955 **** _outCreateStmt(StringInfo str, const CreateStmt *node)
--- 2081,2111 ----
}
static void
+ _outDefineStmt(StringInfo str, const DefineStmt *node)
+ {
+ WRITE_NODE_TYPE("DEFINESTMT");
+
+ WRITE_ENUM_FIELD(kind, ObjectType);
+ WRITE_BOOL_FIELD(oldstyle);
+ WRITE_NODE_FIELD(defnames);
+ WRITE_NODE_FIELD(args);
+ WRITE_NODE_FIELD(definition);
+ }
+
+ static void
+ _outAlterTSConfigurationStmt(StringInfo str, const AlterTSConfigurationStmt *node)
+ {
+ WRITE_NODE_TYPE("ALTERTSCONFIGURATIONSTMT");
+
+ WRITE_NODE_FIELD(cfgname);
+ WRITE_NODE_FIELD(tokentype);
+ WRITE_NODE_FIELD(dicts);
+ WRITE_BOOL_FIELD(override);
+ WRITE_BOOL_FIELD(replace);
+ WRITE_BOOL_FIELD(missing_ok);
+ }
+
+ static void
_outCreateForeignTableStmt(StringInfo str, const CreateForeignTableStmt *node)
{
WRITE_NODE_TYPE("CREATEFOREIGNTABLESTMT");
***************
*** 1961,1966 **** _outCreateForeignTableStmt(StringInfo str, const CreateForeignTableStmt *node)
--- 2117,2144 ----
}
static void
+ _outDropStmt(StringInfo str, const DropStmt *node)
+ {
+ WRITE_NODE_TYPE("DROPSTMT");
+
+ WRITE_NODE_FIELD(objects);
+ WRITE_ENUM_FIELD(removeType,ObjectType);
+ WRITE_ENUM_FIELD(behavior,DropBehavior);
+ WRITE_BOOL_FIELD(missing_ok);
+ }
+
+ static void
+ _outCommentStmt(StringInfo str, const CommentStmt *node)
+ {
+ WRITE_NODE_TYPE("COMMENTSTMT");
+
+ WRITE_ENUM_FIELD(objtype, ObjectType);
+ WRITE_NODE_FIELD(objname);
+ WRITE_NODE_FIELD(objargs);
+ WRITE_STRING_FIELD(comment);
+ }
+
+ static void
_outIndexStmt(StringInfo str, const IndexStmt *node)
{
WRITE_NODE_TYPE("INDEXSTMT");
***************
*** 1984,1989 **** _outIndexStmt(StringInfo str, const IndexStmt *node)
--- 2162,2194 ----
}
static void
+ _outCreateFunctionStmt(StringInfo str, const CreateFunctionStmt *node)
+ {
+ WRITE_NODE_TYPE("CREATEFUNCTIONSTMT");
+
+ WRITE_BOOL_FIELD(replace);
+ WRITE_NODE_FIELD(funcname);
+ WRITE_NODE_FIELD(parameters);
+ WRITE_NODE_FIELD(returnType);
+ WRITE_NODE_FIELD(options);
+ WRITE_NODE_FIELD(withClause);
+ }
+
+ static void
+ _outRuleStmt(StringInfo str, const RuleStmt *node)
+ {
+ WRITE_NODE_TYPE("RULESTMT");
+
+ WRITE_NODE_FIELD(relation);
+ WRITE_STRING_FIELD(rulename);
+ WRITE_NODE_FIELD(whereClause);
+ WRITE_ENUM_FIELD(event, CmdType);
+ WRITE_BOOL_FIELD(instead);
+ WRITE_NODE_FIELD(actions);
+ WRITE_BOOL_FIELD(replace);
+ }
+
+ static void
_outNotifyStmt(StringInfo str, const NotifyStmt *node)
{
WRITE_NODE_TYPE("NOTIFY");
***************
*** 1993,1998 **** _outNotifyStmt(StringInfo str, const NotifyStmt *node)
--- 2198,2292 ----
}
static void
+ _outViewStmt(StringInfo str, const ViewStmt *node)
+ {
+ WRITE_NODE_TYPE("VIEWSTMT");
+
+ WRITE_NODE_FIELD(view);
+ WRITE_NODE_FIELD(aliases);
+ WRITE_NODE_FIELD(query);
+ WRITE_BOOL_FIELD(replace);
+ }
+
+ static void
+ _outCreateDomainStmt(StringInfo str, const CreateDomainStmt *node)
+ {
+ WRITE_NODE_TYPE("CREATECONVERSIONSTMT");
+
+ WRITE_NODE_FIELD(domainname);
+ WRITE_NODE_FIELD(typeName);
+ WRITE_NODE_FIELD(collClause);
+ WRITE_NODE_FIELD(constraints);
+ }
+
+ static void
+ _outCreatedbStmt(StringInfo str, const CreatedbStmt *node)
+ {
+ WRITE_NODE_TYPE("CREATEDBSTMT");
+
+ WRITE_STRING_FIELD(dbname);
+ WRITE_NODE_FIELD(options);
+ }
+
+ static void
+ _outVacuumStmt(StringInfo str, const VacuumStmt *node)
+ {
+ WRITE_NODE_TYPE("VACUUMSTMT");
+
+ WRITE_INT_FIELD(options);
+ WRITE_INT_FIELD(freeze_min_age);
+ WRITE_INT_FIELD(freeze_table_age);
+ WRITE_NODE_FIELD(relation);
+ WRITE_NODE_FIELD(va_cols);
+ }
+
+ static void
+ _outVariableSetStmt(StringInfo str, const VariableSetStmt *node)
+ {
+ WRITE_NODE_TYPE("VARIABLESETSTMT");
+
+ WRITE_ENUM_FIELD(kind, VariableSetKind);
+ WRITE_STRING_FIELD(name);
+ WRITE_NODE_FIELD(args);
+ WRITE_BOOL_FIELD(is_local);
+ }
+
+ static void
+ _outCreatePLangStmt(StringInfo str, const CreatePLangStmt *node)
+ {
+ WRITE_NODE_TYPE("CREATESPLANGSTMT");
+
+ WRITE_BOOL_FIELD(replace);
+ WRITE_STRING_FIELD(plname);
+ WRITE_NODE_FIELD(plhandler);
+ WRITE_NODE_FIELD(plinline);
+ WRITE_NODE_FIELD(plvalidator);
+ WRITE_BOOL_FIELD(pltrusted);
+ }
+
+ static void
+ _outCreateSchemaStmt(StringInfo str, const CreateSchemaStmt *node)
+ {
+ WRITE_NODE_TYPE("CREATESCHEMASTMT");
+
+ WRITE_STRING_FIELD(schemaname);
+ WRITE_STRING_FIELD(authid);
+ WRITE_NODE_FIELD(schemaElts);
+ }
+
+ static void
+ _outCreateConversionStmt(StringInfo str, const CreateConversionStmt *node)
+ {
+ WRITE_NODE_TYPE("CREATECONVERSIONSTMT");
+
+ WRITE_NODE_FIELD(conversion_name);
+ WRITE_STRING_FIELD(for_encoding_name);
+ WRITE_STRING_FIELD(to_encoding_name);
+ WRITE_NODE_FIELD(func_name);
+ WRITE_BOOL_FIELD(def);
+ }
+
+ static void
_outDeclareCursorStmt(StringInfo str, const DeclareCursorStmt *node)
{
WRITE_NODE_TYPE("DECLARECURSOR");
***************
*** 2084,2089 **** _outXmlSerialize(StringInfo str, const XmlSerialize *node)
--- 2378,2393 ----
}
static void
+ _outCreateExtensionStmt(StringInfo str, const CreateExtensionStmt *node)
+ {
+ WRITE_NODE_TYPE("CREATEEXTENSIONSTMT");
+
+ WRITE_STRING_FIELD(extname);
+ WRITE_BOOL_FIELD(if_not_exists);
+ WRITE_NODE_FIELD(options);
+ }
+
+ static void
_outColumnDef(StringInfo str, const ColumnDef *node)
{
WRITE_NODE_TYPE("COLUMNDEF");
***************
*** 2241,2246 **** _outWindowClause(StringInfo str, const WindowClause *node)
--- 2545,2561 ----
}
static void
+ _outFunctionParameter(StringInfo str, const FunctionParameter *node)
+ {
+ WRITE_NODE_TYPE("FUNCTIONPARAMETER");
+
+ WRITE_STRING_FIELD(name);
+ WRITE_NODE_FIELD(argType);
+ WRITE_CHAR_FIELD(mode);
+ WRITE_NODE_FIELD(defexpr);
+ }
+
+ static void
_outRowMarkClause(StringInfo str, const RowMarkClause *node)
{
WRITE_NODE_TYPE("ROWMARKCLAUSE");
***************
*** 2349,2354 **** _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
--- 2664,2679 ----
}
static void
+ _outAlterCmdTrigStmt(StringInfo str, const AlterCmdTrigStmt *node)
+ {
+ WRITE_NODE_TYPE("ALTERCMDTRIGSTMT");
+
+ WRITE_STRING_FIELD(command);
+ WRITE_STRING_FIELD(trigname);
+ WRITE_STRING_FIELD(tgenabled);
+ }
+
+ static void
_outAExpr(StringInfo str, const A_Expr *node)
{
WRITE_NODE_TYPE("AEXPR");
***************
*** 3022,3045 **** _outNode(StringInfo str, const void *obj)
--- 3347,3448 ----
_outPlannerParamItem(str, obj);
break;
+ case T_InsertStmt:
+ _outInsertStmt(str, obj);
+ break;
+ case T_DeleteStmt:
+ _outDeleteStmt(str, obj);
+ break;
+ case T_UpdateStmt:
+ _outUpdateStmt(str, obj);
+ break;
+ case T_AlterTableStmt:
+ _outAlterTableStmt(str, obj);
+ break;
+ case T_AlterTableCmd:
+ _outAlterTableCmd(str, obj);
+ break;
+ case T_AlterDomainStmt:
+ _outAlterDomainStmt(str, obj);
+ break;
+ case T_GrantStmt:
+ _outGrantStmt(str, obj);
+ break;
+ case T_GrantRoleStmt:
+ _outGrantRoleStmt(str, obj);
+ break;
+ case T_AlterDefaultPrivilegesStmt:
+ _outAlterDefaultPrivilegesStmt(str, obj);
+ break;
+ case T_ClusterStmt:
+ _outClusterStmt(str, obj);
+ break;
+ case T_CopyStmt:
+ _outCopyStmt(str, obj);
+ break;
case T_CreateStmt:
_outCreateStmt(str, obj);
break;
+ case T_DefineStmt:
+ _outDefineStmt(str, obj);
+ break;
+ case T_AlterTSConfigurationStmt:
+ _outAlterTSConfigurationStmt(str, obj);
+ break;
case T_CreateForeignTableStmt:
_outCreateForeignTableStmt(str, obj);
break;
+ case T_DropStmt:
+ _outDropStmt(str, obj);
+ break;
+ case T_CommentStmt:
+ _outCommentStmt(str, obj);
+ break;
case T_IndexStmt:
_outIndexStmt(str, obj);
break;
+ case T_CreateFunctionStmt:
+ _outCreateFunctionStmt(str, obj);
+ break;
+ case T_RuleStmt:
+ _outRuleStmt(str, obj);
+ break;
case T_NotifyStmt:
_outNotifyStmt(str, obj);
break;
+ case T_ViewStmt:
+ _outViewStmt(str, obj);
+ break;
+ case T_CreateDomainStmt:
+ _outCreateDomainStmt(str, obj);
+ break;
+ case T_CreatedbStmt:
+ _outCreatedbStmt(str, obj);
+ break;
+ case T_VacuumStmt:
+ _outVacuumStmt(str, obj);
+ break;
+ case T_VariableSetStmt:
+ _outVariableSetStmt(str, obj);
+ break;
+ case T_CreatePLangStmt:
+ _outCreatePLangStmt(str, obj);
+ break;
+ case T_CreateSchemaStmt:
+ _outCreateSchemaStmt(str, obj);
+ break;
+ case T_CreateConversionStmt:
+ _outCreateConversionStmt(str, obj);
+ break;
case T_DeclareCursorStmt:
_outDeclareCursorStmt(str, obj);
break;
case T_SelectStmt:
_outSelectStmt(str, obj);
break;
+ case T_CreateExtensionStmt:
+ _outCreateExtensionStmt(str, obj);
+ break;
case T_ColumnDef:
_outColumnDef(str, obj);
break;
***************
*** 3064,3069 **** _outNode(StringInfo str, const void *obj)
--- 3467,3475 ----
case T_WindowClause:
_outWindowClause(str, obj);
break;
+ case T_FunctionParameter:
+ _outFunctionParameter(str, obj);
+ break;
case T_RowMarkClause:
_outRowMarkClause(str, obj);
break;
***************
*** 3079,3084 **** _outNode(StringInfo str, const void *obj)
--- 3485,3493 ----
case T_RangeTblEntry:
_outRangeTblEntry(str, obj);
break;
+ case T_AlterCmdTrigStmt:
+ _outAlterCmdTrigStmt(str, obj);
+ break;
case T_A_Expr:
_outAExpr(str, obj);
break;
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
***************
*** 255,260 **** _readDeclareCursorStmt(void)
--- 255,463 ----
}
/*
+ * _readCreateStmt
+ */
+ static CreateStmt *
+ _readCreateStmt(void)
+ {
+ READ_LOCALS(CreateStmt);
+
+ READ_NODE_FIELD(relation);
+ READ_NODE_FIELD(tableElts);
+ READ_NODE_FIELD(inhRelations);
+ READ_NODE_FIELD(ofTypename);
+ READ_NODE_FIELD(constraints);
+ READ_NODE_FIELD(options);
+ READ_ENUM_FIELD(oncommit, OnCommitAction);
+ READ_STRING_FIELD(tablespacename);
+ READ_BOOL_FIELD(if_not_exists);
+
+ READ_DONE();
+ }
+
+ /*
+ * _readDropStmt
+ */
+ static DropStmt *
+ _readDropStmt(void)
+ {
+ READ_LOCALS(DropStmt);
+
+ READ_NODE_FIELD(objects);
+ READ_ENUM_FIELD(removeType,ObjectType);
+ READ_ENUM_FIELD(behavior,DropBehavior);
+ READ_BOOL_FIELD(missing_ok);
+
+ READ_DONE();
+ }
+
+ /*
+ * _readCreateExtensionStmt
+ */
+ static CreateExtensionStmt *
+ _readCreateExtensionStmt(void)
+ {
+ READ_LOCALS(CreateExtensionStmt);
+
+ READ_STRING_FIELD(extname);
+ READ_BOOL_FIELD(if_not_exists);
+ READ_NODE_FIELD(options);
+
+ READ_DONE();
+ }
+
+ /*
+ * _readAlterCmdTrigStmt
+ */
+ static AlterCmdTrigStmt *
+ _readAlterCmdTrigStmt(void)
+ {
+ READ_LOCALS(AlterCmdTrigStmt);
+
+ READ_STRING_FIELD(command);
+ READ_STRING_FIELD(trigname);
+ READ_STRING_FIELD(tgenabled);
+
+ READ_DONE();
+ }
+
+ /*
+ * _readTypeName
+ */
+ static TypeName *
+ _readTypeName(void)
+ {
+ READ_LOCALS(TypeName);
+
+ READ_NODE_FIELD(names);
+ READ_OID_FIELD(typeOid);
+ READ_BOOL_FIELD(setof);
+ READ_BOOL_FIELD(pct_type);
+ READ_NODE_FIELD(typmods);
+ READ_INT_FIELD(typemod);
+ READ_NODE_FIELD(arrayBounds);
+ READ_LOCATION_FIELD(location);
+
+ READ_DONE();
+ }
+
+ /*
+ * _readColumnDef
+ */
+ static ColumnDef *
+ _readColumnDef(void)
+ {
+ READ_LOCALS(ColumnDef);
+
+ READ_STRING_FIELD(colname);
+ READ_NODE_FIELD(typeName);
+ READ_INT_FIELD(inhcount);
+ READ_BOOL_FIELD(is_local);
+ READ_BOOL_FIELD(is_not_null);
+ READ_BOOL_FIELD(is_from_type);
+ READ_CHAR_FIELD(storage);
+ READ_NODE_FIELD(raw_default);
+ READ_NODE_FIELD(cooked_default);
+ READ_NODE_FIELD(collClause);
+ READ_OID_FIELD(collOid);
+ READ_NODE_FIELD(constraints);
+ READ_NODE_FIELD(fdwoptions);
+
+ READ_DONE();
+ }
+
+ /*
+ * _readConstraint
+ */
+ static Constraint *
+ _readConstraint(void)
+ {
+ READ_LOCALS(Constraint);
+
+ READ_STRING_FIELD(conname);
+ READ_BOOL_FIELD(deferrable);
+ READ_BOOL_FIELD(initdeferred);
+ READ_LOCATION_FIELD(location);
+
+ /*
+ * READ_ENUM_FIELD(contype,ConstrType);
+ *
+ * The contype is not written out as an enum value, but as a string.
+ * Depending on the value of the string some fields or some other are to be
+ * read in the node string.
+ */
+
+ token = pg_strtok(&length); /* skip :constraint */
+ token = pg_strtok(&length); /* get field value */
+
+ if (strncmp(token, "NULL", 4) == 0)
+ local_node->contype = CONSTR_NULL;
+ else if (strncmp(token, "NOT_NULL", 8) == 0)
+ local_node->contype = CONSTR_NOTNULL;
+ else if (strncmp(token, "DEFAULT", 7) == 0)
+ {
+ local_node->contype = CONSTR_DEFAULT;
+ READ_NODE_FIELD(raw_expr);
+ READ_STRING_FIELD(cooked_expr);
+ }
+ else if (strncmp(token, "CHECK", 7) == 0)
+ {
+ local_node->contype = CONSTR_CHECK;
+ READ_NODE_FIELD(raw_expr);
+ READ_STRING_FIELD(cooked_expr);
+ }
+ else if (strncmp(token, "PRIMARY_KEY", 11) == 0)
+ {
+ local_node->contype = CONSTR_PRIMARY;
+ READ_NODE_FIELD(keys);
+ READ_NODE_FIELD(options);
+ READ_STRING_FIELD(indexname);
+ READ_STRING_FIELD(indexspace);
+ }
+ else if (strncmp(token, "UNIQUE", 6) == 0)
+ {
+ local_node->contype = CONSTR_UNIQUE;
+ READ_NODE_FIELD(keys);
+ READ_NODE_FIELD(options);
+ READ_STRING_FIELD(indexname);
+ READ_STRING_FIELD(indexspace);
+ }
+ else if (strncmp(token, "EXCLUSION", 9) == 0)
+ {
+ local_node->contype = CONSTR_EXCLUSION;
+ READ_NODE_FIELD(exclusions);
+ READ_NODE_FIELD(keys);
+ READ_NODE_FIELD(options);
+ READ_STRING_FIELD(indexname);
+ READ_STRING_FIELD(indexspace);
+ }
+ else if (strncmp(token, "FOREIGN_KEY", 11) == 0)
+ {
+ local_node->contype = CONSTR_FOREIGN;
+ READ_NODE_FIELD(pktable);
+ READ_NODE_FIELD(fk_attrs);
+ READ_NODE_FIELD(pk_attrs);
+ READ_CHAR_FIELD(fk_matchtype);
+ READ_CHAR_FIELD(fk_upd_action);
+ READ_CHAR_FIELD(fk_del_action);
+ READ_BOOL_FIELD(skip_validation);
+ READ_BOOL_FIELD(initially_valid);
+ }
+ else if (strncmp(token, "ATTR_DEFERRABLE", 15) == 0)
+ local_node->contype = CONSTR_ATTR_DEFERRABLE;
+ else if (strncmp(token, "ATTR_NOT_DEFERRABLE", 19) == 0)
+ local_node->contype = CONSTR_ATTR_NOT_DEFERRABLE;
+ else if (strncmp(token, "ATTR_DEFERRED", 13) == 0)
+ local_node->contype = CONSTR_ATTR_DEFERRED;
+ else if (strncmp(token, "ATTR_IMMEDIATE", 14) == 0)
+ local_node->contype = CONSTR_ATTR_IMMEDIATE;
+ else
+ elog(ERROR, "unrecognized constraint type: %d",
+ (int) local_node->contype);
+ READ_DONE();
+ }
+
+ /*
* _readSortGroupClause
*/
static SortGroupClause *
***************
*** 1232,1238 **** _readRangeTblEntry(void)
READ_DONE();
}
-
/*
* parseNodeString
*
--- 1435,1440 ----
***************
*** 1255,1260 **** parseNodeString(void)
--- 1457,1476 ----
if (MATCH("QUERY", 5))
return_value = _readQuery();
+ else if (MATCH("CREATESTMT", 10))
+ return_value = _readCreateStmt();
+ else if (MATCH("DROPSTMT", 8))
+ return_value = _readDropStmt();
+ else if (MATCH("CREATEEXTENSIONSTMT", 19))
+ return_value = _readCreateExtensionStmt();
+ else if (MATCH("ALTERCMDTRIGSTMT", 16))
+ return_value = _readAlterCmdTrigStmt();
+ else if (MATCH("TYPENAME", 8))
+ return_value = _readTypeName();
+ else if (MATCH("COLUMNDEF", 9))
+ return_value = _readColumnDef();
+ else if (MATCH("CONSTRAINT", 10))
+ return_value = _readConstraint();
else if (MATCH("SORTGROUPCLAUSE", 15))
return_value = _readSortGroupClause();
else if (MATCH("WINDOWCLAUSE", 12))
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 53,58 ****
--- 53,59 ----
#include "catalog/index.h"
#include "catalog/namespace.h"
+ #include "catalog/pg_cmdtrigger.h"
#include "catalog/pg_trigger.h"
#include "commands/defrem.h"
#include "nodes/makefuncs.h"
***************
*** 195,200 **** static void processCASbits(int cas_bits, int location, const char *constrType,
--- 196,202 ----
}
%type stmt schema_stmt
+ AlterCmdTrigStmt
AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
***************
*** 208,219 **** static void processCASbits(int cas_bits, int location, const char *constrType,
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
! CreateAssertStmt CreateTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
! DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
! DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
LockStmt NotifyStmt ExplainableStmt PreparableStmt
--- 210,221 ----
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
! CreateAssertStmt CreateTrigStmt CreateCmdTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
! DropAssertStmt DropTrigStmt DropCmdTrigStmt DropRuleStmt DropCastStmt
! DropRoleStmt DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
LockStmt NotifyStmt ExplainableStmt PreparableStmt
***************
*** 264,273 **** static void processCASbits(int cas_bits, int location, const char *constrType,
%type OptSchemaEltList
%type TriggerForSpec TriggerForType
! %type TriggerActionTime
%type TriggerEvents TriggerOneEvent
%type TriggerFuncArg
%type TriggerWhen
%type copy_file_name
database_name access_method_clause access_method attr_name
--- 266,277 ----
%type OptSchemaEltList
%type TriggerForSpec TriggerForType
! %type TriggerActionTime CmdTriggerActionTime
%type TriggerEvents TriggerOneEvent
%type TriggerFuncArg
%type TriggerWhen
+ %type trigger_command enable_trigger
+ %type trigger_command_list
%type copy_file_name
database_name access_method_clause access_method attr_name
***************
*** 495,501 **** static void processCASbits(int cas_bits, int location, const char *constrType,
CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
! CLUSTER COALESCE COLLATE COLLATION COLUMN COMMENT COMMENTS COMMIT
COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS
CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
CROSS CSV CURRENT_P
--- 499,505 ----
CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
! CLUSTER COALESCE COLLATE COLLATION COLUMN COMMAND COMMENT COMMENTS COMMIT
COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS
CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
CROSS CSV CURRENT_P
***************
*** 675,681 **** stmtmulti: stmtmulti ';' stmt
;
stmt :
! AlterDatabaseStmt
| AlterDatabaseSetStmt
| AlterDefaultPrivilegesStmt
| AlterDomainStmt
--- 679,686 ----
;
stmt :
! AlterCmdTrigStmt
! | AlterDatabaseStmt
| AlterDatabaseSetStmt
| AlterDefaultPrivilegesStmt
| AlterDomainStmt
***************
*** 726,731 **** stmt :
--- 731,737 ----
| CreateStmt
| CreateTableSpaceStmt
| CreateTrigStmt
+ | CreateCmdTrigStmt
| CreateRoleStmt
| CreateUserStmt
| CreateUserMappingStmt
***************
*** 749,754 **** stmt :
--- 755,761 ----
| DropStmt
| DropTableSpaceStmt
| DropTrigStmt
+ | DropCmdTrigStmt
| DropRoleStmt
| DropUserStmt
| DropUserMappingStmt
***************
*** 4260,4265 **** DropTrigStmt:
--- 4267,4454 ----
/*****************************************************************************
*
* QUERIES :
+ * CREATE TRIGGER ... BEFORE|INSTEAD OF|AFTER COMMAND ...
+ * DROP TRIGGER ... ON COMMAND ...
+ *
+ *****************************************************************************/
+
+ CreateCmdTrigStmt:
+ CREATE TRIGGER name CmdTriggerActionTime COMMAND trigger_command_list
+ EXECUTE PROCEDURE func_name '(' ')'
+ {
+ CreateCmdTrigStmt *n = makeNode(CreateCmdTrigStmt);
+ n->trigname = $3;
+ n->timing = $4;
+ n->command = $6;
+ n->funcname = $9;
+ $$ = (Node *)n;
+ }
+ | CREATE TRIGGER name CmdTriggerActionTime ANY COMMAND
+ EXECUTE PROCEDURE func_name '(' ')'
+ {
+ CreateCmdTrigStmt *n = makeNode(CreateCmdTrigStmt);
+ n->trigname = $3;
+ n->timing = $4;
+ n->command = list_make1(makeStringConst("ANY", @5));
+ n->funcname = $9;
+ $$ = (Node *)n;
+ }
+ ;
+
+ CmdTriggerActionTime:
+ BEFORE { $$ = CMD_TRIGGER_FIRED_BEFORE; }
+ | AFTER { $$ = CMD_TRIGGER_FIRED_AFTER; }
+ ;
+
+ trigger_command_list:
+ trigger_command
+ {
+ $$ = list_make1(makeStringConst($1, @1));
+ }
+ | trigger_command_list ',' trigger_command
+ {
+ $$ = lappend($1, makeStringConst($3, @1));
+ }
+ ;
+
+
+ /*
+ * that will get matched against what CreateCommandTag returns
+ *
+ * we don't support Command Triggers on every possible command that PostgreSQL
+ * supports, this list should match with the implementation.
+ */
+ trigger_command:
+ CREATE SCHEMA { $$ = "CREATE SCHEMA"; }
+ | CREATE EXTENSION { $$ = "CREATE EXTENSION"; }
+ | CREATE FUNCTION { $$ = "CREATE FUNCTION"; }
+ | CREATE TABLE { $$ = "CREATE TABLE"; }
+ | CREATE FOREIGN TABLE { $$ = "CREATE FOREIGN TABLE"; }
+ | CREATE INDEX { $$ = "CREATE INDEX"; }
+ | CREATE SEQUENCE { $$ = "CREATE SEQUENCE"; }
+ | CREATE VIEW { $$ = "CREATE VIEW"; }
+ | 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"; }
+ | ALTER SCHEMA { $$ = "ALTER SCHEMA"; }
+ | ALTER EXTENSION { $$ = "ALTER EXTENSION"; }
+ | ALTER FUNCTION { $$ = "ALTER FUNCTION"; }
+ | ALTER TABLE { $$ = "ALTER TABLE"; }
+ | ALTER AGGREGATE { $$ = "ALTER AGGREGATE"; }
+ | ALTER OPERATOR { $$ = "ALTER OPERATOR"; }
+ | ALTER COLLATION { $$ = "ALTER COLLATION"; }
+ | ALTER TEXT_P SEARCH PARSER { $$ = "ALTER TEXT SEARCH PARSER"; }
+ | ALTER TEXT_P SEARCH DICTIONARY { $$ = "ALTER TEXT SEARCH DICTIONARY"; }
+ | ALTER TEXT_P SEARCH TEMPLATE { $$ = "ALTER TEXT SEARCH TEMPLATE"; }
+ | ALTER TEXT_P SEARCH CONFIGURATION { $$ = "ALTER TEXT SEARCH CONFIGURATION"; }
+ | ALTER TYPE_P { $$ = "ALTER TYPE"; }
+ | ALTER DOMAIN_P { $$ = "ALTER DOMAIN"; }
+ | ALTER TRIGGER { $$ = "ALTER TRIGGER"; }
+ | DROP TABLE { $$ = "DROP TABLE"; }
+ | DROP SEQUENCE { $$ = "DROP SEQUENCE"; }
+ | DROP VIEW { $$ = "DROP VIEW"; }
+ | DROP INDEX { $$ = "DROP INDEX"; }
+ | DROP FOREIGN TABLE { $$ = "DROP FOREIGN TABLE"; }
+ | DROP TYPE_P { $$ = "DROP TYPE"; }
+ | DROP DOMAIN_P { $$ = "DROP DOMAIN"; }
+ | DROP COLLATION { $$ = "DROP COLLATION"; }
+ | DROP CONVERSION_P { $$ = "DROP CONVERSION_P"; }
+ | DROP SCHEMA { $$ = "DROP SCHEMA"; }
+ | DROP EXTENSION { $$ = "DROP EXTENSION"; }
+ | DROP TEXT_P SEARCH PARSER { $$ = "DROP TEXT SEARCH PARSER"; }
+ | DROP TEXT_P SEARCH DICTIONARY { $$ = "DROP TEXT SEARCH DICTIONARY"; }
+ | DROP TEXT_P SEARCH TEMPLATE { $$ = "DROP TEXT SEARCH TEMPLATE"; }
+ | DROP TEXT_P SEARCH CONFIGURATION { $$ = "DROP TEXT SEARCH CONFIGURATION"; }
+ | DROP LANGUAGE { $$ = "DROP LANGUAGE"; }
+ | DROP FOREIGN DATA_P WRAPPER { $$ = "DROP FOREIGN DATA WRAPPER"; }
+ | DROP SERVER { $$ = "DROP SERVER"; }
+ | DROP TRIGGER { $$ = "DROP TRIGGER"; }
+ | DROP ASSERTION { $$ = "DROP ASSERTION"; }
+ | DROP OPERATOR CLASS { $$ = "DROP OPERATOR CLASS"; }
+ | DROP OPERATOR FAMILY { $$ = "DROP OPERATOR FAMILY"; }
+ | DROP FUNCTION { $$ = "DROP FUNCTION"; }
+ | DROP AGGREGATE { $$ = "DROP AGGREGATE"; }
+ | DROP OPERATOR { $$ = "DROP OPERATOR"; }
+ | DROP CAST { $$ = "DROP CAST"; }
+ | DROP RULE { $$ = "DROP RULE"; }
+ | VACUUM { $$ = "VACUUM"; }
+ ;
+
+ DropCmdTrigStmt:
+ DROP TRIGGER name ON COMMAND trigger_command_list opt_drop_behavior
+ {
+ DropCmdTrigStmt *n = makeNode(DropCmdTrigStmt);
+ n->trigname = $3;
+ n->command = $6;
+ n->behavior = $7;
+ n->missing_ok = false;
+ $$ = (Node *) n;
+ }
+ | DROP TRIGGER IF_P EXISTS name ON COMMAND trigger_command_list opt_drop_behavior
+ {
+ DropCmdTrigStmt *n = makeNode(DropCmdTrigStmt);
+ n->trigname = $5;
+ n->command = $8;
+ n->behavior = $9;
+ n->missing_ok = true;
+ $$ = (Node *) n;
+ }
+ | DROP TRIGGER name ON ANY COMMAND opt_drop_behavior
+ {
+ DropCmdTrigStmt *n = makeNode(DropCmdTrigStmt);
+ n->trigname = $3;
+ n->command = list_make1(makeStringConst("ANY", @4));
+ n->behavior = $7;
+ n->missing_ok = false;
+ $$ = (Node *) n;
+ }
+ | DROP TRIGGER IF_P EXISTS name ON ANY COMMAND opt_drop_behavior
+ {
+ DropCmdTrigStmt *n = makeNode(DropCmdTrigStmt);
+ n->trigname = $5;
+ n->command = list_make1(makeStringConst("ANY", @6));
+ n->behavior = $9;
+ n->missing_ok = true;
+ $$ = (Node *) n;
+ }
+ ;
+
+ AlterCmdTrigStmt:
+ ALTER TRIGGER name ON COMMAND trigger_command SET enable_trigger
+ {
+ AlterCmdTrigStmt *n = makeNode(AlterCmdTrigStmt);
+ n->trigname = $3;
+ n->command = $6;
+ n->tgenabled = $8;
+ $$ = (Node *) n;
+ }
+ | ALTER TRIGGER name ON ANY COMMAND SET enable_trigger
+ {
+ AlterCmdTrigStmt *n = makeNode(AlterCmdTrigStmt);
+ n->trigname = $3;
+ n->command = "ANY";
+ n->tgenabled = $8;
+ $$ = (Node *) n;
+ }
+ ;
+
+ enable_trigger:
+ ENABLE_P { $$ = "O"; }
+ | ENABLE_P REPLICA { $$ = "R"; }
+ | ENABLE_P ALWAYS { $$ = "A"; }
+ | DISABLE_P { $$ = "D"; }
+ ;
+
+ /*****************************************************************************
+ *
+ * QUERIES :
* CREATE ASSERTION ...
* DROP ASSERTION ...
*
***************
*** 6763,6768 **** RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
--- 6952,6966 ----
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER TRIGGER name ON COMMAND trigger_command RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_CMDTRIGGER;
+ n->object = list_make1(makeString($6));
+ n->subname = $3;
+ n->newname = $9;
+ $$ = (Node *)n;
+ }
| ALTER ROLE RoleId RENAME TO RoleId
{
RenameStmt *n = makeNode(RenameStmt);
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 25,30 ****
--- 25,31 ----
#include "commands/alter.h"
#include "commands/async.h"
#include "commands/cluster.h"
+ #include "commands/cmdtrigger.h"
#include "commands/comment.h"
#include "commands/collationcmds.h"
#include "commands/conversioncmds.h"
***************
*** 58,66 ****
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/guc.h"
#include "utils/syscache.h"
-
/* Hook for plugins to get control in ProcessUtility() */
ProcessUtility_hook_type ProcessUtility_hook = NULL;
--- 59,67 ----
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/guc.h"
+ #include "utils/lsyscache.h"
#include "utils/syscache.h"
/* Hook for plugins to get control in ProcessUtility() */
ProcessUtility_hook_type ProcessUtility_hook = NULL;
***************
*** 150,155 **** CommandIsReadOnly(Node *parsetree)
--- 151,305 ----
}
/*
+ * Support function for calling the command triggers.
+ */
+ static void
+ call_before_cmdtriggers(CommandContext cmd)
+ {
+ switch (nodeTag(cmd->parsetree))
+ {
+ case T_AlterDatabaseStmt:
+ case T_AlterDatabaseSetStmt:
+ case T_AlterDomainStmt:
+ case T_AlterFunctionStmt:
+ case T_AlterRoleStmt:
+ case T_AlterRoleSetStmt:
+ case T_AlterObjectSchemaStmt:
+ case T_AlterOwnerStmt:
+ case T_AlterSeqStmt:
+ case T_AlterTableStmt:
+ case T_RenameStmt:
+ case T_CommentStmt:
+ case T_DefineStmt:
+ case T_CreateCastStmt:
+ case T_CreateConversionStmt:
+ case T_CreatedbStmt:
+ case T_CreateDomainStmt:
+ case T_CreateFunctionStmt:
+ case T_CreateRoleStmt:
+ case T_IndexStmt:
+ case T_CreatePLangStmt:
+ case T_CreateOpClassStmt:
+ case T_CreateOpFamilyStmt:
+ case T_AlterOpFamilyStmt:
+ case T_RuleStmt:
+ case T_CreateSchemaStmt:
+ case T_CreateSeqStmt:
+ case T_CreateStmt:
+ case T_CreateTableSpaceStmt:
+ case T_CreateTrigStmt:
+ case T_CompositeTypeStmt:
+ case T_CreateEnumStmt:
+ case T_CreateRangeStmt:
+ case T_AlterEnumStmt:
+ case T_ViewStmt:
+ case T_DropStmt:
+ case T_DropdbStmt:
+ case T_DropTableSpaceStmt:
+ case T_DropRoleStmt:
+ case T_GrantStmt:
+ case T_GrantRoleStmt:
+ case T_AlterDefaultPrivilegesStmt:
+ case T_TruncateStmt:
+ case T_DropOwnedStmt:
+ case T_ReassignOwnedStmt:
+ case T_AlterTSDictionaryStmt:
+ case T_AlterTSConfigurationStmt:
+ case T_CreateExtensionStmt:
+ case T_AlterExtensionStmt:
+ case T_AlterExtensionContentsStmt:
+ case T_CreateFdwStmt:
+ case T_AlterFdwStmt:
+ case T_CreateForeignServerStmt:
+ case T_AlterForeignServerStmt:
+ case T_CreateUserMappingStmt:
+ case T_AlterUserMappingStmt:
+ case T_DropUserMappingStmt:
+ case T_AlterTableSpaceOptionsStmt:
+ case T_CreateForeignTableStmt:
+ case T_SecLabelStmt:
+ ExecBeforeCommandTriggers(cmd);
+
+ default:
+ /* commands that don't support triggers */
+ return;
+ }
+ }
+
+ static void
+ call_after_cmdtriggers(CommandContext cmd)
+ {
+ switch (nodeTag(cmd->parsetree))
+ {
+ case T_AlterDatabaseStmt:
+ case T_AlterDatabaseSetStmt:
+ case T_AlterDomainStmt:
+ case T_AlterFunctionStmt:
+ case T_AlterRoleStmt:
+ case T_AlterRoleSetStmt:
+ case T_AlterObjectSchemaStmt:
+ case T_AlterOwnerStmt:
+ case T_AlterSeqStmt:
+ case T_AlterTableStmt:
+ case T_RenameStmt:
+ case T_CommentStmt:
+ case T_DefineStmt:
+ case T_CreateCastStmt:
+ case T_CreateConversionStmt:
+ case T_CreatedbStmt:
+ case T_CreateDomainStmt:
+ case T_CreateFunctionStmt:
+ case T_CreateRoleStmt:
+ case T_IndexStmt:
+ case T_CreatePLangStmt:
+ case T_CreateOpClassStmt:
+ case T_CreateOpFamilyStmt:
+ case T_AlterOpFamilyStmt:
+ case T_RuleStmt:
+ case T_CreateSchemaStmt:
+ case T_CreateSeqStmt:
+ case T_CreateStmt:
+ case T_CreateTableSpaceStmt:
+ case T_CreateTrigStmt:
+ case T_CompositeTypeStmt:
+ case T_CreateEnumStmt:
+ case T_CreateRangeStmt:
+ case T_AlterEnumStmt:
+ case T_ViewStmt:
+ case T_DropStmt:
+ case T_DropdbStmt:
+ case T_DropTableSpaceStmt:
+ case T_DropRoleStmt:
+ case T_GrantStmt:
+ case T_GrantRoleStmt:
+ case T_AlterDefaultPrivilegesStmt:
+ case T_TruncateStmt:
+ case T_DropOwnedStmt:
+ case T_ReassignOwnedStmt:
+ case T_AlterTSDictionaryStmt:
+ case T_AlterTSConfigurationStmt:
+ case T_CreateExtensionStmt:
+ case T_AlterExtensionStmt:
+ case T_AlterExtensionContentsStmt:
+ case T_CreateFdwStmt:
+ case T_AlterFdwStmt:
+ case T_CreateForeignServerStmt:
+ case T_AlterForeignServerStmt:
+ case T_CreateUserMappingStmt:
+ case T_AlterUserMappingStmt:
+ case T_DropUserMappingStmt:
+ case T_AlterTableSpaceOptionsStmt:
+ case T_CreateForeignTableStmt:
+ case T_SecLabelStmt:
+ ExecAfterCommandTriggers(cmd);
+
+ default:
+ /* commands that don't support triggers */
+ return;
+ }
+ }
+
+ /*
* check_xact_readonly: is a utility command read-only?
*
* Here we use the loose rules of XactReadOnly mode: no permanent effects
***************
*** 184,189 **** check_xact_readonly(Node *parsetree)
--- 334,341 ----
case T_CommentStmt:
case T_DefineStmt:
case T_CreateCastStmt:
+ case T_CreateCmdTrigStmt:
+ case T_AlterCmdTrigStmt:
case T_CreateConversionStmt:
case T_CreatedbStmt:
case T_CreateDomainStmt:
***************
*** 205,210 **** check_xact_readonly(Node *parsetree)
--- 357,363 ----
case T_CreateRangeStmt:
case T_AlterEnumStmt:
case T_ViewStmt:
+ case T_DropCmdTrigStmt:
case T_DropStmt:
case T_DropdbStmt:
case T_DropTableSpaceStmt:
***************
*** 344,354 **** standard_ProcessUtility(Node *parsetree,
--- 497,512 ----
DestReceiver *dest,
char *completionTag)
{
+ CommandContextData cmd;
check_xact_readonly(parsetree);
if (completionTag)
completionTag[0] = '\0';
+ /* call the BEFORE ANY COMMAND triggers first */
+ InitCommandContext(&cmd, parsetree, true);
+ call_before_cmdtriggers(&cmd);
+
switch (nodeTag(parsetree))
{
/*
***************
*** 509,519 **** standard_ProcessUtility(Node *parsetree,
{
List *stmts;
ListCell *l;
! Oid relOid;
/* Run parse analysis ... */
! stmts = transformCreateStmt((CreateStmt *) parsetree,
! queryString);
/* ... and do it */
foreach(l, stmts)
--- 667,693 ----
{
List *stmts;
ListCell *l;
! Oid relOid = InvalidOid;
! CreateStmt *stmt = (CreateStmt *) parsetree;
! CommandContextData cmd;
!
! /*
! * Call BEFORE CREATE TABLE triggers
! */
! InitCommandContext(&cmd, parsetree, false);
!
! if (CommandFiresTriggers(&cmd))
! {
! cmd.objectId = InvalidOid;
! cmd.objectname = stmt->relation->relname;
! cmd.schemaname = get_namespace_name(
! RangeVarGetCreationNamespace(stmt->relation));
!
! ExecBeforeCommandTriggers(&cmd);
! }
/* Run parse analysis ... */
! stmts = transformCreateStmt(stmt, queryString);
/* ... and do it */
foreach(l, stmts)
***************
*** 571,576 **** standard_ProcessUtility(Node *parsetree,
--- 745,757 ----
if (lnext(l) != NULL)
CommandCounterIncrement();
}
+
+ /* Call AFTER CREATE TABLE triggers */
+ if (CommandFiresAfterTriggers(&cmd))
+ {
+ cmd.objectId = relOid;
+ ExecAfterCommandTriggers(&cmd);
+ }
}
break;
***************
*** 704,709 **** standard_ProcessUtility(Node *parsetree,
--- 885,891 ----
List *stmts;
ListCell *l;
LOCKMODE lockmode;
+ CommandContextData cmd;
/*
* Figure out lock mode, and acquire lock. This also does
***************
*** 716,721 **** standard_ProcessUtility(Node *parsetree,
--- 898,918 ----
if (OidIsValid(relid))
{
+ /*
+ * Call BEFORE|INSTEAD OF ALTER TABLE triggers
+ */
+ InitCommandContext(&cmd, parsetree, false);
+
+ if (ListCommandTriggers(&cmd))
+ {
+ cmd.objectId = relid;
+ cmd.objectname = atstmt->relation->relname;
+ cmd.schemaname = get_namespace_name(
+ RangeVarGetCreationNamespace(atstmt->relation));
+
+ ExecBeforeCommandTriggers(&cmd);
+ }
+
/* Run parse analysis ... */
stmts = transformAlterTableStmt(atstmt, queryString);
***************
*** 744,749 **** standard_ProcessUtility(Node *parsetree,
--- 941,949 ----
if (lnext(l) != NULL)
CommandCounterIncrement();
}
+ /* Call AFTER ALTER TABLE triggers */
+ if (cmd.after != NIL)
+ ExecAfterCommandTriggers(&cmd);
}
else
ereport(NOTICE,
***************
*** 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",
--- 1019,1061 ----
case T_DefineStmt:
{
DefineStmt *stmt = (DefineStmt *) parsetree;
+ CommandContextData cmd;
+
+ InitCommandContext(&cmd, parsetree, false);
switch (stmt->kind)
{
case OBJECT_AGGREGATE:
DefineAggregate(stmt->defnames, stmt->args,
! stmt->oldstyle, stmt->definition, &cmd);
break;
case OBJECT_OPERATOR:
Assert(stmt->args == NIL);
! DefineOperator(stmt->defnames, stmt->definition, &cmd);
break;
case OBJECT_TYPE:
Assert(stmt->args == NIL);
! DefineType(stmt->defnames, stmt->definition, &cmd);
break;
case OBJECT_TSPARSER:
Assert(stmt->args == NIL);
! DefineTSParser(stmt->defnames, stmt->definition, &cmd);
break;
case OBJECT_TSDICTIONARY:
Assert(stmt->args == NIL);
! DefineTSDictionary(stmt->defnames, stmt->definition, &cmd);
break;
case OBJECT_TSTEMPLATE:
Assert(stmt->args == NIL);
! DefineTSTemplate(stmt->defnames, stmt->definition, &cmd);
break;
case OBJECT_TSCONFIGURATION:
Assert(stmt->args == NIL);
! DefineTSConfiguration(stmt->defnames, stmt->definition, &cmd);
break;
case OBJECT_COLLATION:
Assert(stmt->args == NIL);
! DefineCollation(stmt->defnames, stmt->definition, &cmd);
break;
default:
elog(ERROR, "unrecognized define stmt type: %d",
***************
*** 865,872 **** standard_ProcessUtility(Node *parsetree,
case T_CompositeTypeStmt: /* CREATE TYPE (composite) */
{
CompositeTypeStmt *stmt = (CompositeTypeStmt *) parsetree;
! DefineCompositeType(stmt->typevar, stmt->coldeflist);
}
break;
--- 1068,1077 ----
case T_CompositeTypeStmt: /* CREATE TYPE (composite) */
{
CompositeTypeStmt *stmt = (CompositeTypeStmt *) parsetree;
+ CommandContextData cmd;
! InitCommandContext(&cmd, parsetree, false);
! DefineCompositeType(stmt->typevar, stmt->coldeflist, &cmd);
}
break;
***************
*** 904,909 **** standard_ProcessUtility(Node *parsetree,
--- 1109,1117 ----
case T_IndexStmt: /* CREATE INDEX */
{
IndexStmt *stmt = (IndexStmt *) parsetree;
+ CommandContextData cmd;
+
+ InitCommandContext(&cmd, parsetree, false);
if (stmt->concurrent)
PreventTransactionChain(isTopLevel,
***************
*** 934,940 **** standard_ProcessUtility(Node *parsetree,
true, /* check_rights */
false, /* skip_build */
false, /* quiet */
! stmt->concurrent); /* concurrent */
}
break;
--- 1142,1149 ----
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;
--- 1181,1187 ----
DropdbStmt *stmt = (DropdbStmt *) parsetree;
PreventTransactionChain(isTopLevel, "DROP DATABASE");
! dropdb(stmt);
}
break;
***************
*** 1059,1064 **** standard_ProcessUtility(Node *parsetree,
--- 1268,1285 ----
InvalidOid, InvalidOid, false);
break;
+ case T_CreateCmdTrigStmt:
+ CreateCmdTrigger((CreateCmdTrigStmt *) parsetree, queryString);
+ break;
+
+ case T_DropCmdTrigStmt:
+ DropCmdTrigger((DropCmdTrigStmt *) parsetree);
+ break;
+
+ case T_AlterCmdTrigStmt:
+ (void) AlterCmdTrigger((AlterCmdTrigStmt *) parsetree);
+ break;
+
case T_CreatePLangStmt:
CreateProceduralLanguage((CreatePLangStmt *) parsetree);
break;
***************
*** 1197,1202 **** standard_ProcessUtility(Node *parsetree,
--- 1418,1425 ----
(int) nodeTag(parsetree));
break;
}
+ /* call the AFTER ANY COMMAND triggers */
+ call_after_cmdtriggers(&cmd);
}
/*
***************
*** 1950,1955 **** CreateCommandTag(Node *parsetree)
--- 2173,2190 ----
tag = "CREATE TRIGGER";
break;
+ case T_CreateCmdTrigStmt:
+ tag = "CREATE COMMAND TRIGGER";
+ break;
+
+ case T_DropCmdTrigStmt:
+ tag = "DROP COMMAND TRIGGER";
+ break;
+
+ case T_AlterCmdTrigStmt:
+ tag = "ALTER COMMAND TRIGGER";
+ break;
+
case T_CreatePLangStmt:
tag = "CREATE LANGUAGE";
break;
***************
*** 2151,2156 **** CreateCommandTag(Node *parsetree)
--- 2386,2398 ----
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)
--- 2687,2708 ----
lev = LOGSTMT_DDL;
break;
+ case T_DropPropertyStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
+ case T_CreateCmdTrigStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
+ case T_DropCmdTrigStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
+ case T_AlterCmdTrigStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_CreatePLangStmt:
lev = LOGSTMT_DDL;
break;
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
***************
*** 39,47 ****
--- 39,49 ----
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/tlist.h"
+ #include "parser/analyze.h"
#include "parser/keywords.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
+ #include "parser/parse_type.h"
#include "parser/parser.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h"
***************
*** 256,262 **** 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
--- 258,263 ----
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 48,53 ****
--- 48,54 ----
#include "access/transam.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_class.h"
+ #include "catalog/pg_cmdtrigger.h"
#include "catalog/pg_default_acl.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
***************
*** 191,196 **** static void dumpConversion(Archive *fout, ConvInfo *convinfo);
--- 192,198 ----
static void dumpRule(Archive *fout, RuleInfo *rinfo);
static void dumpAgg(Archive *fout, AggInfo *agginfo);
static void dumpTrigger(Archive *fout, TriggerInfo *tginfo);
+ static void dumpCmdTrigger(Archive *fout, CmdTriggerInfo *ctginfo);
static void dumpTable(Archive *fout, TableInfo *tbinfo);
static void dumpTableSchema(Archive *fout, TableInfo *tbinfo);
static void dumpAttrDef(Archive *fout, AttrDefInfo *adinfo);
***************
*** 5283,5288 **** getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
--- 5285,5359 ----
}
/*
+ * getCmdTriggers
+ * get information about every command trigger on a dumpable table
+ */
+ CmdTriggerInfo *
+ getCmdTriggers(Archive *fout, int *numCmdTriggers)
+ {
+ int i;
+ PQExpBuffer query = createPQExpBuffer();
+ PGresult *res;
+ CmdTriggerInfo *ctginfo;
+ int i_tableoid,
+ i_oid,
+ i_ctgcommand,
+ i_ctgname,
+ i_ctgfname,
+ i_ctgtype,
+ i_ctgenabled;
+ int ntups;
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(fout, "pg_catalog");
+
+ if (fout->remoteVersion >= 90200)
+ {
+ appendPQExpBuffer(query,
+ "SELECT c.tableoid, c.oid, "
+ "ctgname, ctgtype, ctgcommand, proname as ctgfname, ctgenabled "
+ "FROM pg_cmdtrigger c JOIN pg_proc p on c.ctgfoid = p.oid "
+ "ORDER BY c.oid");
+ }
+
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ *numCmdTriggers = ntups;
+
+ ctginfo = (CmdTriggerInfo *) pg_malloc(ntups * sizeof(CmdTriggerInfo));
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_ctgname = PQfnumber(res, "ctgname");
+ i_ctgtype = PQfnumber(res, "ctgtype");
+ i_ctgcommand = PQfnumber(res, "ctgcommand");
+ i_ctgfname = PQfnumber(res, "ctgfname");
+ i_ctgenabled = PQfnumber(res, "ctgenabled");
+
+ for (i = 0; i < ntups; i++)
+ {
+ ctginfo[i].dobj.objType = DO_CMDTRIGGER;
+ ctginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ ctginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&ctginfo[i].dobj);
+ ctginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_ctgname));
+ ctginfo[i].ctgname = pg_strdup(PQgetvalue(res, i, i_ctgname));
+ ctginfo[i].ctgtype = *(PQgetvalue(res, i, i_ctgtype));
+ ctginfo[i].ctgcommand = pg_strdup(PQgetvalue(res, i, i_ctgcommand));
+ ctginfo[i].ctgfname = pg_strdup(PQgetvalue(res, i, i_ctgfname));
+ ctginfo[i].ctgenabled = *(PQgetvalue(res, i, i_ctgenabled));
+ }
+
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+
+ return ctginfo;
+ }
+
+ /*
* getProcLangs
* get basic information about every procedural language in the system
*
***************
*** 7173,7178 **** dumpDumpableObject(Archive *fout, DumpableObject *dobj)
--- 7244,7252 ----
case DO_TRIGGER:
dumpTrigger(fout, (TriggerInfo *) dobj);
break;
+ case DO_CMDTRIGGER:
+ dumpCmdTrigger(fout, (CmdTriggerInfo *) dobj);
+ break;
case DO_CONSTRAINT:
dumpConstraint(fout, (ConstraintInfo *) dobj);
break;
***************
*** 13645,13650 **** dumpTrigger(Archive *fout, TriggerInfo *tginfo)
--- 13719,13777 ----
destroyPQExpBuffer(labelq);
}
+ static void
+ dumpCmdTrigger(Archive *fout, CmdTriggerInfo *ctginfo)
+ {
+ PQExpBuffer query;
+ PQExpBuffer labelq;
+
+ query = createPQExpBuffer();
+ labelq = createPQExpBuffer();
+
+ appendPQExpBuffer(query, "CREATE TRIGGER ");
+ appendPQExpBufferStr(query, fmtId(ctginfo->dobj.name));
+
+ /* Trigger type */
+ if (ctginfo->ctgtype == CMD_TRIGGER_FIRED_BEFORE)
+ appendPQExpBuffer(query, " BEFORE");
+ else if (ctginfo->ctgtype == CMD_TRIGGER_FIRED_AFTER)
+ appendPQExpBuffer(query, " AFTER");
+ else
+ {
+ write_msg(NULL, "unexpected ctgtype value: %d\n", ctginfo->ctgtype);
+ exit_nicely(1);
+ }
+
+ if (strcmp("ANY", ctginfo->ctgcommand) == 0)
+ appendPQExpBufferStr(query, " ANY COMMAND");
+ else
+ {
+ appendPQExpBufferStr(query, " COMMAND ");
+ appendPQExpBufferStr(query, fmtId(ctginfo->ctgcommand));
+ }
+
+ appendPQExpBuffer(query, " EXECUTE PROCEDURE ");
+ appendPQExpBufferStr(query, fmtId(ctginfo->ctgfname));
+ appendPQExpBuffer(query, " ();\n");
+
+ appendPQExpBuffer(labelq, "TRIGGER %s ",
+ fmtId(ctginfo->dobj.name));
+ appendPQExpBuffer(labelq, "ON COMMAND %s",
+ fmtId(ctginfo->ctgcommand));
+
+ ArchiveEntry(fout, ctginfo->dobj.catId, ctginfo->dobj.dumpId,
+ ctginfo->dobj.name, NULL, NULL, "", false,
+ "COMMAND TRIGGER", SECTION_POST_DATA,
+ query->data, "", NULL, NULL, 0, NULL, NULL);
+
+ dumpComment(fout, labelq->data,
+ NULL, NULL,
+ ctginfo->dobj.catId, 0, ctginfo->dobj.dumpId);
+
+ destroyPQExpBuffer(query);
+ destroyPQExpBuffer(labelq);
+ }
+
/*
* dumpRule
* Dump a rule
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
***************
*** 118,124 **** typedef enum
DO_DEFAULT_ACL,
DO_BLOB,
DO_BLOB_DATA,
! DO_COLLATION
} DumpableObjectType;
typedef struct _dumpableObject
--- 118,125 ----
DO_DEFAULT_ACL,
DO_BLOB,
DO_BLOB_DATA,
! DO_COLLATION,
! DO_CMDTRIGGER
} DumpableObjectType;
typedef struct _dumpableObject
***************
*** 350,355 **** typedef struct _triggerInfo
--- 351,366 ----
char *tgdef;
} TriggerInfo;
+ typedef struct _cmdtriggerInfo
+ {
+ DumpableObject dobj;
+ char *ctgcommand;
+ char *ctgname;
+ char *ctgfname;
+ char ctgtype;
+ char ctgenabled;
+ } CmdTriggerInfo;
+
/*
* struct ConstraintInfo is used for all constraint types. However we
* use a different objType for foreign key constraints, to make it easier
***************
*** 558,562 **** extern ForeignServerInfo *getForeignServers(Archive *fout,
--- 569,574 ----
extern DefaultACLInfo *getDefaultACLs(Archive *fout, int *numDefaultACLs);
extern void getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
int numExtensions);
+ extern CmdTriggerInfo *getCmdTriggers(Archive *fout, int *numCmdTriggers);
#endif /* PG_DUMP_H */
*** a/src/bin/pg_dump/pg_dump_sort.c
--- b/src/bin/pg_dump/pg_dump_sort.c
***************
*** 59,65 **** static const int oldObjectTypePriority[] =
17, /* DO_DEFAULT_ACL */
9, /* DO_BLOB */
11, /* DO_BLOB_DATA */
! 2 /* DO_COLLATION */
};
/*
--- 59,66 ----
17, /* DO_DEFAULT_ACL */
9, /* DO_BLOB */
11, /* DO_BLOB_DATA */
! 2, /* DO_COLLATION */
! 18 /* DO_CMDTRIGGER */
};
/*
***************
*** 98,104 **** static const int newObjectTypePriority[] =
29, /* DO_DEFAULT_ACL */
21, /* DO_BLOB */
23, /* DO_BLOB_DATA */
! 3 /* DO_COLLATION */
};
--- 99,106 ----
29, /* DO_DEFAULT_ACL */
21, /* DO_BLOB */
23, /* DO_BLOB_DATA */
! 3, /* DO_COLLATION */
! 30 /* DO_CMDTRIGGER */
};
***************
*** 1114,1119 **** describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
--- 1116,1126 ----
"TRIGGER %s (ID %d OID %u)",
obj->name, obj->dumpId, obj->catId.oid);
return;
+ case DO_CMDTRIGGER:
+ snprintf(buf, bufsize,
+ "TRIGGER %s ON COMMAND %s (ID %d OID %u)",
+ obj->name, ((CmdTriggerInfo *)obj)->ctgcommand, obj->dumpId, obj->catId.oid);
+ return;
case DO_CONSTRAINT:
snprintf(buf, bufsize,
"CONSTRAINT %s (ID %d OID %u)",
*** a/src/bin/psql/command.c
--- b/src/bin/psql/command.c
***************
*** 363,369 **** exec_command(const char *cmd,
success = describeTablespaces(pattern, show_verbose);
break;
case 'c':
! success = listConversions(pattern, show_verbose, show_system);
break;
case 'C':
success = listCasts(pattern, show_verbose);
--- 363,380 ----
success = describeTablespaces(pattern, show_verbose);
break;
case 'c':
! switch (cmd[2])
! {
! case '\0':
! success = listConversions(pattern, show_verbose, show_system);
! break;
! case 't':
! success = listCmdTriggers(pattern, show_verbose);
! break;
! default:
! status = PSQL_CMD_UNKNOWN;
! break;
! }
break;
case 'C':
success = listCasts(pattern, show_verbose);
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
***************
*** 2941,2946 **** listConversions(const char *pattern, bool verbose, bool showSystem)
--- 2941,3003 ----
}
/*
+ * \dct
+ *
+ * Describes command triggers.
+ */
+ bool
+ listCmdTriggers(const char *pattern, bool verbose)
+ {
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+ static const bool translate_columns[] = {true, true};
+
+ initPQExpBuffer(&buf);
+
+ printfPQExpBuffer(&buf,
+ "SELECT ctgname as \"%s\", "
+ "'CREATE TRIGGER ' || ctgname || ' ' || "
+ "case ctgtype when 'A' then 'AFTER' "
+ " when 'B' then 'BEFORE' "
+ " when 'I' then 'INSTEAD OF' "
+ "end || "
+ "case ctgcommand when 'ANY' then ' ANY COMMAND '"
+ " else ' COMMAND ' || ctgcommand || ' '"
+ "end ||"
+ " 'EXECUTE PROCEDURE ' || proname || '();' as \"%s\" "
+ "FROM pg_cmdtrigger c "
+ "JOIN pg_proc p on c.ctgfoid = p.oid ",
+ gettext_noop("Name"),
+ gettext_noop("Definition"));
+
+ if (pattern)
+ {
+ processSQLNamePattern(pset.db, &buf, pattern, false, false,
+ NULL, "ctgcommand", NULL, NULL);
+
+ appendPQExpBuffer(&buf, " OR ctgcommand = 'ANY' ");
+ }
+
+ appendPQExpBuffer(&buf, "ORDER BY c.oid");
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of command triggers");
+ myopt.translate_header = true;
+ myopt.translate_columns = translate_columns;
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ PQclear(res);
+ return true;
+ }
+
+ /*
* \dC
*
* Describes casts.
*** a/src/bin/psql/describe.h
--- b/src/bin/psql/describe.h
***************
*** 66,71 **** extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
--- 66,74 ----
/* \dc */
extern bool listConversions(const char *pattern, bool verbose, bool showSystem);
+ /* \dcT */
+ extern bool listCmdTriggers(const char *pattern, bool verbose);
+
/* \dC */
extern bool listCasts(const char *pattern, bool verbose);
*** a/src/bin/psql/help.c
--- b/src/bin/psql/help.c
***************
*** 199,204 **** slashUsage(unsigned short int pager)
--- 199,205 ----
fprintf(output, _(" \\d[S+] NAME describe table, view, sequence, or index\n"));
fprintf(output, _(" \\da[S] [PATTERN] list aggregates\n"));
fprintf(output, _(" \\db[+] [PATTERN] list tablespaces\n"));
+ fprintf(output, _(" \\dct [PATTERN] list command triggers\n"));
fprintf(output, _(" \\dc[S+] [PATTERN] list conversions\n"));
fprintf(output, _(" \\dC[+] [PATTERN] list casts\n"));
fprintf(output, _(" \\dd[S] [PATTERN] show object descriptions not displayed elsewhere\n"));
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
***************
*** 146,151 **** typedef enum ObjectClass
--- 146,152 ----
OCLASS_USER_MAPPING, /* pg_user_mapping */
OCLASS_DEFACL, /* pg_default_acl */
OCLASS_EXTENSION, /* pg_extension */
+ OCLASS_CMDTRIGGER, /* pg_cmdtrigger */
MAX_OCLASS /* MUST BE LAST */
} ObjectClass;
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
***************
*** 234,239 **** DECLARE_UNIQUE_INDEX(pg_trigger_tgrelid_tgname_index, 2701, on pg_trigger using
--- 234,244 ----
DECLARE_UNIQUE_INDEX(pg_trigger_oid_index, 2702, on pg_trigger using btree(oid oid_ops));
#define TriggerOidIndexId 2702
+ DECLARE_UNIQUE_INDEX(pg_cmdtrigger_ctgcommand_ctgname_index, 3467, on pg_cmdtrigger using btree(ctgcommand name_ops, ctgname name_ops));
+ #define CmdTriggerCommandNameIndexId 3467
+ DECLARE_UNIQUE_INDEX(pg_cmdtrigger_oid_index, 3468, on pg_cmdtrigger using btree(oid oid_ops));
+ #define CmdTriggerOidIndexId 3468
+
DECLARE_UNIQUE_INDEX(pg_ts_config_cfgname_index, 3608, on pg_ts_config using btree(cfgname name_ops, cfgnamespace oid_ops));
#define TSConfigNameNspIndexId 3608
DECLARE_UNIQUE_INDEX(pg_ts_config_oid_index, 3712, on pg_ts_config using btree(oid oid_ops));
*** a/src/include/catalog/pg_aggregate.h
--- b/src/include/catalog/pg_aggregate.h
***************
*** 19,24 ****
--- 19,25 ----
#ifndef PG_AGGREGATE_H
#define PG_AGGREGATE_H
+ #include "commands/cmdtrigger.h"
#include "catalog/genbki.h"
#include "nodes/pg_list.h"
***************
*** 242,247 **** extern void AggregateCreate(const char *aggName,
List *aggfinalfnName,
List *aggsortopName,
Oid aggTransType,
! const char *agginitval);
#endif /* PG_AGGREGATE_H */
--- 243,249 ----
List *aggfinalfnName,
List *aggsortopName,
Oid aggTransType,
! const char *agginitval,
! CommandContext cmd);
#endif /* PG_AGGREGATE_H */
*** /dev/null
--- b/src/include/catalog/pg_cmdtrigger.h
***************
*** 0 ****
--- 1,70 ----
+ /*-------------------------------------------------------------------------
+ *
+ * pg_cmdtrigger.h
+ * definition of the system "command trigger" relation (pg_cmdtrigger)
+ * along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_trigger.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef PG_CMDTRIGGER_H
+ #define PG_CMDTRIGGER_H
+
+ #include "catalog/genbki.h"
+
+ /* ----------------
+ * pg_cmdtrigger definition. cpp turns this into
+ * typedef struct FormData_pg_cmdtrigger
+ * ----------------
+ */
+ #define CmdTriggerRelationId 3466
+
+ CATALOG(pg_cmdtrigger,3466)
+ {
+ NameData ctgcommand; /* trigger's command */
+ NameData ctgname; /* trigger's name */
+ Oid ctgfoid; /* OID of function to be called */
+ char ctgtype; /* BEFORE/AFTER */
+ char ctgenabled; /* trigger's firing configuration WRT
+ * session_replication_role */
+ } FormData_pg_cmdtrigger;
+
+ /* ----------------
+ * Form_pg_cmdtrigger corresponds to a pointer to a tuple with
+ * the format of pg_cmdtrigger relation.
+ * ----------------
+ */
+ typedef FormData_pg_cmdtrigger *Form_pg_cmdtrigger;
+
+ /* ----------------
+ * compiler constants for pg_cmdtrigger
+ * ----------------
+ */
+ #define Natts_pg_cmdtrigger 5
+ #define Anum_pg_cmdtrigger_ctgcommand 1
+ #define Anum_pg_cmdtrigger_ctgname 2
+ #define Anum_pg_cmdtrigger_ctgfoid 3
+ #define Anum_pg_cmdtrigger_ctgtype 4
+ #define Anum_pg_cmdtrigger_ctgenabled 5
+
+ /*
+ * Times at which a command trigger can be fired. These are the
+ * possible values for pg_cmdtrigger.ctgtype.
+ *
+ * pg_trigger is using binary mask tricks to make it super fast, but we don't
+ * need to be that tricky here: we're talking about commands, not data editing,
+ * and we don't have so many conditions, only type and enabled.
+ */
+ #define CMD_TRIGGER_FIRED_BEFORE 'B'
+ #define CMD_TRIGGER_FIRED_AFTER 'A'
+
+ #endif /* PG_CMDTRIGGER_H */
*** a/src/include/catalog/pg_collation_fn.h
--- b/src/include/catalog/pg_collation_fn.h
***************
*** 14,23 ****
#ifndef PG_COLLATION_FN_H
#define PG_COLLATION_FN_H
extern Oid CollationCreate(const char *collname, Oid collnamespace,
Oid collowner,
int32 collencoding,
! const char *collcollate, const char *collctype);
extern void RemoveCollationById(Oid collationOid);
#endif /* PG_COLLATION_FN_H */
--- 14,26 ----
#ifndef PG_COLLATION_FN_H
#define PG_COLLATION_FN_H
+ #include "commands/cmdtrigger.h"
+
extern Oid CollationCreate(const char *collname, Oid collnamespace,
Oid collowner,
int32 collencoding,
! const char *collcollate, const char *collctype,
! CommandContext cmd);
extern void RemoveCollationById(Oid collationOid);
#endif /* PG_COLLATION_FN_H */
*** a/src/include/catalog/pg_operator.h
--- b/src/include/catalog/pg_operator.h
***************
*** 23,28 ****
--- 23,29 ----
#define PG_OPERATOR_H
#include "catalog/genbki.h"
+ #include "commands/cmdtrigger.h"
#include "nodes/pg_list.h"
/* ----------------
***************
*** 1722,1727 **** extern void OperatorCreate(const char *operatorName,
Oid restrictionId,
Oid joinId,
bool canMerge,
! bool canHash);
#endif /* PG_OPERATOR_H */
--- 1723,1729 ----
Oid restrictionId,
Oid joinId,
bool canMerge,
! bool canHash,
! CommandContext cmd);
#endif /* PG_OPERATOR_H */
*** a/src/include/catalog/pg_type_fn.h
--- b/src/include/catalog/pg_type_fn.h
***************
*** 14,19 ****
--- 14,20 ----
#ifndef PG_TYPE_FN_H
#define PG_TYPE_FN_H
+ #include "commands/cmdtrigger.h"
#include "nodes/nodes.h"
***************
*** 73,79 **** extern void GenerateTypeDependencies(Oid typeNamespace,
bool rebuild);
extern void RenameTypeInternal(Oid typeOid, const char *newTypeName,
! Oid typeNamespace);
extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
--- 74,80 ----
bool rebuild);
extern void RenameTypeInternal(Oid typeOid, const char *newTypeName,
! Oid typeNamespace, CommandContext cmd);
extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
*** a/src/include/commands/alter.h
--- b/src/include/commands/alter.h
***************
*** 14,19 ****
--- 14,20 ----
#ifndef ALTER_H
#define ALTER_H
+ #include "commands/cmdtrigger.h"
#include "utils/acl.h"
#include "utils/relcache.h"
***************
*** 23,29 **** extern Oid AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid);
extern Oid AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId,
Oid objid, Oid nspOid,
int Anum_name, int Anum_namespace, int Anum_owner,
! AclObjectKind acl_kind);
extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt);
#endif /* ALTER_H */
--- 24,30 ----
extern Oid AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId,
Oid objid, Oid nspOid,
int Anum_name, int Anum_namespace, int Anum_owner,
! AclObjectKind acl_kind, CommandContext cmd);
extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt);
#endif /* ALTER_H */
*** /dev/null
--- b/src/include/commands/cmdtrigger.h
***************
*** 0 ****
--- 1,55 ----
+ /*-------------------------------------------------------------------------
+ *
+ * cmdtrigger.h
+ * Declarations for command trigger handling.
+ *
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/commands/cmdtrigger.h
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef CMDTRIGGER_H
+ #define CMDTRIGGER_H
+
+ #include "nodes/parsenodes.h"
+
+ /*
+ * Command Trigger Procedure are passed 4 arguments which are maintained in a
+ * global (bachend private) variable, command_context. That allows each command
+ * implementation to fill in the context then call the Exec...CommandTriggers
+ * API functions.
+ */
+ typedef struct CommandContextData
+ {
+ char *tag; /* Command Tag */
+ Oid objectId; /* oid of the existing object, if any */
+ char *schemaname; /* schemaname or NULL if not relevant */
+ char *objectname; /* objectname */
+ Node *parsetree; /* command parsetree, given as an internal */
+ List *before; /* procedures to call before the command */
+ List *after; /* procedures to call after the command */
+ } CommandContextData;
+
+ typedef struct CommandContextData *CommandContext;
+
+ extern CommandContext command_context;
+
+ extern void CreateCmdTrigger(CreateCmdTrigStmt *stmt, const char *queryString);
+ extern void DropCmdTrigger(DropCmdTrigStmt *stmt);
+ extern void RemoveCmdTriggerById(Oid ctrigOid);
+ extern Oid get_cmdtrigger_oid(const char *command, const char *trigname, bool missing_ok);
+ extern void AlterCmdTrigger(AlterCmdTrigStmt *stmt);
+ extern void RenameCmdTrigger(List *command, const char *trigname, const char *newname);
+
+ extern void InitCommandContext(CommandContext cmd, const Node *stmt, bool list_triggers);
+ extern bool ListCommandTriggers(CommandContext cmd);
+ extern bool CommandFiresTriggers(CommandContext cmd);
+ extern bool CommandFiresAfterTriggers(CommandContext cmd);
+ extern void ExecBeforeCommandTriggers(CommandContext cmd);
+ extern void ExecBeforeAnyCommandTriggers(CommandContext cmd);
+ extern void ExecAfterCommandTriggers(CommandContext cmd);
+ extern void ExecAfterAnyCommandTriggers(CommandContext cmd);
+
+ #endif /* CMD_TRIGGER_H */
*** a/src/include/commands/collationcmds.h
--- b/src/include/commands/collationcmds.h
***************
*** 15,27 ****
#ifndef COLLATIONCMDS_H
#define COLLATIONCMDS_H
#include "nodes/parsenodes.h"
! extern void DefineCollation(List *names, List *parameters);
! extern void RenameCollation(List *name, const char *newname);
! extern void AlterCollationOwner(List *name, Oid newOwnerId);
! extern void AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId);
! extern void AlterCollationNamespace(List *name, const char *newschema);
! extern Oid AlterCollationNamespace_oid(Oid collOid, Oid newNspOid);
#endif /* COLLATIONCMDS_H */
--- 15,30 ----
#ifndef COLLATIONCMDS_H
#define COLLATIONCMDS_H
+ #include "commands/cmdtrigger.h"
#include "nodes/parsenodes.h"
! extern void DefineCollation(List *names, List *parameters, CommandContext cmd);
! extern void RenameCollation(List *name, const char *newname, CommandContext cmd);
! extern void AlterCollationOwner(List *name, Oid newOwnerId, CommandContext cmd);
! extern void AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId, CommandContext cmd);
! extern void AlterCollationNamespace(List *name, const char *newschema,
! CommandContext cmd);
! extern Oid AlterCollationNamespace_oid(Oid collOid, Oid newNspOid,
! CommandContext cmd);
#endif /* COLLATIONCMDS_H */
*** a/src/include/commands/conversioncmds.h
--- b/src/include/commands/conversioncmds.h
***************
*** 18,27 ****
#include "nodes/parsenodes.h"
extern void CreateConversionCommand(CreateConversionStmt *parsetree);
! extern void RenameConversion(List *name, const char *newname);
! extern void AlterConversionOwner(List *name, Oid newOwnerId);
! extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId);
! extern void AlterConversionNamespace(List *name, const char *newschema);
extern Oid AlterConversionNamespace_oid(Oid convOid, Oid newNspOid);
#endif /* CONVERSIONCMDS_H */
--- 18,28 ----
#include "nodes/parsenodes.h"
extern void CreateConversionCommand(CreateConversionStmt *parsetree);
! extern void RenameConversion(List *name, const char *newname, CommandContext cmd);
! extern void AlterConversionOwner(List *name, Oid newOwnerId, CommandContext cmd);
! extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId, CommandContext cmd);
! extern void AlterConversionNamespace(List *name, const char *newschema,
! CommandContext cmd);
extern Oid AlterConversionNamespace_oid(Oid convOid, Oid newNspOid);
#endif /* CONVERSIONCMDS_H */
*** a/src/include/commands/dbcommands.h
--- b/src/include/commands/dbcommands.h
***************
*** 53,59 **** typedef struct xl_dbase_drop_rec
} xl_dbase_drop_rec;
extern void createdb(const CreatedbStmt *stmt);
! extern void dropdb(const char *dbname, bool missing_ok);
extern void RenameDatabase(const char *oldname, const char *newname);
extern void AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel);
extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt);
--- 53,59 ----
} xl_dbase_drop_rec;
extern void createdb(const CreatedbStmt *stmt);
! extern void dropdb(const DropdbStmt *stmt);
extern void RenameDatabase(const char *oldname, const char *newname);
extern void AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel);
extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt);
*** a/src/include/commands/defrem.h
--- b/src/include/commands/defrem.h
***************
*** 14,19 ****
--- 14,20 ----
#ifndef DEFREM_H
#define DEFREM_H
+ #include "commands/cmdtrigger.h"
#include "nodes/parsenodes.h"
/* commands/dropcmds.c */
***************
*** 39,45 **** 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,
--- 40,47 ----
bool check_rights,
bool skip_build,
bool quiet,
! bool concurrent,
! CommandContext cmd);
extern void ReindexIndex(RangeVar *indexRelation);
extern void ReindexTable(RangeVar *relation);
extern void ReindexDatabase(const char *databaseName,
***************
*** 64,95 **** extern void CreateFunction(CreateFunctionStmt *stmt, const char *queryString);
extern void RemoveFunctionById(Oid funcOid);
extern void SetFunctionReturnType(Oid funcOid, Oid newRetType);
extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType);
! extern void RenameFunction(List *name, List *argtypes, const char *newname);
! extern void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId);
! extern void AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId);
extern void AlterFunction(AlterFunctionStmt *stmt);
extern void CreateCast(CreateCastStmt *stmt);
extern void DropCastById(Oid castOid);
extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
! const char *newschema);
! extern Oid AlterFunctionNamespace_oid(Oid procOid, Oid nspOid);
extern void ExecuteDoStmt(DoStmt *stmt);
extern Oid get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok);
/* commands/operatorcmds.c */
! extern void DefineOperator(List *names, List *parameters);
extern void RemoveOperatorById(Oid operOid);
extern void AlterOperatorOwner(List *name, TypeName *typeName1,
! TypeName *typename2, Oid newOwnerId);
extern void AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId);
! extern void AlterOperatorNamespace(List *names, List *argtypes, const char *newschema);
extern Oid AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid);
/* commands/aggregatecmds.c */
extern void DefineAggregate(List *name, List *args, bool oldstyle,
! List *parameters);
! extern void RenameAggregate(List *name, List *args, const char *newname);
! extern void AlterAggregateOwner(List *name, List *args, Oid newOwnerId);
/* commands/opclasscmds.c */
extern void DefineOpClass(CreateOpClassStmt *stmt);
--- 66,99 ----
extern void RemoveFunctionById(Oid funcOid);
extern void SetFunctionReturnType(Oid funcOid, Oid newRetType);
extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType);
! extern void RenameFunction(List *name, List *argtypes, const char *newname, CommandContext cmd);
! extern void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId, CommandContext cmd);
! extern void AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId, CommandContext cmd);
extern void AlterFunction(AlterFunctionStmt *stmt);
extern void CreateCast(CreateCastStmt *stmt);
extern void DropCastById(Oid castOid);
extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
! const char *newschema, CommandContext cmd);
! extern Oid AlterFunctionNamespace_oid(Oid procOid, Oid nspOid,
! CommandContext cmd);
extern void ExecuteDoStmt(DoStmt *stmt);
extern Oid get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok);
/* commands/operatorcmds.c */
! extern void DefineOperator(List *names, List *parameters, CommandContext cmd);
extern void RemoveOperatorById(Oid operOid);
extern void AlterOperatorOwner(List *name, TypeName *typeName1,
! TypeName *typename2, Oid newOwnerId, CommandContext cmd);
extern void AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId);
! extern void AlterOperatorNamespace(List *names, List *argtypes,
! const char *newschema, CommandContext cmd);
extern Oid AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid);
/* commands/aggregatecmds.c */
extern void DefineAggregate(List *name, List *args, bool oldstyle,
! List *parameters, CommandContext cmd);
! extern void RenameAggregate(List *name, List *args, const char *newname, CommandContext cmd);
! extern void AlterAggregateOwner(List *name, List *args, Oid newOwnerId, CommandContext cmd);
/* commands/opclasscmds.c */
extern void DefineOpClass(CreateOpClassStmt *stmt);
***************
*** 103,145 **** extern void RenameOpClass(List *name, const char *access_method, const char *new
extern void RenameOpFamily(List *name, const char *access_method, const char *newname);
extern void AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId);
extern void AlterOpClassOwner_oid(Oid opclassOid, Oid newOwnerId);
! extern void AlterOpClassNamespace(List *name, char *access_method, const char *newschema);
extern Oid AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid);
extern void AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId);
extern void AlterOpFamilyOwner_oid(Oid opfamilyOid, Oid newOwnerId);
! extern void AlterOpFamilyNamespace(List *name, char *access_method, const char *newschema);
extern Oid AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid);
extern Oid get_am_oid(const char *amname, bool missing_ok);
extern Oid get_opclass_oid(Oid amID, List *opclassname, bool missing_ok);
extern Oid get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok);
/* commands/tsearchcmds.c */
! extern void DefineTSParser(List *names, List *parameters);
! extern void RenameTSParser(List *oldname, const char *newname);
! extern void AlterTSParserNamespace(List *name, const char *newschema);
extern Oid AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid);
extern void RemoveTSParserById(Oid prsId);
! extern void DefineTSDictionary(List *names, List *parameters);
! extern void RenameTSDictionary(List *oldname, const char *newname);
extern void RemoveTSDictionaryById(Oid dictId);
extern void AlterTSDictionary(AlterTSDictionaryStmt *stmt);
! extern void AlterTSDictionaryOwner(List *name, Oid newOwnerId);
! extern void AlterTSDictionaryNamespace(List *name, const char *newschema);
extern Oid AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid);
! extern void DefineTSTemplate(List *names, List *parameters);
! extern void RenameTSTemplate(List *oldname, const char *newname);
! extern void AlterTSTemplateNamespace(List *name, const char *newschema);
extern Oid AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid);
extern void RemoveTSTemplateById(Oid tmplId);
! extern void DefineTSConfiguration(List *names, List *parameters);
! extern void RenameTSConfiguration(List *oldname, const char *newname);
extern void RemoveTSConfigurationById(Oid cfgId);
extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
! extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId);
! extern void AlterTSConfigurationNamespace(List *name, const char *newschema);
extern Oid AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid);
extern text *serialize_deflist(List *deflist);
--- 107,151 ----
extern void RenameOpFamily(List *name, const char *access_method, const char *newname);
extern void AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId);
extern void AlterOpClassOwner_oid(Oid opclassOid, Oid newOwnerId);
! extern void AlterOpClassNamespace(List *name, char *access_method,
! const char *newschema, CommandContext cmd);
extern Oid AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid);
extern void AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId);
extern void AlterOpFamilyOwner_oid(Oid opfamilyOid, Oid newOwnerId);
! extern void AlterOpFamilyNamespace(List *name, char *access_method,
! const char *newschema, CommandContext cmd);
extern Oid AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid);
extern Oid get_am_oid(const char *amname, bool missing_ok);
extern Oid get_opclass_oid(Oid amID, List *opclassname, bool missing_ok);
extern Oid get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok);
/* commands/tsearchcmds.c */
! extern void DefineTSParser(List *names, List *parameters, CommandContext cmd);
! extern void RenameTSParser(List *oldname, const char *newname, CommandContext cmd);
! extern void AlterTSParserNamespace(List *name, const char *newschema, CommandContext cmd);
extern Oid AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid);
extern void RemoveTSParserById(Oid prsId);
! extern void DefineTSDictionary(List *names, List *parameters, CommandContext cmd);
! extern void RenameTSDictionary(List *oldname, const char *newname, CommandContext cmd);
extern void RemoveTSDictionaryById(Oid dictId);
extern void AlterTSDictionary(AlterTSDictionaryStmt *stmt);
! extern void AlterTSDictionaryOwner(List *name, Oid newOwnerId, CommandContext cmd);
! extern void AlterTSDictionaryNamespace(List *name, const char *newschema, CommandContext cmd);
extern Oid AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid);
! extern void DefineTSTemplate(List *names, List *parameters, CommandContext cmd);
! extern void RenameTSTemplate(List *oldname, const char *newname, CommandContext cmd);
! extern void AlterTSTemplateNamespace(List *name, const char *newschema, CommandContext cmd);
extern Oid AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid);
extern void RemoveTSTemplateById(Oid tmplId);
! extern void DefineTSConfiguration(List *names, List *parameters, CommandContext cmd);
! extern void RenameTSConfiguration(List *oldname, const char *newname, CommandContext cmd);
extern void RemoveTSConfigurationById(Oid cfgId);
extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
! extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId, CommandContext cmd);
! extern void AlterTSConfigurationNamespace(List *name, const char *newschema, CommandContext cmd);
extern Oid AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid);
extern text *serialize_deflist(List *deflist);
*** a/src/include/commands/extension.h
--- b/src/include/commands/extension.h
***************
*** 14,19 ****
--- 14,20 ----
#ifndef EXTENSION_H
#define EXTENSION_H
+ #include "commands/cmdtrigger.h"
#include "nodes/parsenodes.h"
***************
*** 43,48 **** extern void ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt);
extern Oid get_extension_oid(const char *extname, bool missing_ok);
extern char *get_extension_name(Oid ext_oid);
! extern void AlterExtensionNamespace(List *names, const char *newschema);
#endif /* EXTENSION_H */
--- 44,50 ----
extern Oid get_extension_oid(const char *extname, bool missing_ok);
extern char *get_extension_name(Oid ext_oid);
! extern void AlterExtensionNamespace(List *names, const char *newschema,
! CommandContext cmd);
#endif /* EXTENSION_H */
*** a/src/include/commands/schemacmds.h
--- b/src/include/commands/schemacmds.h
***************
*** 22,29 **** extern void CreateSchemaCommand(CreateSchemaStmt *parsetree,
extern void RemoveSchemaById(Oid schemaOid);
! extern void RenameSchema(const char *oldname, const char *newname);
! extern void AlterSchemaOwner(const char *name, Oid newOwnerId);
extern void AlterSchemaOwner_oid(Oid schemaOid, Oid newOwnerId);
#endif /* SCHEMACMDS_H */
--- 22,29 ----
extern void RemoveSchemaById(Oid schemaOid);
! extern void RenameSchema(const char *oldname, const char *newname, CommandContext cmd);
! extern void AlterSchemaOwner(const char *name, Oid newOwnerId, CommandContext cmd);
extern void AlterSchemaOwner_oid(Oid schemaOid, Oid newOwnerId);
#endif /* SCHEMACMDS_H */
*** a/src/include/commands/tablecmds.h
--- b/src/include/commands/tablecmds.h
***************
*** 15,20 ****
--- 15,21 ----
#define TABLECMDS_H
#include "access/htup.h"
+ #include "commands/cmdtrigger.h"
#include "nodes/parsenodes.h"
#include "storage/lock.h"
#include "utils/relcache.h"
***************
*** 48,57 **** extern void SetRelationHasSubclass(Oid relationId, bool relhassubclass);
extern void renameatt(RenameStmt *stmt);
! extern void RenameRelation(RenameStmt *stmt);
extern void RenameRelationInternal(Oid myrelid,
! const char *newrelname);
extern void find_composite_type_dependencies(Oid typeOid,
Relation origRelation,
--- 49,58 ----
extern void renameatt(RenameStmt *stmt);
! extern void RenameRelation(RenameStmt *stmt, CommandContext cmd);
extern void RenameRelationInternal(Oid myrelid,
! const char *newrelname, CommandContext cmd);
extern void find_composite_type_dependencies(Oid typeOid,
Relation origRelation,
*** a/src/include/commands/trigger.h
--- b/src/include/commands/trigger.h
***************
*** 13,18 ****
--- 13,19 ----
#ifndef TRIGGER_H
#define TRIGGER_H
+ #include "commands/cmdtrigger.h"
#include "nodes/execnodes.h"
#include "nodes/parsenodes.h"
***************
*** 115,121 **** extern Oid CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
extern void RemoveTriggerById(Oid trigOid);
extern Oid get_trigger_oid(Oid relid, const char *name, bool missing_ok);
! extern void renametrig(RenameStmt *stmt);
extern void EnableDisableTrigger(Relation rel, const char *tgname,
char fires_when, bool skip_system);
--- 116,122 ----
extern void RemoveTriggerById(Oid trigOid);
extern Oid get_trigger_oid(Oid relid, const char *name, bool missing_ok);
! extern void renametrig(RenameStmt *stmt, CommandContext cmd);
extern void EnableDisableTrigger(Relation rel, const char *tgname,
char fires_when, bool skip_system);
*** a/src/include/commands/typecmds.h
--- b/src/include/commands/typecmds.h
***************
*** 14,31 ****
#ifndef TYPECMDS_H
#define TYPECMDS_H
#include "nodes/parsenodes.h"
#define DEFAULT_TYPDELIM ','
! extern void DefineType(List *names, List *parameters);
extern void RemoveTypeById(Oid typeOid);
extern void DefineDomain(CreateDomainStmt *stmt);
extern void DefineEnum(CreateEnumStmt *stmt);
extern void DefineRange(CreateRangeStmt *stmt);
extern void AlterEnum(AlterEnumStmt *stmt);
! extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
extern void AlterDomainDefault(List *names, Node *defaultRaw);
--- 14,33 ----
#ifndef TYPECMDS_H
#define TYPECMDS_H
+ #include "commands/cmdtrigger.h"
+ #include "utils/lsyscache.h"
#include "nodes/parsenodes.h"
#define DEFAULT_TYPDELIM ','
! extern void DefineType(List *names, List *parameters, CommandContext cmd);
extern void RemoveTypeById(Oid typeOid);
extern void DefineDomain(CreateDomainStmt *stmt);
extern void DefineEnum(CreateEnumStmt *stmt);
extern void DefineRange(CreateRangeStmt *stmt);
extern void AlterEnum(AlterEnumStmt *stmt);
! extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist, CommandContext cmd);
extern Oid AssignTypeArrayOid(void);
extern void AlterDomainDefault(List *names, Node *defaultRaw);
***************
*** 37,50 **** extern void AlterDomainDropConstraint(List *names, const char *constrName,
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 */
--- 39,55 ----
extern List *GetDomainConstraints(Oid typeOid);
! extern void RenameType(RenameStmt *stmt, CommandContext cmd);
! extern void AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype,
! CommandContext cmd);
extern void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId,
bool hasDependEntry);
! extern void AlterTypeNamespace(List *names, const char *newschema,
! ObjectType objecttype, CommandContext cmd);
! extern Oid AlterTypeNamespace_oid(Oid typeOid, Oid nspOid, CommandContext cmd);
extern Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
bool isImplicitArray,
! bool errorOnTableType,
! CommandContext cmd);
#endif /* TYPECMDS_H */
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 290,295 **** typedef enum NodeTag
--- 290,296 ----
T_IndexStmt,
T_CreateFunctionStmt,
T_AlterFunctionStmt,
+ T_RemoveFuncStmt,
T_DoStmt,
T_RenameStmt,
T_RuleStmt,
***************
*** 310,316 **** typedef enum NodeTag
--- 311,319 ----
T_VariableShowStmt,
T_DiscardStmt,
T_CreateTrigStmt,
+ T_DropPropertyStmt,
T_CreatePLangStmt,
+ T_DropPLangStmt,
T_CreateRoleStmt,
T_AlterRoleStmt,
T_DropRoleStmt,
***************
*** 324,332 **** typedef enum NodeTag
--- 327,338 ----
T_AlterRoleSetStmt,
T_CreateConversionStmt,
T_CreateCastStmt,
+ T_DropCastStmt,
T_CreateOpClassStmt,
T_CreateOpFamilyStmt,
T_AlterOpFamilyStmt,
+ T_RemoveOpClassStmt,
+ T_RemoveOpFamilyStmt,
T_PrepareStmt,
T_ExecuteStmt,
T_DeallocateStmt,
***************
*** 345,352 **** typedef enum NodeTag
--- 351,360 ----
T_AlterTSConfigurationStmt,
T_CreateFdwStmt,
T_AlterFdwStmt,
+ T_DropFdwStmt,
T_CreateForeignServerStmt,
T_AlterForeignServerStmt,
+ T_DropForeignServerStmt,
T_CreateUserMappingStmt,
T_AlterUserMappingStmt,
T_DropUserMappingStmt,
***************
*** 356,361 **** typedef enum NodeTag
--- 364,372 ----
T_CreateExtensionStmt,
T_AlterExtensionStmt,
T_AlterExtensionContentsStmt,
+ T_CreateCmdTrigStmt,
+ T_DropCmdTrigStmt,
+ T_AlterCmdTrigStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 1107,1112 **** typedef enum ObjectType
--- 1107,1113 ----
OBJECT_AGGREGATE,
OBJECT_ATTRIBUTE, /* type's attribute, when distinct from column */
OBJECT_CAST,
+ OBJECT_CMDTRIGGER,
OBJECT_COLUMN,
OBJECT_CONSTRAINT,
OBJECT_COLLATION,
***************
*** 1729,1734 **** typedef struct CreateTrigStmt
--- 1730,1763 ----
} CreateTrigStmt;
/* ----------------------
+ * Create COMMAND TRIGGER Statement
+ * ----------------------
+ */
+ typedef struct CreateCmdTrigStmt
+ {
+ NodeTag type;
+ List *command; /* commands name */
+ char *trigname; /* TRIGGER's name */
+ /* timing uses the TRIGGER_TYPE bits defined in catalog/pg_trigger.h */
+ char timing; /* BEFORE, AFTER */
+ List *funcname; /* qual. name of function to call */
+ } CreateCmdTrigStmt;
+
+ /* ----------------------
+ * Alter COMMAND TRIGGER Statement
+ * ----------------------
+ */
+ typedef struct AlterCmdTrigStmt
+ {
+ NodeTag type;
+ char *command; /* command's name */
+ char *trigname; /* TRIGGER's name */
+ char *tgenabled; /* trigger's firing configuration WRT
+ * session_replication_role */
+ } AlterCmdTrigStmt;
+
+ /* ----------------------
+ * Create/Drop PROCEDURAL LANGUAGE Statements
* Create PROCEDURAL LANGUAGE Statements
* ----------------------
*/
***************
*** 1911,1916 **** typedef struct DropStmt
--- 1940,1976 ----
} DropStmt;
/* ----------------------
+ * Drop Rule|Trigger Statement
+ *
+ * In general this may be used for dropping any property of a relation;
+ * for example, someday soon we may have DROP ATTRIBUTE.
+ * ----------------------
+ */
+
+ typedef struct DropPropertyStmt
+ {
+ NodeTag type;
+ RangeVar *relation; /* owning relation */
+ char *property; /* name of rule, trigger, etc */
+ ObjectType removeType; /* OBJECT_RULE or OBJECT_TRIGGER */
+ DropBehavior behavior; /* RESTRICT or CASCADE behavior */
+ bool missing_ok; /* skip error if missing? */
+ } DropPropertyStmt;
+
+ /* ----------------------
+ * Drop Command Trigger Statement
+ * ----------------------
+ */
+ typedef struct DropCmdTrigStmt
+ {
+ NodeTag type;
+ List *command; /* command's name */
+ char *trigname; /* TRIGGER's name */
+ DropBehavior behavior; /* RESTRICT or CASCADE behavior */
+ bool missing_ok; /* skip error if missing? */
+ } DropCmdTrigStmt;
+
+ /* ----------------------
* Truncate Table Statement
* ----------------------
*/
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
***************
*** 81,86 **** PG_KEYWORD("coalesce", COALESCE, COL_NAME_KEYWORD)
--- 81,87 ----
PG_KEYWORD("collate", COLLATE, RESERVED_KEYWORD)
PG_KEYWORD("collation", COLLATION, UNRESERVED_KEYWORD)
PG_KEYWORD("column", COLUMN, RESERVED_KEYWORD)
+ PG_KEYWORD("command", COMMAND, UNRESERVED_KEYWORD)
PG_KEYWORD("comment", COMMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("comments", COMMENTS, UNRESERVED_KEYWORD)
PG_KEYWORD("commit", COMMIT, UNRESERVED_KEYWORD)
*** /dev/null
--- b/src/test/regress/expected/cmdtriggers.out
***************
*** 0 ****
--- 1,183 ----
+ --
+ -- COMMAND TRIGGERS
+ --
+ create or replace function snitch
+ (in tg_when text, in cmd_tag text, in objectid oid, in schemaname text, in objectname text)
+ returns void language plpgsql
+ as $$
+ begin
+ -- can't output the objectid here that would break pg_regress
+ raise notice 'snitch: % % % %', tg_when, cmd_tag, schemaname, objectname;
+ end;
+ $$;
+ create trigger snitch_before before any command execute procedure snitch();
+ create trigger snitch_after after any command execute procedure snitch();
+ alter trigger snitch_before on any command set disable;
+ alter trigger snitch_before on any command set enable;
+ create trigger snitch_some_more
+ after command create table, alter table, drop table,
+ create function, create collation,
+ alter operator, create domain, alter schema
+ execute procedure snitch();
+ create trigger snitch_some_even_more
+ before command create trigger, alter trigger, drop trigger,
+ create schema, drop schema,
+ create aggregate, alter collation, create operator,
+ alter domain, create type, alter type
+ execute procedure snitch();
+ create schema cmd;
+ NOTICE: snitch: BEFORE CREATE SCHEMA
+ NOTICE: snitch: BEFORE CREATE SCHEMA cmd
+ NOTICE: snitch: AFTER CREATE SCHEMA
+ create table cmd.foo(id bigserial primary key);
+ NOTICE: snitch: BEFORE CREATE TABLE
+ NOTICE: CREATE TABLE will create implicit sequence "foo_id_seq" for serial column "foo.id"
+ NOTICE: snitch: BEFORE CREATE SEQUENCE
+ NOTICE: snitch: AFTER CREATE SEQUENCE
+ NOTICE: snitch: BEFORE CREATE INDEX
+ NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo"
+ NOTICE: snitch: AFTER CREATE INDEX
+ NOTICE: snitch: BEFORE ALTER SEQUENCE
+ NOTICE: snitch: AFTER ALTER SEQUENCE
+ NOTICE: snitch: AFTER CREATE TABLE cmd foo
+ NOTICE: snitch: AFTER CREATE TABLE
+ create view cmd.v as select * from cmd.foo;
+ NOTICE: snitch: BEFORE CREATE VIEW
+ NOTICE: snitch: AFTER CREATE VIEW
+ alter table cmd.foo add column t text;
+ NOTICE: snitch: BEFORE ALTER TABLE
+ NOTICE: snitch: AFTER ALTER TABLE cmd foo
+ NOTICE: snitch: AFTER ALTER TABLE
+ set session_replication_role to replica;
+ create table cmd.bar();
+ reset session_replication_role;
+ create index idx_foo on cmd.foo(t);
+ NOTICE: snitch: BEFORE CREATE INDEX
+ NOTICE: snitch: AFTER CREATE INDEX
+ drop index cmd.idx_foo;
+ NOTICE: snitch: BEFORE DROP INDEX
+ NOTICE: snitch: AFTER DROP INDEX
+ create function cmd.fun(int) returns text language sql
+ as $$ select t from cmd.foo where id = $1; $$;
+ NOTICE: snitch: BEFORE CREATE FUNCTION
+ NOTICE: snitch: AFTER CREATE FUNCTION cmd fun
+ NOTICE: snitch: AFTER CREATE FUNCTION
+ alter function cmd.fun(int) strict;
+ NOTICE: snitch: BEFORE ALTER FUNCTION
+ NOTICE: snitch: AFTER ALTER FUNCTION
+ alter function cmd.fun(int) rename to notfun;
+ NOTICE: snitch: BEFORE ALTER FUNCTION
+ NOTICE: snitch: AFTER ALTER FUNCTION
+ drop function cmd.notfun(int);
+ NOTICE: snitch: BEFORE DROP FUNCTION
+ NOTICE: snitch: AFTER DROP FUNCTION
+ create function cmd.plus1(int) returns bigint language sql
+ as $$ select $1::bigint + 1; $$;
+ NOTICE: snitch: BEFORE CREATE FUNCTION
+ NOTICE: snitch: AFTER CREATE FUNCTION cmd plus1
+ NOTICE: snitch: AFTER CREATE FUNCTION
+ create operator cmd.+!(procedure = cmd.plus1, leftarg = int);
+ NOTICE: snitch: BEFORE CREATE OPERATOR
+ NOTICE: snitch: BEFORE CREATE OPERATOR cmd +!
+ NOTICE: snitch: AFTER CREATE OPERATOR
+ alter operator cmd.+!(int, NONE) set schema public;
+ NOTICE: snitch: BEFORE ALTER OPERATOR
+ NOTICE: snitch: AFTER ALTER OPERATOR public +!
+ NOTICE: snitch: AFTER ALTER OPERATOR
+ drop operator public.+!(int, NONE);
+ NOTICE: snitch: BEFORE DROP OPERATOR
+ NOTICE: snitch: AFTER DROP OPERATOR
+ create aggregate cmd.avg (float8)
+ (
+ sfunc = float8_accum,
+ stype = float8[],
+ finalfunc = float8_avg,
+ initcond = '{0,0,0}'
+ );
+ NOTICE: snitch: BEFORE CREATE AGGREGATE
+ NOTICE: snitch: BEFORE CREATE AGGREGATE cmd avg
+ NOTICE: snitch: AFTER CREATE AGGREGATE
+ alter aggregate cmd.avg(float8) set schema public;
+ NOTICE: snitch: BEFORE ALTER AGGREGATE
+ NOTICE: snitch: AFTER ALTER AGGREGATE
+ drop aggregate public.avg(float8);
+ NOTICE: snitch: BEFORE DROP AGGREGATE
+ NOTICE: snitch: AFTER DROP AGGREGATE
+ create collation cmd.french (LOCALE = 'fr_FR');
+ NOTICE: snitch: BEFORE CREATE COLLATION
+ NOTICE: snitch: AFTER CREATE COLLATION cmd french
+ NOTICE: snitch: AFTER CREATE COLLATION
+ alter collation cmd.french rename to francais;
+ NOTICE: snitch: BEFORE ALTER COLLATION
+ NOTICE: snitch: BEFORE ALTER COLLATION cmd french
+ NOTICE: snitch: AFTER ALTER COLLATION
+ create type cmd.compfoo AS (f1 int, f2 text);
+ NOTICE: snitch: BEFORE CREATE TYPE
+ NOTICE: snitch: BEFORE CREATE TYPE cmd compfoo
+ NOTICE: snitch: AFTER CREATE TYPE
+ alter type cmd.compfoo add attribute f3 text;
+ NOTICE: snitch: BEFORE ALTER TYPE
+ NOTICE: snitch: BEFORE ALTER TYPE cmd compfoo
+ NOTICE: snitch: AFTER ALTER TYPE
+ drop type cmd.compfoo;
+ NOTICE: snitch: BEFORE DROP TYPE
+ NOTICE: snitch: AFTER DROP TYPE
+ create type cmd.bug_status as enum ('new', 'open', 'closed');
+ NOTICE: snitch: BEFORE CREATE TYPE
+ NOTICE: snitch: BEFORE CREATE TYPE cmd bug_status
+ NOTICE: snitch: AFTER CREATE TYPE
+ alter type cmd.bug_status add value 'wontfix';
+ NOTICE: snitch: BEFORE ALTER TYPE
+ NOTICE: snitch: AFTER ALTER TYPE
+ create domain cmd.us_postal_code as text check(value ~ '^\d{5}$' or value ~ '^\d{5}-\d{4}$');
+ NOTICE: snitch: BEFORE CREATE DOMAIN
+ NOTICE: snitch: AFTER CREATE DOMAIN cmd us_postal_code
+ NOTICE: snitch: AFTER CREATE DOMAIN
+ alter domain cmd.us_postal_code set not null;
+ NOTICE: snitch: BEFORE ALTER DOMAIN
+ NOTICE: snitch: AFTER ALTER DOMAIN
+ create function cmd.trigfunc() returns trigger language plpgsql as
+ $$ begin raise notice 'trigfunc'; end;$$;
+ NOTICE: snitch: BEFORE CREATE FUNCTION
+ NOTICE: snitch: AFTER CREATE FUNCTION cmd trigfunc
+ NOTICE: snitch: AFTER CREATE FUNCTION
+ create trigger footg before update on cmd.foo for each row execute procedure cmd.trigfunc();
+ NOTICE: snitch: BEFORE CREATE TRIGGER
+ NOTICE: snitch: BEFORE CREATE TRIGGER cmd footg
+ NOTICE: snitch: AFTER CREATE TRIGGER
+ alter trigger footg on cmd.foo rename to foo_trigger;
+ NOTICE: snitch: BEFORE ALTER TRIGGER
+ NOTICE: snitch: BEFORE ALTER TRIGGER cmd footg
+ NOTICE: snitch: AFTER ALTER TRIGGER
+ drop trigger foo_trigger on cmd.foo;
+ NOTICE: snitch: BEFORE DROP TRIGGER
+ NOTICE: snitch: BEFORE DROP TRIGGER foo_trigger
+ NOTICE: snitch: AFTER DROP TRIGGER
+ alter schema cmd rename to cmd1;
+ NOTICE: snitch: BEFORE ALTER SCHEMA
+ NOTICE: snitch: AFTER ALTER SCHEMA cmd1
+ NOTICE: snitch: AFTER ALTER SCHEMA
+ drop schema cmd1 cascade;
+ NOTICE: snitch: BEFORE DROP SCHEMA
+ NOTICE: snitch: BEFORE DROP SCHEMA cmd1
+ NOTICE: drop cascades to 8 other objects
+ DETAIL: drop cascades to table cmd1.foo
+ drop cascades to view cmd1.v
+ drop cascades to table cmd1.bar
+ drop cascades to function cmd1.plus1(integer)
+ drop cascades to collation francais
+ drop cascades to type cmd1.bug_status
+ drop cascades to type cmd1.us_postal_code
+ drop cascades to function cmd1.trigfunc()
+ NOTICE: snitch: AFTER DROP SCHEMA
+ drop trigger snitch_before on any command;
+ drop trigger snitch_after on any command;
+ drop trigger snitch_some_more
+ on command create table, alter table, drop table,
+ create function, create collation,
+ alter operator, create domain, alter schema;
+ drop trigger snitch_some_even_more
+ on command create trigger, alter trigger, drop trigger,
+ create schema, drop schema,
+ create aggregate, alter collation, create operator,
+ alter domain, create type, alter type;
*** a/src/test/regress/expected/sanity_check.out
--- b/src/test/regress/expected/sanity_check.out
***************
*** 93,98 **** SELECT relname, relhasindex
--- 93,99 ----
pg_authid | t
pg_cast | t
pg_class | t
+ pg_cmdtrigger | t
pg_collation | t
pg_constraint | t
pg_conversion | t
***************
*** 164,170 **** SELECT relname, relhasindex
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
! (153 rows)
--
-- another sanity check: every system catalog that has OIDs should have
--- 165,171 ----
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
! (154 rows)
--
-- another sanity check: every system catalog that has OIDs should have
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
***************
*** 61,66 **** test: create_aggregate
--- 61,67 ----
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,105 ----
+ --
+ -- COMMAND TRIGGERS
+ --
+ create or replace function snitch
+ (in tg_when text, in cmd_tag text, in objectid oid, in schemaname text, in objectname text)
+ returns void language plpgsql
+ as $$
+ begin
+ -- can't output the objectid here that would break pg_regress
+ raise notice 'snitch: % % % %', tg_when, cmd_tag, schemaname, objectname;
+ end;
+ $$;
+
+ create trigger snitch_before before any command execute procedure snitch();
+ create trigger snitch_after after any command execute procedure snitch();
+
+ alter trigger snitch_before on any command set disable;
+ alter trigger snitch_before on any command set enable;
+
+ create trigger snitch_some_more
+ after command create table, alter table, drop table,
+ create function, create collation,
+ alter operator, create domain, alter schema
+ execute procedure snitch();
+
+ create trigger snitch_some_even_more
+ before command create trigger, alter trigger, drop trigger,
+ create schema, drop schema,
+ create aggregate, alter collation, create operator,
+ alter domain, create type, alter type
+ execute procedure snitch();
+
+ create schema cmd;
+ create table cmd.foo(id bigserial primary key);
+ create view cmd.v as select * from cmd.foo;
+ alter table cmd.foo add column t text;
+
+ set session_replication_role to replica;
+ create table cmd.bar();
+ reset session_replication_role;
+
+ create index idx_foo on cmd.foo(t);
+ drop index cmd.idx_foo;
+
+ create function cmd.fun(int) returns text language sql
+ as $$ select t from cmd.foo where id = $1; $$;
+
+ alter function cmd.fun(int) strict;
+ alter function cmd.fun(int) rename to notfun;
+ drop function cmd.notfun(int);
+
+ create function cmd.plus1(int) returns bigint language sql
+ as $$ select $1::bigint + 1; $$;
+
+ create operator cmd.+!(procedure = cmd.plus1, leftarg = int);
+ alter operator cmd.+!(int, NONE) set schema public;
+ drop operator public.+!(int, NONE);
+
+ create aggregate cmd.avg (float8)
+ (
+ sfunc = float8_accum,
+ stype = float8[],
+ finalfunc = float8_avg,
+ initcond = '{0,0,0}'
+ );
+ alter aggregate cmd.avg(float8) set schema public;
+ drop aggregate public.avg(float8);
+
+ create collation cmd.french (LOCALE = 'fr_FR');
+ alter collation cmd.french rename to francais;
+
+ create type cmd.compfoo AS (f1 int, f2 text);
+ alter type cmd.compfoo add attribute f3 text;
+ drop type cmd.compfoo;
+
+ create type cmd.bug_status as enum ('new', 'open', 'closed');
+ alter type cmd.bug_status add value 'wontfix';
+
+ create domain cmd.us_postal_code as text check(value ~ '^\d{5}$' or value ~ '^\d{5}-\d{4}$');
+ alter domain cmd.us_postal_code set not null;
+
+ create function cmd.trigfunc() returns trigger language plpgsql as
+ $$ begin raise notice 'trigfunc'; end;$$;
+
+ create trigger footg before update on cmd.foo for each row execute procedure cmd.trigfunc();
+ alter trigger footg on cmd.foo rename to foo_trigger;
+ drop trigger foo_trigger on cmd.foo;
+
+ alter schema cmd rename to cmd1;
+
+ drop schema cmd1 cascade;
+
+ drop trigger snitch_before on any command;
+ drop trigger snitch_after on any command;
+ drop trigger snitch_some_more
+ on command create table, alter table, drop table,
+ create function, create collation,
+ alter operator, create domain, alter schema;
+
+ drop trigger snitch_some_even_more
+ on command create trigger, alter trigger, drop trigger,
+ create schema, drop schema,
+ create aggregate, alter collation, create operator,
+ alter domain, create type, alter type;
+
*** a/src/test/regress/sql/triggers.sql
--- b/src/test/regress/sql/triggers.sql
***************
*** 962,968 **** SELECT * FROM city_view;
DROP TABLE city_table CASCADE;
DROP TABLE country_table;
-
-- Test pg_trigger_depth()
create table depth_a (id int not null primary key);
--- 962,967 ----