mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-02-05 19:09:58 +08:00
Make ALTER TABLE RENAME update foreign-key trigger arguments correctly.
Brent Verner, with review and kibitzing from Tom Lane.
This commit is contained in:
parent
8bfc437301
commit
f14fdad858
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user