@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $ PostgreSQL : pgsql / src / backend / commands / tablecmds . c , v 1.213 2007 / 02 / 02 00 : 07 : 02 tgl Exp $
* $ PostgreSQL : pgsql / src / backend / commands / tablecmds . c , v 1.214 2007 / 02 / 14 01 : 58 : 56 tgl Exp $
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
@ -28,6 +28,7 @@
# include "catalog/pg_depend.h"
# include "catalog/pg_inherits.h"
# include "catalog/pg_namespace.h"
# include "catalog/pg_opclass.h"
# include "catalog/pg_trigger.h"
# include "catalog/pg_type.h"
# include "catalog/toasting.h"
@ -139,6 +140,7 @@ typedef struct NewConstraint
char * name ; /* Constraint name, or NULL if none */
ConstrType contype ; /* CHECK or FOREIGN */
Oid refrelid ; /* PK rel, if FOREIGN */
Oid conid ; /* OID of pg_constraint entry, if FOREIGN */
Node * qual ; /* Check expr or FkConstraint struct */
List * qualstate ; /* Execution state for CHECK */
} NewConstraint ;
@ -186,10 +188,9 @@ static Oid transformFkeyCheckAttrs(Relation pkrel,
int numattrs , int16 * attnums ,
Oid * opclasses ) ;
static void validateForeignKeyConstraint ( FkConstraint * fkconstraint ,
Relation rel , Relation pkrel ) ;
Relation rel , Relation pkrel , Oid constraintOid ) ;
static void createForeignKeyTriggers ( Relation rel , FkConstraint * fkconstraint ,
Oid constrOid ) ;
static char * fkMatchTypeToString ( char match_type ) ;
Oid constraintOid ) ;
static void ATController ( Relation rel , List * cmds , bool recurse ) ;
static void ATPrepCmd ( List * * wqueue , Relation rel , AlterTableCmd * cmd ,
bool recurse , bool recursing ) ;
@ -256,11 +257,6 @@ static void ATExecEnableDisableTrigger(Relation rel, char *trigname,
static void ATExecAddInherit ( Relation rel , RangeVar * parent ) ;
static void ATExecDropInherit ( Relation rel , RangeVar * parent ) ;
static void copy_relation_data ( Relation rel , SMgrRelation dst ) ;
static void update_ri_trigger_args ( Oid relid ,
const char * oldname ,
const char * newname ,
bool fk_scan ,
bool update_relname ) ;
/* ----------------------------------------------------------------
@ -1615,21 +1611,6 @@ renameatt(Oid myrelid,
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 ) ;
}
relation_close ( targetrelation , NoLock ) ; /* close rel but keep lock */
}
@ -1708,226 +1689,12 @@ renamerel(Oid myrelid, const char *newrelname)
if ( relkind ! = RELKIND_INDEX )
TypeRename ( oldrelname , namespaceId , newrelname ) ;
/*
* Update rel name in any RI triggers associated with the relation .
*/
if ( relhastriggers )
{
/* update tgargs where relname is primary key */
update_ri_trigger_args ( myrelid ,
oldrelname ,
newrelname ,
false , true ) ;
/* update tgargs where relname is foreign key */
update_ri_trigger_args ( myrelid ,
oldrelname ,
newrelname ,
true , true ) ;
}
/*
* Close rel , but keep exclusive lock !
*/
relation_close ( targetrelation , NoLock ) ;
}
/*
* 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 ;
ScanKeyData skey [ 1 ] ;
SysScanDesc trigscan ;
HeapTuple tuple ;
Datum values [ Natts_pg_trigger ] ;
char nulls [ Natts_pg_trigger ] ;
char replaces [ Natts_pg_trigger ] ;
tgrel = heap_open ( TriggerRelationId , RowExclusiveLock ) ;
if ( fk_scan )
{
ScanKeyInit ( & skey [ 0 ] ,
Anum_pg_trigger_tgconstrrelid ,
BTEqualStrategyNumber , F_OIDEQ ,
ObjectIdGetDatum ( relid ) ) ;
trigscan = systable_beginscan ( tgrel , TriggerConstrRelidIndexId ,
true , SnapshotNow ,
1 , skey ) ;
}
else
{
ScanKeyInit ( & skey [ 0 ] ,
Anum_pg_trigger_tgrelid ,
BTEqualStrategyNumber , F_OIDEQ ,
ObjectIdGetDatum ( relid ) ) ;
trigscan = systable_beginscan ( tgrel , TriggerRelidNameIndexId ,
true , SnapshotNow ,
1 , skey ) ;
}
while ( ( tuple = systable_getnext ( trigscan ) ) ! = NULL )
{
Form_pg_trigger pg_trigger = ( Form_pg_trigger ) GETSTRUCT ( tuple ) ;
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 ;
tg_type = RI_FKey_trigger_type ( pg_trigger - > tgfoid ) ;
if ( tg_type = = RI_TRIGGER_NONE )
{
/* Not an RI trigger, forget it */
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 = DatumGetByteaP ( 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 */
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 */
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 , RelationGetDescr ( tgrel ) , values , nulls , replaces ) ;
/*
* Update pg_trigger and its indexes
*/
simple_heap_update ( tgrel , & tuple - > t_self , tuple ) ;
CatalogUpdateIndexes ( tgrel , tuple ) ;
/*
* Invalidate trigger ' s relation ' s relcache entry so that other
* backends ( and this one too ! ) are sent SI message to make them
* rebuild relcache entries . ( Ideally this should happen
* automatically . . . )
*
* We can skip this for triggers on relid itself , since that relcache
* flush will happen anyway due to the table or column rename . We
* just need to catch the far ends of RI relationships .
*/
pg_trigger = ( Form_pg_trigger ) GETSTRUCT ( tuple ) ;
if ( pg_trigger - > tgrelid ! = relid )
CacheInvalidateRelcacheByRelid ( pg_trigger - > tgrelid ) ;
/* free up our scratch memory */
pfree ( newtgargs ) ;
heap_freetuple ( tuple ) ;
}
systable_endscan ( trigscan ) ;
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 ( ) ;
}
/*
* AlterTable
* Execute ALTER TABLE , which can be a list of subcommands
@ -2552,7 +2319,8 @@ ATRewriteTables(List **wqueue)
refrel = heap_open ( con - > refrelid , RowShareLock ) ;
validateForeignKeyConstraint ( fkconstraint , rel , refrel ) ;
validateForeignKeyConstraint ( fkconstraint , rel , refrel ,
con - > conid ) ;
heap_close ( refrel , NoLock ) ;
}
@ -4061,6 +3829,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Oid pktypoid [ INDEX_MAX_KEYS ] ;
Oid fktypoid [ INDEX_MAX_KEYS ] ;
Oid opclasses [ INDEX_MAX_KEYS ] ;
Oid pfeqoperators [ INDEX_MAX_KEYS ] ;
Oid ppeqoperators [ INDEX_MAX_KEYS ] ;
Oid ffeqoperators [ INDEX_MAX_KEYS ] ;
int i ;
int numfks ,
numpks ;
@ -4138,6 +3909,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
MemSet ( pktypoid , 0 , sizeof ( pktypoid ) ) ;
MemSet ( fktypoid , 0 , sizeof ( fktypoid ) ) ;
MemSet ( opclasses , 0 , sizeof ( opclasses ) ) ;
MemSet ( pfeqoperators , 0 , sizeof ( pfeqoperators ) ) ;
MemSet ( ppeqoperators , 0 , sizeof ( ppeqoperators ) ) ;
MemSet ( ffeqoperators , 0 , sizeof ( ffeqoperators ) ) ;
numfks = transformColumnNameList ( RelationGetRelid ( rel ) ,
fkconstraint - > fk_attrs ,
@ -4166,7 +3940,15 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
opclasses ) ;
}
/* Be sure referencing and referenced column types are comparable */
/*
* Look up the equality operators to use in the constraint .
*
* Note that we look for operator ( s ) with the PK type on the left ; when
* the types are different this is the right choice because the PK index
* will need operators with the indexkey on the left . Also , we take the
* PK type as being the declared input type of the opclass , which might be
* binary - compatible but not identical to the PK column type .
*/
if ( numfks ! = numpks )
ereport ( ERROR ,
( errcode ( ERRCODE_INVALID_FOREIGN_KEY ) ,
@ -4174,24 +3956,71 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
for ( i = 0 ; i < numpks ; i + + )
{
HeapTuple cla_ht ;
Form_pg_opclass cla_tup ;
Oid amid ;
Oid opfamily ;
Oid pktype ;
Oid fktype ;
Oid pfeqop ;
Oid ppeqop ;
Oid ffeqop ;
int16 eqstrategy ;
/* We need several fields out of the pg_opclass entry */
cla_ht = SearchSysCache ( CLAOID ,
ObjectIdGetDatum ( opclasses [ i ] ) ,
0 , 0 , 0 ) ;
if ( ! HeapTupleIsValid ( cla_ht ) )
elog ( ERROR , " cache lookup failed for opclass %u " , opclasses [ i ] ) ;
cla_tup = ( Form_pg_opclass ) GETSTRUCT ( cla_ht ) ;
amid = cla_tup - > opcmethod ;
opfamily = cla_tup - > opcfamily ;
pktype = cla_tup - > opcintype ;
ReleaseSysCache ( cla_ht ) ;
/*
* pktypoid [ i ] is the primary key table ' s i ' th key ' s type fktypoid [ i ]
* is the foreign key table ' s i ' th key ' s type
*
* Note that we look for an operator with the PK type on the left ;
* when the types are different this is critical because the PK index
* will need operators with the indexkey on the left . ( Ordinarily both
* commutator operators will exist if either does , but we won ' t get
* the right answer from the test below on opclass membership unless
* we select the proper operator . )
* Check it ' s a btree ; currently this can never fail since no other
* index AMs support unique indexes . If we ever did have other
* types of unique indexes , we ' d need a way to determine which
* operator strategy number is equality . ( Is it reasonable to
* insist that every such index AM use btree ' s number for equality ? )
*/
if ( amid ! = BTREE_AM_OID )
elog ( ERROR , " only b-tree indexes are supported for foreign keys " ) ;
eqstrategy = BTEqualStrategyNumber ;
/*
* There had better be a PK = PK operator for the index .
*/
ppeqop = get_opfamily_member ( opfamily , pktype , pktype , eqstrategy ) ;
if ( ! OidIsValid ( ppeqop ) )
elog ( ERROR , " missing operator %d(%u,%u) in opfamily %u " ,
eqstrategy , pktype , pktype , opfamily ) ;
/*
* Are there equality operators that take exactly the FK type ?
* Assume we should look through any domain here .
*/
Operator o = oper ( NULL , list_make1 ( makeString ( " = " ) ) ,
pktypoid [ i ] , fktypoid [ i ] ,
true , - 1 ) ;
fktype = getBaseType ( fktypoid [ i ] ) ;
pfeqop = get_opfamily_member ( opfamily , pktype , fktype , eqstrategy ) ;
ffeqop = get_opfamily_member ( opfamily , fktype , fktype , eqstrategy ) ;
if ( o = = NULL )
if ( ! ( OidIsValid ( pfeqop ) & & OidIsValid ( ffeqop ) ) )
{
/*
* Otherwise , look for an implicit cast from the FK type to
* the PK type , and if found , use the PK type ' s equality operator .
*/
if ( can_coerce_type ( 1 , & fktype , & pktype , COERCION_IMPLICIT ) )
pfeqop = ffeqop = ppeqop ;
}
if ( ! ( OidIsValid ( pfeqop ) & & OidIsValid ( ffeqop ) ) )
ereport ( ERROR ,
( errcode ( ERRCODE_UNDEFINED_FUNCTION ) ,
( errcode ( ERRCODE_DATATYPE_MISMATCH ) ,
errmsg ( " foreign key constraint \" %s \" "
" cannot be implemented " ,
fkconstraint - > constr_name ) ,
@ -4202,41 +4031,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
format_type_be ( fktypoid [ i ] ) ,
format_type_be ( pktypoid [ i ] ) ) ) ) ;
/*
* Check that the found operator is compatible with the PK index , and
* generate a warning if not , since otherwise costly seqscans will be
* incurred to check FK validity .
*/
if ( ! op_in_opfamily ( oprid ( o ) , get_opclass_family ( opclasses [ i ] ) ) )
ereport ( WARNING ,
( errmsg ( " foreign key constraint \" %s \" "
" will require costly sequential scans " ,
fkconstraint - > constr_name ) ,
errdetail ( " Key columns \" %s \" and \" %s \" "
" are of different types: %s and %s. " ,
strVal ( list_nth ( fkconstraint - > fk_attrs , i ) ) ,
strVal ( list_nth ( fkconstraint - > pk_attrs , i ) ) ,
format_type_be ( fktypoid [ i ] ) ,
format_type_be ( pktypoid [ i ] ) ) ) ) ;
ReleaseSysCache ( o ) ;
}
/*
* Tell Phase 3 to check that the constraint is satisfied by existing rows
* ( we can skip this during table creation ) .
*/
if ( ! fkconstraint - > skip_validation )
{
NewConstraint * newcon ;
newcon = ( NewConstraint * ) palloc0 ( sizeof ( NewConstraint ) ) ;
newcon - > name = fkconstraint - > constr_name ;
newcon - > contype = CONSTR_FOREIGN ;
newcon - > refrelid = RelationGetRelid ( pkrel ) ;
newcon - > qual = ( Node * ) fkconstraint ;
tab - > constraints = lappend ( tab - > constraints , newcon ) ;
pfeqoperators [ i ] = pfeqop ;
ppeqoperators [ i ] = ppeqop ;
ffeqoperators [ i ] = ffeqop ;
}
/*
@ -4254,6 +4051,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
* constraint */
RelationGetRelid ( pkrel ) ,
pkattnum ,
pfeqoperators ,
ppeqoperators ,
ffeqoperators ,
numpks ,
fkconstraint - > fk_upd_action ,
fkconstraint - > fk_del_action ,
@ -4268,6 +4068,24 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
*/
createForeignKeyTriggers ( rel , fkconstraint , constrOid ) ;
/*
* Tell Phase 3 to check that the constraint is satisfied by existing rows
* ( we can skip this during table creation ) .
*/
if ( ! fkconstraint - > skip_validation )
{
NewConstraint * newcon ;
newcon = ( NewConstraint * ) palloc0 ( sizeof ( NewConstraint ) ) ;
newcon - > name = fkconstraint - > constr_name ;
newcon - > contype = CONSTR_FOREIGN ;
newcon - > refrelid = RelationGetRelid ( pkrel ) ;
newcon - > conid = constrOid ;
newcon - > qual = ( Node * ) fkconstraint ;
tab - > constraints = lappend ( tab - > constraints , newcon ) ;
}
/*
* Close pk table , but keep lock until we ' ve committed .
*/
@ -4526,25 +4344,15 @@ transformFkeyCheckAttrs(Relation pkrel,
static void
validateForeignKeyConstraint ( FkConstraint * fkconstraint ,
Relation rel ,
Relation pkrel )
Relation pkrel ,
Oid constraintOid )
{
HeapScanDesc scan ;
HeapTuple tuple ;
Trigger trig ;
ListCell * list ;
int count ;
/*
* See if we can do it with a single LEFT JOIN query . A FALSE result
* indicates we must proceed with the fire - the - trigger method .
*/
if ( RI_Initial_Check ( fkconstraint , rel , pkrel ) )
return ;
/*
* Scan through each tuple , calling RI_FKey_check_ins ( insert trigger ) as
* if that tuple had just been inserted . If any of those fail , it should
* ereport ( ERROR ) and that ' s that .
* Build a trigger call structure ; we ' ll need it either way .
*/
MemSet ( & trig , 0 , sizeof ( trig ) ) ;
trig . tgoid = InvalidOid ;
@ -4552,35 +4360,23 @@ validateForeignKeyConstraint(FkConstraint *fkconstraint,
trig . tgenabled = TRUE ;
trig . tgisconstraint = TRUE ;
trig . tgconstrrelid = RelationGetRelid ( pkrel ) ;
trig . tgconstraint = constraintOid ;
trig . tgdeferrable = FALSE ;
trig . tginitdeferred = FALSE ;
/* we needn't fill in tgargs */
trig . tgargs = ( char * * ) palloc ( sizeof ( char * ) *
( 4 + list_length ( fkconstraint - > fk_attrs )
+ list_length ( fkconstraint - > pk_attrs ) ) ) ;
trig . tgargs [ 0 ] = trig . tgname ;
trig . tgargs [ 1 ] = RelationGetRelationName ( rel ) ;
trig . tgargs [ 2 ] = RelationGetRelationName ( pkrel ) ;
trig . tgargs [ 3 ] = fkMatchTypeToString ( fkconstraint - > fk_matchtype ) ;
count = 4 ;
foreach ( list , fkconstraint - > fk_attrs )
{
char * fk_at = strVal ( lfirst ( list ) ) ;
trig . tgargs [ count ] = fk_at ;
count + = 2 ;
}
count = 5 ;
foreach ( list , fkconstraint - > pk_attrs )
{
char * pk_at = strVal ( lfirst ( list ) ) ;
trig . tgargs [ count ] = pk_at ;
count + = 2 ;
}
trig . tgnargs = count - 1 ;
/*
* See if we can do it with a single LEFT JOIN query . A FALSE result
* indicates we must proceed with the fire - the - trigger method .
*/
if ( RI_Initial_Check ( & trig , rel , pkrel ) )
return ;
/*
* Scan through each tuple , calling RI_FKey_check_ins ( insert trigger ) as
* if that tuple had just been inserted . If any of those fail , it should
* ereport ( ERROR ) and that ' s that .
*/
scan = heap_beginscan ( rel , SnapshotNow , 0 , NULL ) ;
while ( ( tuple = heap_getnext ( scan , ForwardScanDirection ) ) ! = NULL )
@ -4613,18 +4409,13 @@ validateForeignKeyConstraint(FkConstraint *fkconstraint,
}
heap_endscan ( scan ) ;
pfree ( trig . tgargs ) ;
}
static void
CreateFKCheckTrigger ( RangeVar * myRel , FkConstraint * fkconstraint ,
ObjectAddress * constrobj , ObjectAddress * trigobj ,
bool on_insert )
Oid constraintOid , bool on_insert )
{
CreateTrigStmt * fk_trigger ;
ListCell * fk_attr ;
ListCell * pk_attr ;
fk_trigger = makeNode ( CreateTrigStmt ) ;
fk_trigger - > trigname = fkconstraint - > constr_name ;
@ -4649,32 +4440,9 @@ CreateFKCheckTrigger(RangeVar *myRel, FkConstraint *fkconstraint,
fk_trigger - > deferrable = fkconstraint - > deferrable ;
fk_trigger - > initdeferred = fkconstraint - > initdeferred ;
fk_trigger - > constrrel = fkconstraint - > pktable ;
fk_trigger - > args = NIL ;
fk_trigger - > args = lappend ( fk_trigger - > args ,
makeString ( fkconstraint - > constr_name ) ) ;
fk_trigger - > args = lappend ( fk_trigger - > args ,
makeString ( myRel - > relname ) ) ;
fk_trigger - > args = lappend ( fk_trigger - > args ,
makeString ( fkconstraint - > pktable - > relname ) ) ;
fk_trigger - > args = lappend ( fk_trigger - > args ,
makeString ( fkMatchTypeToString ( fkconstraint - > fk_matchtype ) ) ) ;
if ( list_length ( fkconstraint - > fk_attrs ) ! = list_length ( fkconstraint - > pk_attrs ) )
ereport ( ERROR ,
( errcode ( ERRCODE_INVALID_FOREIGN_KEY ) ,
errmsg ( " number of referencing and referenced columns for foreign key disagree " ) ) ) ;
forboth ( fk_attr , fkconstraint - > fk_attrs ,
pk_attr , fkconstraint - > pk_attrs )
{
fk_trigger - > args = lappend ( fk_trigger - > args , lfirst ( fk_attr ) ) ;
fk_trigger - > args = lappend ( fk_trigger - > args , lfirst ( pk_attr ) ) ;
}
trigobj - > objectId = CreateTrigger ( fk_trigger , true ) ;
/* Register dependency from trigger to constraint */
recordDependencyOn ( trigobj , constrobj , DEPENDENCY_INTERNAL ) ;
( void ) CreateTrigger ( fk_trigger , constraintOid ) ;
/* Make changes-so-far visible */
CommandCounterIncrement ( ) ;
@ -4685,14 +4453,10 @@ CreateFKCheckTrigger(RangeVar *myRel, FkConstraint *fkconstraint,
*/
static void
createForeignKeyTriggers ( Relation rel , FkConstraint * fkconstraint ,
Oid constrOid )
Oid constraint Oid )
{
RangeVar * myRel ;
CreateTrigStmt * fk_trigger ;
ListCell * fk_attr ;
ListCell * pk_attr ;
ObjectAddress trigobj ,
constrobj ;
/*
* Reconstruct a RangeVar for my relation ( not passed in , unfortunately ) .
@ -4700,15 +4464,6 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
myRel = makeRangeVar ( get_namespace_name ( RelationGetNamespace ( rel ) ) ,
pstrdup ( RelationGetRelationName ( rel ) ) ) ;
/*
* Preset objectAddress fields
*/
constrobj . classId = ConstraintRelationId ;
constrobj . objectId = constrOid ;
constrobj . objectSubId = 0 ;
trigobj . classId = TriggerRelationId ;
trigobj . objectSubId = 0 ;
/* Make changes-so-far visible */
CommandCounterIncrement ( ) ;
@ -4716,8 +4471,8 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
* Build and execute a CREATE CONSTRAINT TRIGGER statement for the CHECK
* action for both INSERTs and UPDATEs on the referencing table .
*/
CreateFKCheckTrigger ( myRel , fkconstraint , & constrobj , & trigobj , true ) ;
CreateFKCheckTrigger ( myRel , fkconstraint , & constrobj , & trigobj , false ) ;
CreateFKCheckTrigger ( myRel , fkconstraint , constraintOid , true ) ;
CreateFKCheckTrigger ( myRel , fkconstraint , constraintOid , false ) ;
/*
* Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
@ -4765,27 +4520,9 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
( int ) fkconstraint - > fk_del_action ) ;
break ;
}
fk_trigger - > args = NIL ;
fk_trigger - > args = lappend ( fk_trigger - > args ,
makeString ( fkconstraint - > constr_name ) ) ;
fk_trigger - > args = lappend ( fk_trigger - > args ,
makeString ( myRel - > relname ) ) ;
fk_trigger - > args = lappend ( fk_trigger - > args ,
makeString ( fkconstraint - > pktable - > relname ) ) ;
fk_trigger - > args = lappend ( fk_trigger - > args ,
makeString ( fkMatchTypeToString ( fkconstraint - > fk_matchtype ) ) ) ;
forboth ( fk_attr , fkconstraint - > fk_attrs ,
pk_attr , fkconstraint - > pk_attrs )
{
fk_trigger - > args = lappend ( fk_trigger - > args , lfirst ( fk_attr ) ) ;
fk_trigger - > args = lappend ( fk_trigger - > args , lfirst ( pk_attr ) ) ;
}
trigobj . objectId = CreateTrigger ( fk_trigger , true ) ;
/* Register dependency from trigger to constraint */
recordDependencyOn ( & trigobj , & constrobj , DEPENDENCY_INTERNAL ) ;
( void ) CreateTrigger ( fk_trigger , constraintOid ) ;
/* Make changes-so-far visible */
CommandCounterIncrement ( ) ;
@ -4835,49 +4572,9 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
( int ) fkconstraint - > fk_upd_action ) ;
break ;
}
fk_trigger - > args = NIL ;
fk_trigger - > args = lappend ( fk_trigger - > args ,
makeString ( fkconstraint - > constr_name ) ) ;
fk_trigger - > args = lappend ( fk_trigger - > args ,
makeString ( myRel - > relname ) ) ;
fk_trigger - > args = lappend ( fk_trigger - > args ,
makeString ( fkconstraint - > pktable - > relname ) ) ;
fk_trigger - > args = lappend ( fk_trigger - > args ,
makeString ( fkMatchTypeToString ( fkconstraint - > fk_matchtype ) ) ) ;
forboth ( fk_attr , fkconstraint - > fk_attrs ,
pk_attr , fkconstraint - > pk_attrs )
{
fk_trigger - > args = lappend ( fk_trigger - > args , lfirst ( fk_attr ) ) ;
fk_trigger - > args = lappend ( fk_trigger - > args , lfirst ( pk_attr ) ) ;
}
trigobj . objectId = CreateTrigger ( fk_trigger , true ) ;
/* Register dependency from trigger to constraint */
recordDependencyOn ( & trigobj , & constrobj , DEPENDENCY_INTERNAL ) ;
}
/*
* fkMatchTypeToString -
* convert FKCONSTR_MATCH_xxx code to string to use in trigger args
*/
static char *
fkMatchTypeToString ( char match_type )
{
switch ( match_type )
{
case FKCONSTR_MATCH_FULL :
return pstrdup ( " FULL " ) ;
case FKCONSTR_MATCH_PARTIAL :
return pstrdup ( " PARTIAL " ) ;
case FKCONSTR_MATCH_UNSPECIFIED :
return pstrdup ( " UNSPECIFIED " ) ;
default :
elog ( ERROR , " unrecognized match type: %d " ,
( int ) match_type ) ;
}
return NULL ; /* can't get here */
( void ) CreateTrigger ( fk_trigger , constraintOid ) ;
}
/*