use these instead of its previous hack of changing pg_class.reltriggers.
Documentation is lacking, will add that later.
Patch by Satoshi Nagayasu, review and some extra work by Tom Lane.
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.168 2005/08/22 19:40:09 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.169 2005/08/23 22:40:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
char *tablespacename);
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace);
+static void ATExecEnableDisableTrigger(Relation rel, char *trigname,
+ bool enable, bool skip_system);
static void copy_relation_data(Relation rel, SMgrRelation dst);
static void update_ri_trigger_args(Oid relid,
const char *oldname,
ATPrepSetTableSpace(tab, rel, cmd->name);
pass = AT_PASS_MISC; /* doesn't actually matter */
break;
+ case AT_EnableTrig: /* ENABLE TRIGGER variants */
+ case AT_EnableTrigAll:
+ case AT_EnableTrigUser:
+ case AT_DisableTrig: /* DISABLE TRIGGER variants */
+ case AT_DisableTrigAll:
+ case AT_DisableTrigUser:
+ ATSimplePermissions(rel, false);
+ /* These commands never recurse */
+ /* No command-specific prep needed */
+ pass = AT_PASS_MISC;
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
* Nothing to do here; Phase 3 does the work
*/
break;
+ case AT_EnableTrig: /* ENABLE TRIGGER name */
+ ATExecEnableDisableTrigger(rel, cmd->name, true, false);
+ break;
+ case AT_DisableTrig: /* DISABLE TRIGGER name */
+ ATExecEnableDisableTrigger(rel, cmd->name, false, false);
+ break;
+ case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
+ ATExecEnableDisableTrigger(rel, NULL, true, false);
+ break;
+ case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
+ ATExecEnableDisableTrigger(rel, NULL, false, false);
+ break;
+ case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
+ ATExecEnableDisableTrigger(rel, NULL, true, true);
+ break;
+ case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
+ ATExecEnableDisableTrigger(rel, NULL, false, true);
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
}
/*
+ * ALTER TABLE ENABLE/DISABLE TRIGGER
+ *
+ * We just pass this off to trigger.c.
+ */
+static void
+ATExecEnableDisableTrigger(Relation rel, char *trigname,
+ bool enable, bool skip_system)
+{
+ EnableDisableTrigger(rel, trigname, enable, skip_system);
+}
+
+/*
* ALTER TABLE CREATE TOAST TABLE
*
* Note: this is also invoked from outside this module; in such cases we
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.192 2005/08/20 00:39:54 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.193 2005/08/23 22:40:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
heap_close(targetrel, NoLock);
}
+
+/*
+ * EnableDisableTrigger()
+ *
+ * Called by ALTER TABLE ENABLE/DISABLE TRIGGER
+ * to change 'tgenabled' flag for the specified trigger(s)
+ *
+ * rel: relation to process (caller must hold suitable lock on it)
+ * tgname: trigger to process, or NULL to scan all triggers
+ * enable: new value for tgenabled flag
+ * skip_system: if true, skip "system" triggers (constraint triggers)
+ *
+ * Caller should have checked permissions for the table; here we also
+ * enforce that superuser privilege is required to alter the state of
+ * system triggers
+ */
+void
+EnableDisableTrigger(Relation rel, const char *tgname,
+ bool enable, bool skip_system)
+{
+ Relation tgrel;
+ int nkeys;
+ ScanKeyData keys[2];
+ SysScanDesc tgscan;
+ HeapTuple tuple;
+ bool found;
+ bool changed;
+
+ /* Scan the relevant entries in pg_triggers */
+ tgrel = heap_open(TriggerRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&keys[0],
+ Anum_pg_trigger_tgrelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationGetRelid(rel)));
+ if (tgname)
+ {
+ ScanKeyInit(&keys[1],
+ Anum_pg_trigger_tgname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(tgname));
+ nkeys = 2;
+ }
+ else
+ nkeys = 1;
+
+ tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
+ SnapshotNow, nkeys, keys);
+
+ found = changed = false;
+
+ while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
+ {
+ Form_pg_trigger oldtrig = (Form_pg_trigger) GETSTRUCT(tuple);
+
+ if (oldtrig->tgisconstraint)
+ {
+ /* system trigger ... ok to process? */
+ if (skip_system)
+ continue;
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied: \"%s\" is a system trigger",
+ NameStr(oldtrig->tgname))));
+ }
+
+ found = true;
+
+ if (oldtrig->tgenabled != enable)
+ {
+ /* need to change this one ... make a copy to scribble on */
+ HeapTuple newtup = heap_copytuple(tuple);
+ Form_pg_trigger newtrig = (Form_pg_trigger) GETSTRUCT(newtup);
+
+ newtrig->tgenabled = enable;
+
+ simple_heap_update(tgrel, &newtup->t_self, newtup);
+
+ /* Keep catalog indexes current */
+ CatalogUpdateIndexes(tgrel, newtup);
+
+ heap_freetuple(newtup);
+
+ changed = true;
+ }
+ }
+
+ systable_endscan(tgscan);
+
+ heap_close(tgrel, RowExclusiveLock);
+
+ if (tgname && !found)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("trigger \"%s\" for table \"%s\" does not exist",
+ tgname, RelationGetRelationName(rel))));
+
+ /*
+ * If we changed anything, broadcast a SI inval message to force each
+ * backend (including our own!) to rebuild relation's relcache entry.
+ * Otherwise they will fail to apply the change promptly.
+ */
+ if (changed)
+ CacheInvalidateRelcache(rel);
+}
+
+
/*
* Build trigger data to attach to the given relcache entry.
*
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.507 2005/08/01 20:31:09 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.508 2005/08/23 22:40:20 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS
- DESC DISTINCT DO DOMAIN_P DOUBLE_P DROP
+ DESC DISABLE_P DISTINCT DO DOMAIN_P DOUBLE_P DROP
- EACH ELSE ENCODING ENCRYPTED END_P ESCAPE EXCEPT EXCLUDING
+ EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ESCAPE EXCEPT EXCLUDING
EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
FALSE_P FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD
n->name = NULL;
$$ = (Node *)n;
}
+ /* ALTER TABLE <name> ENABLE TRIGGER <trig> */
+ | ENABLE_P TRIGGER name
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_EnableTrig;
+ n->name = $3;
+ $$ = (Node *)n;
+ }
+ /* ALTER TABLE <name> ENABLE TRIGGER ALL */
+ | ENABLE_P TRIGGER ALL
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_EnableTrigAll;
+ $$ = (Node *)n;
+ }
+ /* ALTER TABLE <name> ENABLE TRIGGER USER */
+ | ENABLE_P TRIGGER USER
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_EnableTrigUser;
+ $$ = (Node *)n;
+ }
+ /* ALTER TABLE <name> DISABLE TRIGGER <trig> */
+ | DISABLE_P TRIGGER name
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_DisableTrig;
+ n->name = $3;
+ $$ = (Node *)n;
+ }
+ /* ALTER TABLE <name> DISABLE TRIGGER ALL */
+ | DISABLE_P TRIGGER ALL
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_DisableTrigAll;
+ $$ = (Node *)n;
+ }
+ /* ALTER TABLE <name> DISABLE TRIGGER USER */
+ | DISABLE_P TRIGGER USER
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_DisableTrigUser;
+ $$ = (Node *)n;
+ }
| alter_rel_cmd
{
$$ = $1;
| DELETE_P
| DELIMITER
| DELIMITERS
+ | DISABLE_P
| DOMAIN_P
| DOUBLE_P
| DROP
| EACH
+ | ENABLE_P
| ENCODING
| ENCRYPTED
| ESCAPE
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.164 2005/07/31 17:19:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.165 2005/08/23 22:40:27 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{"delimiter", DELIMITER},
{"delimiters", DELIMITERS},
{"desc", DESC},
+ {"disable", DISABLE_P},
{"distinct", DISTINCT},
{"do", DO},
{"domain", DOMAIN_P},
{"drop", DROP},
{"each", EACH},
{"else", ELSE},
+ {"enable", ENABLE_P},
{"encoding", ENCODING},
{"encrypted", ENCRYPTED},
{"end", END_P},
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.113 2005/08/22 19:40:37 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.114 2005/08/23 22:40:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
if (!ropt->dataOnly || !ropt->disable_triggers)
return;
+ ahlog(AH, 1, "disabling triggers for %s\n", te->tag);
+
/*
* Become superuser if possible, since they are the only ones who can
- * update pg_class. If -S was not given, assume the initial user
- * identity is a superuser.
+ * disable constraint triggers. If -S was not given, assume the initial
+ * user identity is a superuser. (XXX would it be better to become the
+ * table owner?)
*/
_becomeUser(AH, ropt->superuser);
- ahlog(AH, 1, "disabling triggers\n");
-
/*
- * Disable them. This is a hack. Needs to be done via an appropriate
- * 'SET' command when one is available.
+ * Disable them.
*/
- ahprintf(AH, "-- Disable triggers\n");
+ _selectOutputSchema(AH, te->namespace);
- /*
- * Just update the AFFECTED table, if known. Otherwise update all
- * non-system tables.
- */
- if (te && te->tag && strlen(te->tag) > 0)
- ahprintf(AH, "UPDATE pg_catalog.pg_class SET reltriggers = 0 "
- "WHERE oid = '%s'::pg_catalog.regclass;\n\n",
- fmtId(te->tag));
- else
- ahprintf(AH, "UPDATE pg_catalog.pg_class SET reltriggers = 0 FROM pg_catalog.pg_namespace "
- "WHERE relnamespace = pg_namespace.oid AND nspname !~ '^pg_';\n\n");
+ ahprintf(AH, "ALTER TABLE %s DISABLE TRIGGER ALL;\n\n",
+ fmtId(te->tag));
}
static void
if (!ropt->dataOnly || !ropt->disable_triggers)
return;
+ ahlog(AH, 1, "enabling triggers for %s\n", te->tag);
+
/*
* Become superuser if possible, since they are the only ones who can
- * update pg_class. If -S was not given, assume the initial user
- * identity is a superuser.
+ * disable constraint triggers. If -S was not given, assume the initial
+ * user identity is a superuser. (XXX would it be better to become the
+ * table owner?)
*/
_becomeUser(AH, ropt->superuser);
- ahlog(AH, 1, "enabling triggers\n");
-
/*
- * Enable them. This is a hack. Needs to be done via an appropriate
- * 'SET' command when one is available.
+ * Enable them.
*/
- ahprintf(AH, "-- Enable triggers\n");
+ _selectOutputSchema(AH, te->namespace);
- /*
- * Just update the AFFECTED table, if known. Otherwise update all
- * non-system tables.
- */
- if (te && te->tag && strlen(te->tag) > 0)
- ahprintf(AH, "UPDATE pg_catalog.pg_class SET reltriggers = "
- "(SELECT pg_catalog.count(*) FROM pg_catalog.pg_trigger where pg_class.oid = tgrelid) "
- "WHERE oid = '%s'::pg_catalog.regclass;\n\n",
- fmtId(te->tag));
- else
- ahprintf(AH, "UPDATE pg_catalog.pg_class SET reltriggers = "
- "(SELECT pg_catalog.count(*) FROM pg_catalog.pg_trigger where pg_class.oid = tgrelid) "
- "FROM pg_catalog.pg_namespace "
- "WHERE relnamespace = pg_namespace.oid AND nspname !~ '^pg_';\n\n");
+ ahprintf(AH, "ALTER TABLE %s ENABLE TRIGGER ALL;\n\n",
+ fmtId(te->tag));
}
/*
* by PostgreSQL
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.418 2005/08/15 21:50:15 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.419 2005/08/23 22:40:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
i_tgconstrname,
i_tgconstrrelid,
i_tgconstrrelname,
+ i_tgenabled,
i_tgdeferrable,
i_tginitdeferred;
int ntups;
appendPQExpBuffer(query,
"SELECT tgname, "
"tgfoid::pg_catalog.regproc as tgfname, "
- "tgtype, tgnargs, tgargs, "
+ "tgtype, tgnargs, tgargs, tgenabled, "
"tgisconstraint, tgconstrname, tgdeferrable, "
"tgconstrrelid, tginitdeferred, tableoid, oid, "
"tgconstrrelid::pg_catalog.regclass as tgconstrrelname "
{
appendPQExpBuffer(query,
"SELECT tgname, tgfoid::regproc as tgfname, "
- "tgtype, tgnargs, tgargs, "
+ "tgtype, tgnargs, tgargs, tgenabled, "
"tgisconstraint, tgconstrname, tgdeferrable, "
"tgconstrrelid, tginitdeferred, tableoid, oid, "
"(select relname from pg_class where oid = tgconstrrelid) "
{
appendPQExpBuffer(query,
"SELECT tgname, tgfoid::regproc as tgfname, "
- "tgtype, tgnargs, tgargs, "
+ "tgtype, tgnargs, tgargs, tgenabled, "
"tgisconstraint, tgconstrname, tgdeferrable, "
"tgconstrrelid, tginitdeferred, "
"(SELECT oid FROM pg_class WHERE relname = 'pg_trigger') AS tableoid, "
i_tgconstrname = PQfnumber(res, "tgconstrname");
i_tgconstrrelid = PQfnumber(res, "tgconstrrelid");
i_tgconstrrelname = PQfnumber(res, "tgconstrrelname");
+ i_tgenabled = PQfnumber(res, "tgenabled");
i_tgdeferrable = PQfnumber(res, "tgdeferrable");
i_tginitdeferred = PQfnumber(res, "tginitdeferred");
tginfo[j].tgnargs = atoi(PQgetvalue(res, j, i_tgnargs));
tginfo[j].tgargs = strdup(PQgetvalue(res, j, i_tgargs));
tginfo[j].tgisconstraint = *(PQgetvalue(res, j, i_tgisconstraint)) == 't';
+ tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled)) == 't';
tginfo[j].tgdeferrable = *(PQgetvalue(res, j, i_tgdeferrable)) == 't';
tginfo[j].tginitdeferred = *(PQgetvalue(res, j, i_tginitdeferred)) == 't';
}
appendPQExpBuffer(query, ");\n");
+ if (!tginfo->tgenabled)
+ {
+ appendPQExpBuffer(query, "\nALTER TABLE %s ",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(query, "DISABLE TRIGGER %s;\n",
+ fmtId(tginfo->dobj.name));
+ }
+
ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
tginfo->dobj.name,
tbinfo->dobj.namespace->dobj.name,
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.119 2005/08/15 21:50:15 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.120 2005/08/23 22:40:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
char *tgconstrname;
Oid tgconstrrelid;
char *tgconstrrelname;
+ bool tgenabled;
bool tgdeferrable;
bool tginitdeferred;
} TriggerInfo;
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.54 2005/05/30 07:20:58 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.55 2005/08/23 22:40:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern void renametrig(Oid relid, const char *oldname, const char *newname);
+extern void EnableDisableTrigger(Relation rel, const char *tgname,
+ bool enable, bool skip_system);
+
extern void RelationBuildTriggers(Relation relation);
extern TriggerDesc *CopyTriggerDesc(TriggerDesc *trigdesc);
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.289 2005/08/01 20:31:15 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.290 2005/08/23 22:40:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
AT_ClusterOn, /* CLUSTER ON */
AT_DropCluster, /* SET WITHOUT CLUSTER */
AT_DropOids, /* SET WITHOUT OIDS */
- AT_SetTableSpace /* SET TABLESPACE */
+ AT_SetTableSpace, /* SET TABLESPACE */
+ AT_EnableTrig, /* ENABLE TRIGGER name */
+ AT_DisableTrig, /* DISABLE TRIGGER name */
+ AT_EnableTrigAll, /* ENABLE TRIGGER ALL */
+ AT_DisableTrigAll, /* DISABLE TRIGGER ALL */
+ AT_EnableTrigUser, /* ENABLE TRIGGER USER */
+ AT_DisableTrigUser /* DISABLE TRIGGER USER */
} AlterTableType;
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
{
NodeTag type;
AlterTableType subtype; /* Type of table alteration to apply */
- char *name; /* column or constraint name to act on, or
- * new owner or tablespace */
+ char *name; /* column, constraint, or trigger to act on,
+ * or new owner or tablespace */
Node *def; /* definition of new column, column type,
* index, or constraint */
Node *transform; /* transformation expr for ALTER TYPE */