aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/trigger.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/trigger.c')
-rw-r--r--src/backend/commands/trigger.c137
1 files changed, 81 insertions, 56 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 8798992ecf9..c5a823857ee 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.221 2007/11/04 21:25:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.222 2007/11/05 19:00:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -222,11 +222,11 @@ CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid)
(list_length(stmt->args) % 2) == 0 &&
RI_FKey_trigger_type(funcoid) != RI_TRIGGER_NONE)
{
- ConvertTriggerToFK(stmt, funcoid);
-
/* Keep lock on target rel until end of xact */
heap_close(rel, NoLock);
+ ConvertTriggerToFK(stmt, funcoid);
+
return InvalidOid;
}
@@ -462,18 +462,23 @@ CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid)
*
* The conversion is complex because a pre-7.3 foreign key involved three
* separate triggers, which were reported separately in dumps. While the
- * single trigger on the referencing table can be ignored, we need info
- * from both of the triggers on the referenced table to build the constraint
- * declaration. Our approach is to save info from the first trigger seen
- * in a list in TopMemoryContext. When we see the second trigger we can
- * create the FK constraint and remove the list entry. We match triggers
- * together by comparing the trigger arguments (which include constraint
- * name, table and column names, so should be good enough).
+ * single trigger on the referencing table adds no new information, we need
+ * to know the trigger functions of both of the triggers on the referenced
+ * table to build the constraint declaration. Also, due to lack of proper
+ * dependency checking pre-7.3, it is possible that the source database had
+ * an incomplete set of triggers resulting in an only partially enforced
+ * FK constraint. (This would happen if one of the tables had been dropped
+ * and re-created, but only if the DB had been affected by a 7.0 pg_dump bug
+ * that caused loss of tgconstrrelid information.) We choose to translate to
+ * an FK constraint only when we've seen all three triggers of a set. This is
+ * implemented by storing unmatched items in a list in TopMemoryContext.
+ * We match triggers together by comparing the trigger arguments (which
+ * include constraint name, table and column names, so should be good enough).
*/
typedef struct {
List *args; /* list of (T_String) Values or NIL */
- Oid funcoid; /* OID of trigger function */
- bool isupd; /* is it the UPDATE trigger? */
+ Oid funcoids[3]; /* OIDs of trigger functions */
+ /* The three function OIDs are stored in the order update, delete, child */
} OldTriggerInfo;
static void
@@ -481,6 +486,12 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
{
static List *info_list = NIL;
+ static const char * const funcdescr[3] = {
+ gettext_noop("Found referenced table's UPDATE trigger."),
+ gettext_noop("Found referenced table's DELETE trigger."),
+ gettext_noop("Found referencing table's trigger.")
+ };
+
char *constr_name;
char *fk_table_name;
char *pk_table_name;
@@ -488,7 +499,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
List *fk_attrs = NIL;
List *pk_attrs = NIL;
StringInfoData buf;
- bool isupd;
+ int funcnum;
OldTriggerInfo *info = NULL;
ListCell *l;
int i;
@@ -548,73 +559,99 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
/* Identify class of trigger --- update, delete, or referencing-table */
switch (funcoid)
{
- case F_RI_FKEY_CASCADE_DEL:
- case F_RI_FKEY_RESTRICT_DEL:
- case F_RI_FKEY_SETNULL_DEL:
- case F_RI_FKEY_SETDEFAULT_DEL:
- case F_RI_FKEY_NOACTION_DEL:
- isupd = false;
- break;
-
case F_RI_FKEY_CASCADE_UPD:
case F_RI_FKEY_RESTRICT_UPD:
case F_RI_FKEY_SETNULL_UPD:
case F_RI_FKEY_SETDEFAULT_UPD:
case F_RI_FKEY_NOACTION_UPD:
- isupd = true;
+ funcnum = 0;
+ break;
+
+ case F_RI_FKEY_CASCADE_DEL:
+ case F_RI_FKEY_RESTRICT_DEL:
+ case F_RI_FKEY_SETNULL_DEL:
+ case F_RI_FKEY_SETDEFAULT_DEL:
+ case F_RI_FKEY_NOACTION_DEL:
+ funcnum = 1;
break;
default:
- /* Ignore triggers on referencing table */
- ereport(NOTICE,
- (errmsg("ignoring incomplete trigger group for constraint \"%s\" %s",
- constr_name, buf.data)));
- return;
+ funcnum = 2;
+ break;
}
/* See if we have a match to this trigger */
foreach(l, info_list)
{
info = (OldTriggerInfo *) lfirst(l);
- if (info->isupd != isupd && equal(info->args, stmt->args))
+ if (info->funcoids[funcnum] == InvalidOid &&
+ equal(info->args, stmt->args))
+ {
+ info->funcoids[funcnum] = funcoid;
break;
+ }
}
if (l == NULL)
{
- /* First trigger of pair, so save away what we need */
+ /* First trigger of set, so create a new list entry */
MemoryContext oldContext;
ereport(NOTICE,
(errmsg("ignoring incomplete trigger group for constraint \"%s\" %s",
- constr_name, buf.data)));
+ constr_name, buf.data),
+ errdetail(funcdescr[funcnum])));
oldContext = MemoryContextSwitchTo(TopMemoryContext);
- info = (OldTriggerInfo *) palloc(sizeof(OldTriggerInfo));
+ info = (OldTriggerInfo *) palloc0(sizeof(OldTriggerInfo));
info->args = copyObject(stmt->args);
- info->funcoid = funcoid;
- info->isupd = isupd;
+ info->funcoids[funcnum] = funcoid;
info_list = lappend(info_list, info);
MemoryContextSwitchTo(oldContext);
}
+ else if (info->funcoids[0] == InvalidOid ||
+ info->funcoids[1] == InvalidOid ||
+ info->funcoids[2] == InvalidOid)
+ {
+ /* Second trigger of set */
+ ereport(NOTICE,
+ (errmsg("ignoring incomplete trigger group for constraint \"%s\" %s",
+ constr_name, buf.data),
+ errdetail(funcdescr[funcnum])));
+ }
else
{
- /* OK, we have a pair, so make the FK constraint ALTER TABLE cmd */
+ /* OK, we have a set, so make the FK constraint ALTER TABLE cmd */
AlterTableStmt *atstmt = makeNode(AlterTableStmt);
AlterTableCmd *atcmd = makeNode(AlterTableCmd);
FkConstraint *fkcon = makeNode(FkConstraint);
- Oid updfunc,
- delfunc;
ereport(NOTICE,
(errmsg("converting trigger group into constraint \"%s\" %s",
- constr_name, buf.data)));
-
- if (stmt->constrrel)
- atstmt->relation = stmt->constrrel;
+ constr_name, buf.data),
+ errdetail(funcdescr[funcnum])));
+ if (funcnum == 2)
+ {
+ /* This trigger is on the FK table */
+ atstmt->relation = stmt->relation;
+ if (stmt->constrrel)
+ fkcon->pktable = stmt->constrrel;
+ else
+ {
+ /* Work around ancient pg_dump bug that omitted constrrel */
+ fkcon->pktable = makeRangeVar(NULL, pk_table_name);
+ }
+ }
else
{
- /* Work around ancient pg_dump bug that omitted constrrel */
- atstmt->relation = makeRangeVar(NULL, fk_table_name);
+ /* This trigger is on the PK table */
+ fkcon->pktable = stmt->relation;
+ if (stmt->constrrel)
+ atstmt->relation = stmt->constrrel;
+ else
+ {
+ /* Work around ancient pg_dump bug that omitted constrrel */
+ atstmt->relation = makeRangeVar(NULL, fk_table_name);
+ }
}
atstmt->cmds = list_make1(atcmd);
atstmt->relkind = OBJECT_TABLE;
@@ -624,22 +661,10 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
fkcon->constr_name = NULL;
else
fkcon->constr_name = constr_name;
- fkcon->pktable = stmt->relation;
fkcon->fk_attrs = fk_attrs;
fkcon->pk_attrs = pk_attrs;
fkcon->fk_matchtype = fk_matchtype;
-
- if (isupd)
- {
- updfunc = funcoid;
- delfunc = info->funcoid;
- }
- else
- {
- updfunc = info->funcoid;
- delfunc = funcoid;
- }
- switch (updfunc)
+ switch (info->funcoids[0])
{
case F_RI_FKEY_NOACTION_UPD:
fkcon->fk_upd_action = FKCONSTR_ACTION_NOACTION;
@@ -660,7 +685,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
/* can't get here because of earlier checks */
elog(ERROR, "confused about RI update function");
}
- switch (delfunc)
+ switch (info->funcoids[1])
{
case F_RI_FKEY_NOACTION_DEL:
fkcon->fk_del_action = FKCONSTR_ACTION_NOACTION;