7
7
* Portions Copyright (c) 1994, Regents of the University of California
8
8
*
9
9
* IDENTIFICATION
10
- * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.221 2007/11/04 21:25:55 tgl Exp $
10
+ * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.222 2007/11/05 19:00:25 tgl Exp $
11
11
*
12
12
*-------------------------------------------------------------------------
13
13
*/
@@ -222,11 +222,11 @@ CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid)
222
222
(list_length (stmt -> args ) % 2 ) == 0 &&
223
223
RI_FKey_trigger_type (funcoid ) != RI_TRIGGER_NONE )
224
224
{
225
- ConvertTriggerToFK (stmt , funcoid );
226
-
227
225
/* Keep lock on target rel until end of xact */
228
226
heap_close (rel , NoLock );
229
227
228
+ ConvertTriggerToFK (stmt , funcoid );
229
+
230
230
return InvalidOid ;
231
231
}
232
232
@@ -462,33 +462,44 @@ CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid)
462
462
*
463
463
* The conversion is complex because a pre-7.3 foreign key involved three
464
464
* separate triggers, which were reported separately in dumps. While the
465
- * single trigger on the referencing table can be ignored, we need info
466
- * from both of the triggers on the referenced table to build the constraint
467
- * declaration. Our approach is to save info from the first trigger seen
468
- * in a list in TopMemoryContext. When we see the second trigger we can
469
- * create the FK constraint and remove the list entry. We match triggers
470
- * together by comparing the trigger arguments (which include constraint
471
- * name, table and column names, so should be good enough).
465
+ * single trigger on the referencing table adds no new information, we need
466
+ * to know the trigger functions of both of the triggers on the referenced
467
+ * table to build the constraint declaration. Also, due to lack of proper
468
+ * dependency checking pre-7.3, it is possible that the source database had
469
+ * an incomplete set of triggers resulting in an only partially enforced
470
+ * FK constraint. (This would happen if one of the tables had been dropped
471
+ * and re-created, but only if the DB had been affected by a 7.0 pg_dump bug
472
+ * that caused loss of tgconstrrelid information.) We choose to translate to
473
+ * an FK constraint only when we've seen all three triggers of a set. This is
474
+ * implemented by storing unmatched items in a list in TopMemoryContext.
475
+ * We match triggers together by comparing the trigger arguments (which
476
+ * include constraint name, table and column names, so should be good enough).
472
477
*/
473
478
typedef struct {
474
479
List * args ; /* list of (T_String) Values or NIL */
475
- Oid funcoid ; /* OID of trigger function */
476
- bool isupd ; /* is it the UPDATE trigger? */
480
+ Oid funcoids [ 3 ]; /* OIDs of trigger functions */
481
+ /* The three function OIDs are stored in the order update, delete, child */
477
482
} OldTriggerInfo ;
478
483
479
484
static void
480
485
ConvertTriggerToFK (CreateTrigStmt * stmt , Oid funcoid )
481
486
{
482
487
static List * info_list = NIL ;
483
488
489
+ static const char * const funcdescr [3 ] = {
490
+ gettext_noop ("Found referenced table's UPDATE trigger." ),
491
+ gettext_noop ("Found referenced table's DELETE trigger." ),
492
+ gettext_noop ("Found referencing table's trigger." )
493
+ };
494
+
484
495
char * constr_name ;
485
496
char * fk_table_name ;
486
497
char * pk_table_name ;
487
498
char fk_matchtype = FKCONSTR_MATCH_UNSPECIFIED ;
488
499
List * fk_attrs = NIL ;
489
500
List * pk_attrs = NIL ;
490
501
StringInfoData buf ;
491
- bool isupd ;
502
+ int funcnum ;
492
503
OldTriggerInfo * info = NULL ;
493
504
ListCell * l ;
494
505
int i ;
@@ -548,73 +559,99 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
548
559
/* Identify class of trigger --- update, delete, or referencing-table */
549
560
switch (funcoid )
550
561
{
551
- case F_RI_FKEY_CASCADE_DEL :
552
- case F_RI_FKEY_RESTRICT_DEL :
553
- case F_RI_FKEY_SETNULL_DEL :
554
- case F_RI_FKEY_SETDEFAULT_DEL :
555
- case F_RI_FKEY_NOACTION_DEL :
556
- isupd = false;
557
- break ;
558
-
559
562
case F_RI_FKEY_CASCADE_UPD :
560
563
case F_RI_FKEY_RESTRICT_UPD :
561
564
case F_RI_FKEY_SETNULL_UPD :
562
565
case F_RI_FKEY_SETDEFAULT_UPD :
563
566
case F_RI_FKEY_NOACTION_UPD :
564
- isupd = true;
567
+ funcnum = 0 ;
568
+ break ;
569
+
570
+ case F_RI_FKEY_CASCADE_DEL :
571
+ case F_RI_FKEY_RESTRICT_DEL :
572
+ case F_RI_FKEY_SETNULL_DEL :
573
+ case F_RI_FKEY_SETDEFAULT_DEL :
574
+ case F_RI_FKEY_NOACTION_DEL :
575
+ funcnum = 1 ;
565
576
break ;
566
577
567
578
default :
568
- /* Ignore triggers on referencing table */
569
- ereport (NOTICE ,
570
- (errmsg ("ignoring incomplete trigger group for constraint \"%s\" %s" ,
571
- constr_name , buf .data )));
572
- return ;
579
+ funcnum = 2 ;
580
+ break ;
573
581
}
574
582
575
583
/* See if we have a match to this trigger */
576
584
foreach (l , info_list )
577
585
{
578
586
info = (OldTriggerInfo * ) lfirst (l );
579
- if (info -> isupd != isupd && equal (info -> args , stmt -> args ))
587
+ if (info -> funcoids [funcnum ] == InvalidOid &&
588
+ equal (info -> args , stmt -> args ))
589
+ {
590
+ info -> funcoids [funcnum ] = funcoid ;
580
591
break ;
592
+ }
581
593
}
582
594
583
595
if (l == NULL )
584
596
{
585
- /* First trigger of pair , so save away what we need */
597
+ /* First trigger of set , so create a new list entry */
586
598
MemoryContext oldContext ;
587
599
588
600
ereport (NOTICE ,
589
601
(errmsg ("ignoring incomplete trigger group for constraint \"%s\" %s" ,
590
- constr_name , buf .data )));
602
+ constr_name , buf .data ),
603
+ errdetail (funcdescr [funcnum ])));
591
604
oldContext = MemoryContextSwitchTo (TopMemoryContext );
592
- info = (OldTriggerInfo * ) palloc (sizeof (OldTriggerInfo ));
605
+ info = (OldTriggerInfo * ) palloc0 (sizeof (OldTriggerInfo ));
593
606
info -> args = copyObject (stmt -> args );
594
- info -> funcoid = funcoid ;
595
- info -> isupd = isupd ;
607
+ info -> funcoids [funcnum ] = funcoid ;
596
608
info_list = lappend (info_list , info );
597
609
MemoryContextSwitchTo (oldContext );
598
610
}
611
+ else if (info -> funcoids [0 ] == InvalidOid ||
612
+ info -> funcoids [1 ] == InvalidOid ||
613
+ info -> funcoids [2 ] == InvalidOid )
614
+ {
615
+ /* Second trigger of set */
616
+ ereport (NOTICE ,
617
+ (errmsg ("ignoring incomplete trigger group for constraint \"%s\" %s" ,
618
+ constr_name , buf .data ),
619
+ errdetail (funcdescr [funcnum ])));
620
+ }
599
621
else
600
622
{
601
- /* OK, we have a pair , so make the FK constraint ALTER TABLE cmd */
623
+ /* OK, we have a set , so make the FK constraint ALTER TABLE cmd */
602
624
AlterTableStmt * atstmt = makeNode (AlterTableStmt );
603
625
AlterTableCmd * atcmd = makeNode (AlterTableCmd );
604
626
FkConstraint * fkcon = makeNode (FkConstraint );
605
- Oid updfunc ,
606
- delfunc ;
607
627
608
628
ereport (NOTICE ,
609
629
(errmsg ("converting trigger group into constraint \"%s\" %s" ,
610
- constr_name , buf .data )));
611
-
612
- if (stmt -> constrrel )
613
- atstmt -> relation = stmt -> constrrel ;
630
+ constr_name , buf .data ),
631
+ errdetail (funcdescr [funcnum ])));
632
+ if (funcnum == 2 )
633
+ {
634
+ /* This trigger is on the FK table */
635
+ atstmt -> relation = stmt -> relation ;
636
+ if (stmt -> constrrel )
637
+ fkcon -> pktable = stmt -> constrrel ;
638
+ else
639
+ {
640
+ /* Work around ancient pg_dump bug that omitted constrrel */
641
+ fkcon -> pktable = makeRangeVar (NULL , pk_table_name );
642
+ }
643
+ }
614
644
else
615
645
{
616
- /* Work around ancient pg_dump bug that omitted constrrel */
617
- atstmt -> relation = makeRangeVar (NULL , fk_table_name );
646
+ /* This trigger is on the PK table */
647
+ fkcon -> pktable = stmt -> relation ;
648
+ if (stmt -> constrrel )
649
+ atstmt -> relation = stmt -> constrrel ;
650
+ else
651
+ {
652
+ /* Work around ancient pg_dump bug that omitted constrrel */
653
+ atstmt -> relation = makeRangeVar (NULL , fk_table_name );
654
+ }
618
655
}
619
656
atstmt -> cmds = list_make1 (atcmd );
620
657
atstmt -> relkind = OBJECT_TABLE ;
@@ -624,22 +661,10 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
624
661
fkcon -> constr_name = NULL ;
625
662
else
626
663
fkcon -> constr_name = constr_name ;
627
- fkcon -> pktable = stmt -> relation ;
628
664
fkcon -> fk_attrs = fk_attrs ;
629
665
fkcon -> pk_attrs = pk_attrs ;
630
666
fkcon -> fk_matchtype = fk_matchtype ;
631
-
632
- if (isupd )
633
- {
634
- updfunc = funcoid ;
635
- delfunc = info -> funcoid ;
636
- }
637
- else
638
- {
639
- updfunc = info -> funcoid ;
640
- delfunc = funcoid ;
641
- }
642
- switch (updfunc )
667
+ switch (info -> funcoids [0 ])
643
668
{
644
669
case F_RI_FKEY_NOACTION_UPD :
645
670
fkcon -> fk_upd_action = FKCONSTR_ACTION_NOACTION ;
@@ -660,7 +685,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
660
685
/* can't get here because of earlier checks */
661
686
elog (ERROR , "confused about RI update function" );
662
687
}
663
- switch (delfunc )
688
+ switch (info -> funcoids [ 1 ] )
664
689
{
665
690
case F_RI_FKEY_NOACTION_DEL :
666
691
fkcon -> fk_del_action = FKCONSTR_ACTION_NOACTION ;
0 commit comments