aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2001-11-12 00:46:36 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2001-11-12 00:46:36 +0000
commitf14fdad85845b5701d6cda0107a9dd196f81085b (patch)
treeaec5511930b85a96ad270507e90bf5773648f9e7
parent8bfc437301d5ac1a209349b4c9e466b798e5b90a (diff)
downloadpostgresql-f14fdad85845b5701d6cda0107a9dd196f81085b.tar.gz
postgresql-f14fdad85845b5701d6cda0107a9dd196f81085b.zip
Make ALTER TABLE RENAME update foreign-key trigger arguments correctly.
Brent Verner, with review and kibitzing from Tom Lane.
-rw-r--r--src/backend/commands/rename.c284
-rw-r--r--src/backend/utils/adt/ri_triggers.c12
-rw-r--r--src/include/commands/trigger.h21
3 files changed, 304 insertions, 13 deletions
diff --git a/src/backend/commands/rename.c b/src/backend/commands/rename.c
index d319161b9ae..c12d714f218 100644
--- a/src/backend/commands/rename.c
+++ b/src/backend/commands/rename.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/Attic/rename.c,v 1.61 2001/11/05 17:46:24 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/rename.c,v 1.62 2001/11/12 00:46:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -16,25 +16,43 @@
#include <errno.h>
+#include "access/genam.h"
#include "access/heapam.h"
+#include "access/itup.h"
#include "catalog/catname.h"
#include "catalog/pg_index.h"
+#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "catalog/heap.h"
#include "catalog/indexing.h"
#include "catalog/catalog.h"
#include "commands/rename.h"
+#include "commands/trigger.h"
#include "miscadmin.h"
#include "storage/smgr.h"
#include "optimizer/prep.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteSupport.h"
#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
#include "utils/relcache.h"
#include "utils/syscache.h"
#include "utils/temprel.h"
+#define RI_TRIGGER_PK 1 /* is a trigger on the PK relation */
+#define RI_TRIGGER_FK 2 /* is a trigger on the FK relation */
+#define RI_TRIGGER_NONE 0 /* is not an RI trigger function */
+
+static int ri_trigger_type(Oid tgfoid);
+static void update_ri_trigger_args(Oid relid,
+ const char* oldname,
+ const char* newname,
+ bool fk_scan,
+ bool update_relname);
+
+
/*
* renameatt - changes the name of a attribute in a relation
*
@@ -226,6 +244,22 @@ renameatt(char *relname,
freeList(indexoidlist);
heap_close(attrelation, RowExclusiveLock);
+
+ /*
+ * Update att name in any RI triggers associated with the relation.
+ */
+ if (targetrelation->rd_rel->reltriggers > 0)
+ {
+ /* update tgargs column reference where att is primary key */
+ update_ri_trigger_args(RelationGetRelid(targetrelation),
+ oldattname, newattname,
+ false, false);
+ /* update tgargs column reference where att is foreign key */
+ update_ri_trigger_args(RelationGetRelid(targetrelation),
+ oldattname, newattname,
+ true, false);
+ }
+
heap_close(targetrelation, NoLock); /* close rel but keep lock! */
}
@@ -240,6 +274,7 @@ renamerel(const char *oldrelname, const char *newrelname)
HeapTuple reltup;
Oid reloid;
char relkind;
+ bool relhastriggers;
Relation irelations[Num_pg_class_indices];
if (!allowSystemTableMods && IsSystemRelationName(oldrelname))
@@ -265,6 +300,7 @@ renamerel(const char *oldrelname, const char *newrelname)
reloid = RelationGetRelid(targetrelation);
relkind = targetrelation->rd_rel->relkind;
+ relhastriggers = (targetrelation->rd_rel->reltriggers > 0);
/*
* Close rel, but keep exclusive lock!
@@ -331,4 +367,250 @@ renamerel(const char *oldrelname, const char *newrelname)
newrulename = MakeRetrieveViewRuleName(newrelname);
RenameRewriteRule(oldrulename, newrulename);
}
+
+ /*
+ * Update rel name in any RI triggers associated with the relation.
+ */
+ if (relhastriggers)
+ {
+ /* update tgargs where relname is primary key */
+ update_ri_trigger_args(reloid,
+ oldrelname, newrelname,
+ false, true);
+ /* update tgargs where relname is foreign key */
+ update_ri_trigger_args(reloid,
+ oldrelname, newrelname,
+ true, true);
+ }
+}
+
+/*
+ * Given a trigger function OID, determine whether it is an RI trigger,
+ * and if so whether it is attached to PK or FK relation.
+ *
+ * XXX this probably doesn't belong here; should be exported by
+ * ri_triggers.c
+ */
+static int
+ri_trigger_type(Oid tgfoid)
+{
+ switch (tgfoid)
+ {
+ case F_RI_FKEY_CASCADE_DEL:
+ case F_RI_FKEY_CASCADE_UPD:
+ case F_RI_FKEY_RESTRICT_DEL:
+ case F_RI_FKEY_RESTRICT_UPD:
+ case F_RI_FKEY_SETNULL_DEL:
+ case F_RI_FKEY_SETNULL_UPD:
+ case F_RI_FKEY_SETDEFAULT_DEL:
+ case F_RI_FKEY_SETDEFAULT_UPD:
+ case F_RI_FKEY_NOACTION_DEL:
+ case F_RI_FKEY_NOACTION_UPD:
+ return RI_TRIGGER_PK;
+
+ case F_RI_FKEY_CHECK_INS:
+ case F_RI_FKEY_CHECK_UPD:
+ return RI_TRIGGER_FK;
+ }
+
+ return RI_TRIGGER_NONE;
+}
+
+/*
+ * Scan pg_trigger for RI triggers that are on the specified relation
+ * (if fk_scan is false) or have it as the tgconstrrel (if fk_scan
+ * is true). Update RI trigger args fields matching oldname to contain
+ * newname instead. If update_relname is true, examine the relname
+ * fields; otherwise examine the attname fields.
+ */
+static void
+update_ri_trigger_args(Oid relid,
+ const char* oldname,
+ const char* newname,
+ bool fk_scan,
+ bool update_relname)
+{
+ Relation tgrel;
+ Relation irel;
+ ScanKeyData skey[1];
+ IndexScanDesc idxtgscan;
+ RetrieveIndexResult idxres;
+ Datum values[Natts_pg_trigger];
+ char nulls[Natts_pg_trigger];
+ char replaces[Natts_pg_trigger];
+
+ tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
+ if (fk_scan)
+ irel = index_openr(TriggerConstrRelidIndex);
+ else
+ irel = index_openr(TriggerRelidIndex);
+
+ ScanKeyEntryInitialize(&skey[0], 0x0,
+ 1, /* always column 1 of index */
+ F_OIDEQ,
+ ObjectIdGetDatum(relid));
+ idxtgscan = index_beginscan(irel, false, 1, skey);
+
+ while ((idxres = index_getnext(idxtgscan, ForwardScanDirection)) != NULL)
+ {
+ HeapTupleData tupledata;
+ Buffer buffer;
+ HeapTuple tuple;
+ Form_pg_trigger pg_trigger;
+ bytea* val;
+ bytea* newtgargs;
+ bool isnull;
+ int tg_type;
+ bool examine_pk;
+ bool changed;
+ int tgnargs;
+ int i;
+ int newlen;
+ const char *arga[RI_MAX_ARGUMENTS];
+ const char *argp;
+
+ tupledata.t_self = idxres->heap_iptr;
+ heap_fetch(tgrel, SnapshotNow, &tupledata, &buffer, idxtgscan);
+ pfree(idxres);
+ if (!tupledata.t_data)
+ continue;
+ tuple = &tupledata;
+ pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
+ tg_type = ri_trigger_type(pg_trigger->tgfoid);
+ if (tg_type == RI_TRIGGER_NONE)
+ {
+ /* Not an RI trigger, forget it */
+ ReleaseBuffer(buffer);
+ continue;
+ }
+
+ /*
+ * It is an RI trigger, so parse the tgargs bytea.
+ *
+ * NB: we assume the field will never be compressed or moved
+ * out of line; so does trigger.c ...
+ */
+ tgnargs = pg_trigger->tgnargs;
+ val = (bytea *) fastgetattr(tuple,
+ Anum_pg_trigger_tgargs,
+ tgrel->rd_att, &isnull);
+ if (isnull || tgnargs < RI_FIRST_ATTNAME_ARGNO ||
+ tgnargs > RI_MAX_ARGUMENTS)
+ {
+ /* This probably shouldn't happen, but ignore busted triggers */
+ ReleaseBuffer(buffer);
+ continue;
+ }
+ argp = (const char *) VARDATA(val);
+ for (i = 0; i < tgnargs; i++)
+ {
+ arga[i] = argp;
+ argp += strlen(argp)+1;
+ }
+
+ /*
+ * Figure out which item(s) to look at. If the trigger is
+ * primary-key type and attached to my rel, I should look at
+ * the PK fields; if it is foreign-key type and attached to my
+ * rel, I should look at the FK fields. But the opposite rule
+ * holds when examining triggers found by tgconstrrel search.
+ */
+ examine_pk = (tg_type == RI_TRIGGER_PK) == (!fk_scan);
+
+ changed = false;
+ if (update_relname)
+ {
+ /* Change the relname if needed */
+ i = examine_pk ? RI_PK_RELNAME_ARGNO : RI_FK_RELNAME_ARGNO;
+ if (strcmp(arga[i], oldname) == 0)
+ {
+ arga[i] = newname;
+ changed = true;
+ }
+ }
+ else
+ {
+ /* Change attname(s) if needed */
+ i = examine_pk ? RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_PK_IDX :
+ RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_FK_IDX;
+ for (; i < tgnargs; i += 2)
+ {
+ if (strcmp(arga[i], oldname) == 0)
+ {
+ arga[i] = newname;
+ changed = true;
+ }
+ }
+ }
+
+ if (!changed)
+ {
+ /* Don't need to update this tuple */
+ ReleaseBuffer(buffer);
+ continue;
+ }
+
+ /*
+ * Construct modified tgargs bytea.
+ */
+ newlen = VARHDRSZ;
+ for (i = 0; i < tgnargs; i++)
+ newlen += strlen(arga[i]) + 1;
+ newtgargs = (bytea *) palloc(newlen);
+ VARATT_SIZEP(newtgargs) = newlen;
+ newlen = VARHDRSZ;
+ for (i = 0; i < tgnargs; i++)
+ {
+ strcpy(((char *) newtgargs) + newlen, arga[i]);
+ newlen += strlen(arga[i]) + 1;
+ }
+
+ /*
+ * Build modified tuple.
+ */
+ for (i = 0; i < Natts_pg_trigger; i++)
+ {
+ values[i] = (Datum) 0;
+ replaces[i] = ' ';
+ nulls[i] = ' ';
+ }
+ values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(newtgargs);
+ replaces[Anum_pg_trigger_tgargs - 1] = 'r';
+
+ tuple = heap_modifytuple(tuple, tgrel, values, nulls, replaces);
+
+ /*
+ * Now we can release hold on original tuple.
+ */
+ ReleaseBuffer(buffer);
+
+ /*
+ * Update pg_trigger and its indexes
+ */
+ simple_heap_update(tgrel, &tuple->t_self, tuple);
+
+ {
+ Relation irelations[Num_pg_attr_indices];
+
+ CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, irelations);
+ CatalogIndexInsert(irelations, Num_pg_trigger_indices, tgrel, tuple);
+ CatalogCloseIndices(Num_pg_trigger_indices, irelations);
+ }
+
+ /* free up our scratch memory */
+ pfree(newtgargs);
+ heap_freetuple(tuple);
+ }
+
+ index_endscan(idxtgscan);
+ index_close(irel);
+
+ heap_close(tgrel, RowExclusiveLock);
+
+ /*
+ * Increment cmd counter to make updates visible; this is needed
+ * in case the same tuple has to be updated again by next pass
+ * (can happen in case of a self-referential FK relationship).
+ */
+ CommandCounterIncrement();
}
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index b71732a1942..071b7248d88 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -18,7 +18,7 @@
* Portions Copyright (c) 2000-2001, PostgreSQL Global Development Group
* Copyright 1999 Jan Wieck
*
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.29 2001/10/25 05:49:45 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.30 2001/11/12 00:46:36 tgl Exp $
*
* ----------
*/
@@ -43,16 +43,6 @@
* Local definitions
* ----------
*/
-#define RI_CONSTRAINT_NAME_ARGNO 0
-#define RI_FK_RELNAME_ARGNO 1
-#define RI_PK_RELNAME_ARGNO 2
-#define RI_MATCH_TYPE_ARGNO 3
-#define RI_FIRST_ATTNAME_ARGNO 4
-
-#define RI_MAX_NUMKEYS 16
-#define RI_MAX_ARGUMENTS (RI_FIRST_ATTNAME_ARGNO + (RI_MAX_NUMKEYS * 2))
-#define RI_KEYPAIR_FK_IDX 0
-#define RI_KEYPAIR_PK_IDX 1
#define RI_INIT_QUERYHASHSIZE 128
#define RI_INIT_OPREQHASHSIZE 128
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index c0399cbf105..eae66cccaab 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: trigger.h,v 1.30 2001/11/05 17:46:33 momjian Exp $
+ * $Id: trigger.h,v 1.31 2001/11/12 00:46:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -78,6 +78,25 @@ typedef struct TriggerData
#define TRIGGER_FIRED_AFTER(event) \
(!TRIGGER_FIRED_BEFORE (event))
+/*
+ * RI trigger function arguments are stored in pg_trigger.tgargs bytea
+ *
+ * constrname\0fkrel\0pkrel\0matchtype\0fkatt\0pkatt\0fkatt\0pkatt\0...
+ *
+ * There are one or more pairs of fkatt/pkatt names.
+ */
+#define RI_CONSTRAINT_NAME_ARGNO 0
+#define RI_FK_RELNAME_ARGNO 1
+#define RI_PK_RELNAME_ARGNO 2
+#define RI_MATCH_TYPE_ARGNO 3
+#define RI_FIRST_ATTNAME_ARGNO 4 /* first attname pair starts here */
+
+#define RI_KEYPAIR_FK_IDX 0
+#define RI_KEYPAIR_PK_IDX 1
+
+#define RI_MAX_NUMKEYS INDEX_MAX_KEYS
+#define RI_MAX_ARGUMENTS (RI_FIRST_ATTNAME_ARGNO + (RI_MAX_NUMKEYS * 2))
+
extern void CreateTrigger(CreateTrigStmt *stmt);
extern void DropTrigger(DropTrigStmt *stmt);