@ -16,6 +16,7 @@
# include "access/attmap.h"
# include "access/attmap.h"
# include "access/genam.h"
# include "access/genam.h"
# include "access/gist.h"
# include "access/heapam.h"
# include "access/heapam.h"
# include "access/heapam_xlog.h"
# include "access/heapam_xlog.h"
# include "access/multixact.h"
# include "access/multixact.h"
@ -209,6 +210,7 @@ typedef struct NewConstraint
ConstrType contype ; /* CHECK or FOREIGN */
ConstrType contype ; /* CHECK or FOREIGN */
Oid refrelid ; /* PK rel, if FOREIGN */
Oid refrelid ; /* PK rel, if FOREIGN */
Oid refindid ; /* OID of PK's index, if FOREIGN */
Oid refindid ; /* OID of PK's index, if FOREIGN */
bool conwithperiod ; /* Whether the new FOREIGN KEY uses PERIOD */
Oid conid ; /* OID of pg_constraint entry, if FOREIGN */
Oid conid ; /* OID of pg_constraint entry, if FOREIGN */
Node * qual ; /* Check expr or CONSTR_FOREIGN Constraint */
Node * qual ; /* Check expr or CONSTR_FOREIGN Constraint */
ExprState * qualstate ; /* Execution state for CHECK expr */
ExprState * qualstate ; /* Execution state for CHECK expr */
@ -384,16 +386,17 @@ static int transformColumnNameList(Oid relId, List *colList,
static int transformFkeyGetPrimaryKey ( Relation pkrel , Oid * indexOid ,
static int transformFkeyGetPrimaryKey ( Relation pkrel , Oid * indexOid ,
List * * attnamelist ,
List * * attnamelist ,
int16 * attnums , Oid * atttypids ,
int16 * attnums , Oid * atttypids ,
Oid * opclasses ) ;
Oid * opclasses , bool * pk_has_without_overlaps ) ;
static Oid transformFkeyCheckAttrs ( Relation pkrel ,
static Oid transformFkeyCheckAttrs ( Relation pkrel ,
int numattrs , int16 * attnums ,
int numattrs , int16 * attnums ,
Oid * opclasses ) ;
bool with_period , Oid * opclasses ,
bool * pk_has_without_overlaps ) ;
static void checkFkeyPermissions ( Relation rel , int16 * attnums , int natts ) ;
static void checkFkeyPermissions ( Relation rel , int16 * attnums , int natts ) ;
static CoercionPathType findFkeyCast ( Oid targetTypeId , Oid sourceTypeId ,
static CoercionPathType findFkeyCast ( Oid targetTypeId , Oid sourceTypeId ,
Oid * funcid ) ;
Oid * funcid ) ;
static void validateForeignKeyConstraint ( char * conname ,
static void validateForeignKeyConstraint ( char * conname ,
Relation rel , Relation pkrel ,
Relation rel , Relation pkrel ,
Oid pkindOid , Oid constraintOid ) ;
Oid pkindOid , Oid constraintOid , bool hasperiod ) ;
static void ATController ( AlterTableStmt * parsetree ,
static void ATController ( AlterTableStmt * parsetree ,
Relation rel , List * cmds , bool recurse , LOCKMODE lockmode ,
Relation rel , List * cmds , bool recurse , LOCKMODE lockmode ,
AlterTableUtilityContext * context ) ;
AlterTableUtilityContext * context ) ;
@ -506,7 +509,8 @@ static ObjectAddress addFkRecurseReferenced(List **wqueue, Constraint *fkconstra
Oid * pfeqoperators , Oid * ppeqoperators , Oid * ffeqoperators ,
Oid * pfeqoperators , Oid * ppeqoperators , Oid * ffeqoperators ,
int numfkdelsetcols , int16 * fkdelsetcols ,
int numfkdelsetcols , int16 * fkdelsetcols ,
bool old_check_ok ,
bool old_check_ok ,
Oid parentDelTrigger , Oid parentUpdTrigger ) ;
Oid parentDelTrigger , Oid parentUpdTrigger ,
bool with_period ) ;
static void validateFkOnDeleteSetColumns ( int numfks , const int16 * fkattnums ,
static void validateFkOnDeleteSetColumns ( int numfks , const int16 * fkattnums ,
int numfksetcols , const int16 * fksetcolsattnums ,
int numfksetcols , const int16 * fksetcolsattnums ,
List * fksetcols ) ;
List * fksetcols ) ;
@ -516,7 +520,9 @@ static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
Oid * pfeqoperators , Oid * ppeqoperators , Oid * ffeqoperators ,
Oid * pfeqoperators , Oid * ppeqoperators , Oid * ffeqoperators ,
int numfkdelsetcols , int16 * fkdelsetcols ,
int numfkdelsetcols , int16 * fkdelsetcols ,
bool old_check_ok , LOCKMODE lockmode ,
bool old_check_ok , LOCKMODE lockmode ,
Oid parentInsTrigger , Oid parentUpdTrigger ) ;
Oid parentInsTrigger , Oid parentUpdTrigger ,
bool with_period ) ;
static void CloneForeignKeyConstraints ( List * * wqueue , Relation parentRel ,
static void CloneForeignKeyConstraints ( List * * wqueue , Relation parentRel ,
Relation partitionRel ) ;
Relation partitionRel ) ;
static void CloneFkReferenced ( Relation parentRel , Relation partitionRel ) ;
static void CloneFkReferenced ( Relation parentRel , Relation partitionRel ) ;
@ -5958,7 +5964,8 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
validateForeignKeyConstraint ( fkconstraint - > conname , rel , refrel ,
validateForeignKeyConstraint ( fkconstraint - > conname , rel , refrel ,
con - > refindid ,
con - > refindid ,
con - > conid ) ;
con - > conid ,
con - > conwithperiod ) ;
/*
/*
* No need to mark the constraint row as validated , we did
* No need to mark the constraint row as validated , we did
@ -9809,6 +9816,8 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
Oid ppeqoperators [ INDEX_MAX_KEYS ] = { 0 } ;
Oid ppeqoperators [ INDEX_MAX_KEYS ] = { 0 } ;
Oid ffeqoperators [ INDEX_MAX_KEYS ] = { 0 } ;
Oid ffeqoperators [ INDEX_MAX_KEYS ] = { 0 } ;
int16 fkdelsetcols [ INDEX_MAX_KEYS ] = { 0 } ;
int16 fkdelsetcols [ INDEX_MAX_KEYS ] = { 0 } ;
bool with_period ;
bool pk_has_without_overlaps ;
int i ;
int i ;
int numfks ,
int numfks ,
numpks ,
numpks ,
@ -9903,6 +9912,11 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
numfks = transformColumnNameList ( RelationGetRelid ( rel ) ,
numfks = transformColumnNameList ( RelationGetRelid ( rel ) ,
fkconstraint - > fk_attrs ,
fkconstraint - > fk_attrs ,
fkattnum , fktypoid ) ;
fkattnum , fktypoid ) ;
with_period = fkconstraint - > fk_with_period | | fkconstraint - > pk_with_period ;
if ( with_period & & ! fkconstraint - > fk_with_period )
ereport ( ERROR ,
errcode ( ERRCODE_INVALID_FOREIGN_KEY ) ,
errmsg ( " foreign key uses PERIOD on the referenced table but not the referencing table " ) ) ;
numfkdelsetcols = transformColumnNameList ( RelationGetRelid ( rel ) ,
numfkdelsetcols = transformColumnNameList ( RelationGetRelid ( rel ) ,
fkconstraint - > fk_del_set_cols ,
fkconstraint - > fk_del_set_cols ,
@ -9922,18 +9936,40 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
numpks = transformFkeyGetPrimaryKey ( pkrel , & indexOid ,
numpks = transformFkeyGetPrimaryKey ( pkrel , & indexOid ,
& fkconstraint - > pk_attrs ,
& fkconstraint - > pk_attrs ,
pkattnum , pktypoid ,
pkattnum , pktypoid ,
opclasses ) ;
opclasses , & pk_has_without_overlaps ) ;
/* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
if ( pk_has_without_overlaps & & ! fkconstraint - > fk_with_period )
ereport ( ERROR ,
errcode ( ERRCODE_INVALID_FOREIGN_KEY ) ,
errmsg ( " foreign key uses PERIOD on the referenced table but not the referencing table " ) ) ;
}
}
else
else
{
{
numpks = transformColumnNameList ( RelationGetRelid ( pkrel ) ,
numpks = transformColumnNameList ( RelationGetRelid ( pkrel ) ,
fkconstraint - > pk_attrs ,
fkconstraint - > pk_attrs ,
pkattnum , pktypoid ) ;
pkattnum , pktypoid ) ;
/* Since we got pk_attrs, one should be a period. */
if ( with_period & & ! fkconstraint - > pk_with_period )
ereport ( ERROR ,
errcode ( ERRCODE_INVALID_FOREIGN_KEY ) ,
errmsg ( " foreign key uses PERIOD on the referencing table but not the referenced table " ) ) ;
/* Look for an index matching the column list */
/* Look for an index matching the column list */
indexOid = transformFkeyCheckAttrs ( pkrel , numpks , pkattnum ,
indexOid = transformFkeyCheckAttrs ( pkrel , numpks , pkattnum ,
opclasses ) ;
with_period , opclasses , & pk_has_without_overlap s ) ;
}
}
/*
* If the referenced primary key has WITHOUT OVERLAPS , the foreign key
* must use PERIOD .
*/
if ( pk_has_without_overlaps & & ! with_period )
ereport ( ERROR ,
errcode ( ERRCODE_INVALID_FOREIGN_KEY ) ,
errmsg ( " foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS " ) ) ;
/*
/*
* Now we can check permissions .
* Now we can check permissions .
*/
*/
@ -9967,6 +10003,28 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
}
}
}
}
/*
* Some actions are currently unsupported for foreign keys using PERIOD .
*/
if ( fkconstraint - > fk_with_period )
{
if ( fkconstraint - > fk_upd_action = = FKCONSTR_ACTION_CASCADE | |
fkconstraint - > fk_upd_action = = FKCONSTR_ACTION_SETNULL | |
fkconstraint - > fk_upd_action = = FKCONSTR_ACTION_SETDEFAULT )
ereport ( ERROR ,
errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
errmsg ( " unsupported %s action for foreign key constraint using PERIOD " ,
" ON UPDATE " ) ) ;
if ( fkconstraint - > fk_del_action = = FKCONSTR_ACTION_CASCADE | |
fkconstraint - > fk_del_action = = FKCONSTR_ACTION_SETNULL | |
fkconstraint - > fk_del_action = = FKCONSTR_ACTION_SETDEFAULT )
ereport ( ERROR ,
errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
errmsg ( " unsupported %s action for foreign key constraint using PERIOD " ,
" ON DELETE " ) ) ;
}
/*
/*
* Look up the equality operators to use in the constraint .
* Look up the equality operators to use in the constraint .
*
*
@ -10013,16 +10071,56 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
opcintype = cla_tup - > opcintype ;
opcintype = cla_tup - > opcintype ;
ReleaseSysCache ( cla_ht ) ;
ReleaseSysCache ( cla_ht ) ;
/*
if ( with_period )
* 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
StrategyNumber rtstrategy ;
* of unique indexes , we ' d need a way to determine which operator
bool for_overlaps = with_period & & i = = numpks - 1 ;
* strategy number is equality . ( Is it reasonable to insist that
* every such index AM use btree ' s number for equality ? )
/*
*/
* GiST indexes are required to support temporal foreign keys
if ( amid ! = BTREE_AM_OID )
* because they combine equals and overlaps .
elog ( ERROR , " only b-tree indexes are supported for foreign keys " ) ;
*/
eqstrategy = BTEqualStrategyNumber ;
if ( amid ! = GIST_AM_OID )
elog ( ERROR , " only GiST indexes are supported for temporal foreign keys " ) ;
rtstrategy = for_overlaps ? RTOverlapStrategyNumber : RTEqualStrategyNumber ;
/*
* An opclass can use whatever strategy numbers it wants , so we
* ask the opclass what number it actually uses instead of our RT *
* constants .
*/
eqstrategy = GistTranslateStratnum ( opclasses [ i ] , rtstrategy ) ;
if ( eqstrategy = = InvalidStrategy )
{
HeapTuple tuple ;
tuple = SearchSysCache1 ( CLAOID , ObjectIdGetDatum ( opclasses [ i ] ) ) ;
if ( ! HeapTupleIsValid ( tuple ) )
elog ( ERROR , " cache lookup failed for operator class %u " , opclasses [ i ] ) ;
ereport ( ERROR ,
errcode ( ERRCODE_UNDEFINED_OBJECT ) ,
for_overlaps
? errmsg ( " could not identify an overlaps operator for foreign key " )
: errmsg ( " could not identify an equality operator for foreign key " ) ,
errdetail ( " Could not translate strategy number %d for operator class \" %s \" for access method \" %s \" . " ,
rtstrategy , NameStr ( ( ( Form_pg_opclass ) GETSTRUCT ( tuple ) ) - > opcname ) , " gist " ) ) ;
}
}
else
{
/*
* 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 . ( We could use
* something like GistTranslateStratnum . )
*/
if ( amid ! = BTREE_AM_OID )
elog ( ERROR , " only b-tree indexes are supported for foreign keys " ) ;
eqstrategy = BTEqualStrategyNumber ;
}
/*
/*
* There had better be a primary equality operator for the index .
* There had better be a primary equality operator for the index .
@ -10172,6 +10270,22 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
ffeqoperators [ i ] = ffeqop ;
ffeqoperators [ i ] = ffeqop ;
}
}
/*
* For FKs with PERIOD we need additional operators to check whether the
* referencing row ' s range is contained by the aggregated ranges of the
* referenced row ( s ) . For rangetypes and multirangetypes this is
* fk . periodatt < @ range_agg ( pk . periodatt ) . Those are the only types we
* support for now . FKs will look these up at " runtime " , but we should
* make sure the lookup works here , even if we don ' t use the values .
*/
if ( with_period )
{
Oid periodoperoid ;
Oid aggedperiodoperoid ;
FindFKPeriodOpers ( opclasses [ numpks - 1 ] , & periodoperoid , & aggedperiodoperoid ) ;
}
/*
/*
* Create all the constraint and trigger objects , recursing to partitions
* Create all the constraint and trigger objects , recursing to partitions
* as necessary . First handle the referenced side .
* as necessary . First handle the referenced side .
@ -10188,7 +10302,8 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
numfkdelsetcols ,
numfkdelsetcols ,
fkdelsetcols ,
fkdelsetcols ,
old_check_ok ,
old_check_ok ,
InvalidOid , InvalidOid ) ;
InvalidOid , InvalidOid ,
with_period ) ;
/* Now handle the referencing side. */
/* Now handle the referencing side. */
addFkRecurseReferencing ( wqueue , fkconstraint , rel , pkrel ,
addFkRecurseReferencing ( wqueue , fkconstraint , rel , pkrel ,
@ -10204,7 +10319,8 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
fkdelsetcols ,
fkdelsetcols ,
old_check_ok ,
old_check_ok ,
lockmode ,
lockmode ,
InvalidOid , InvalidOid ) ;
InvalidOid , InvalidOid ,
with_period ) ;
/*
/*
* Done . Close pk table , but keep lock until we ' ve committed .
* Done . Close pk table , but keep lock until we ' ve committed .
@ -10289,7 +10405,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
Oid * ppeqoperators , Oid * ffeqoperators ,
Oid * ppeqoperators , Oid * ffeqoperators ,
int numfkdelsetcols , int16 * fkdelsetcols ,
int numfkdelsetcols , int16 * fkdelsetcols ,
bool old_check_ok ,
bool old_check_ok ,
Oid parentDelTrigger , Oid parentUpdTrigger )
Oid parentDelTrigger , Oid parentUpdTrigger ,
bool with_period )
{
{
ObjectAddress address ;
ObjectAddress address ;
Oid constrOid ;
Oid constrOid ;
@ -10375,7 +10492,7 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
conislocal , /* islocal */
conislocal , /* islocal */
coninhcount , /* inhcount */
coninhcount , /* inhcount */
connoinherit , /* conNoInherit */
connoinherit , /* conNoInherit */
false , /* conPeriod */
with_period , /* conPeriod */
false ) ; /* is_internal */
false ) ; /* is_internal */
ObjectAddressSet ( address , ConstraintRelationId , constrOid ) ;
ObjectAddressSet ( address , ConstraintRelationId , constrOid ) ;
@ -10451,7 +10568,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
pfeqoperators , ppeqoperators , ffeqoperators ,
pfeqoperators , ppeqoperators , ffeqoperators ,
numfkdelsetcols , fkdelsetcols ,
numfkdelsetcols , fkdelsetcols ,
old_check_ok ,
old_check_ok ,
deleteTriggerOid , updateTriggerOid ) ;
deleteTriggerOid , updateTriggerOid ,
with_period ) ;
/* Done -- clean up (but keep the lock) */
/* Done -- clean up (but keep the lock) */
table_close ( partRel , NoLock ) ;
table_close ( partRel , NoLock ) ;
@ -10509,7 +10627,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
Oid * pfeqoperators , Oid * ppeqoperators , Oid * ffeqoperators ,
Oid * pfeqoperators , Oid * ppeqoperators , Oid * ffeqoperators ,
int numfkdelsetcols , int16 * fkdelsetcols ,
int numfkdelsetcols , int16 * fkdelsetcols ,
bool old_check_ok , LOCKMODE lockmode ,
bool old_check_ok , LOCKMODE lockmode ,
Oid parentInsTrigger , Oid parentUpdTrigger )
Oid parentInsTrigger , Oid parentUpdTrigger ,
bool with_period )
{
{
Oid insertTriggerOid ,
Oid insertTriggerOid ,
updateTriggerOid ;
updateTriggerOid ;
@ -10557,6 +10676,7 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
newcon - > refrelid = RelationGetRelid ( pkrel ) ;
newcon - > refrelid = RelationGetRelid ( pkrel ) ;
newcon - > refindid = indexOid ;
newcon - > refindid = indexOid ;
newcon - > conid = parentConstr ;
newcon - > conid = parentConstr ;
newcon - > conwithperiod = fkconstraint - > fk_with_period ;
newcon - > qual = ( Node * ) fkconstraint ;
newcon - > qual = ( Node * ) fkconstraint ;
tab - > constraints = lappend ( tab - > constraints , newcon ) ;
tab - > constraints = lappend ( tab - > constraints , newcon ) ;
@ -10674,7 +10794,7 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
false ,
false ,
1 ,
1 ,
false ,
false ,
false , /* conPeriod */
with_period , /* conPeriod */
false ) ;
false ) ;
/*
/*
@ -10705,7 +10825,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
old_check_ok ,
old_check_ok ,
lockmode ,
lockmode ,
insertTriggerOid ,
insertTriggerOid ,
updateTriggerOid ) ;
updateTriggerOid ,
with_period ) ;
table_close ( partition , NoLock ) ;
table_close ( partition , NoLock ) ;
}
}
@ -10941,7 +11062,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel)
confdelsetcols ,
confdelsetcols ,
true ,
true ,
deleteTriggerOid ,
deleteTriggerOid ,
updateTriggerOid ) ;
updateTriggerOid ,
constrForm - > conperiod ) ;
table_close ( fkRel , NoLock ) ;
table_close ( fkRel , NoLock ) ;
ReleaseSysCache ( tuple ) ;
ReleaseSysCache ( tuple ) ;
@ -11034,6 +11156,7 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
ListCell * lc ;
ListCell * lc ;
Oid insertTriggerOid ,
Oid insertTriggerOid ,
updateTriggerOid ;
updateTriggerOid ;
bool with_period ;
tuple = SearchSysCache1 ( CONSTROID , ObjectIdGetDatum ( parentConstrOid ) ) ;
tuple = SearchSysCache1 ( CONSTROID , ObjectIdGetDatum ( parentConstrOid ) ) ;
if ( ! HeapTupleIsValid ( tuple ) )
if ( ! HeapTupleIsValid ( tuple ) )
@ -11149,6 +11272,7 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
fkconstraint - > conname = pstrdup ( NameStr ( constrForm - > conname ) ) ;
fkconstraint - > conname = pstrdup ( NameStr ( constrForm - > conname ) ) ;
indexOid = constrForm - > conindid ;
indexOid = constrForm - > conindid ;
with_period = constrForm - > conperiod ;
constrOid =
constrOid =
CreateConstraintEntry ( fkconstraint - > conname ,
CreateConstraintEntry ( fkconstraint - > conname ,
constrForm - > connamespace ,
constrForm - > connamespace ,
@ -11180,7 +11304,7 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
false , /* islocal */
false , /* islocal */
1 , /* inhcount */
1 , /* inhcount */
false , /* conNoInherit */
false , /* conNoInherit */
false , /* conPeriod */
with_period , /* conPeriod */
true ) ;
true ) ;
/* Set up partition dependencies for the new constraint */
/* Set up partition dependencies for the new constraint */
@ -11214,7 +11338,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
false , /* no old check exists */
false , /* no old check exists */
AccessExclusiveLock ,
AccessExclusiveLock ,
insertTriggerOid ,
insertTriggerOid ,
updateTriggerOid ) ;
updateTriggerOid ,
with_period ) ;
table_close ( pkrel , NoLock ) ;
table_close ( pkrel , NoLock ) ;
}
}
@ -12024,7 +12149,8 @@ transformColumnNameList(Oid relId, List *colList,
*
*
* Look up the names , attnums , and types of the primary key attributes
* Look up the names , attnums , and types of the primary key attributes
* for the pkrel . Also return the index OID and index opclasses of the
* for the pkrel . Also return the index OID and index opclasses of the
* index supporting the primary key .
* index supporting the primary key . Also return whether the index has
* WITHOUT OVERLAPS .
*
*
* All parameters except pkrel are output parameters . Also , the function
* All parameters except pkrel are output parameters . Also , the function
* return value is the number of attributes in the primary key .
* return value is the number of attributes in the primary key .
@ -12035,7 +12161,7 @@ static int
transformFkeyGetPrimaryKey ( Relation pkrel , Oid * indexOid ,
transformFkeyGetPrimaryKey ( Relation pkrel , Oid * indexOid ,
List * * attnamelist ,
List * * attnamelist ,
int16 * attnums , Oid * atttypids ,
int16 * attnums , Oid * atttypids ,
Oid * opclasses )
Oid * opclasses , bool * pk_has_without_overlaps )
{
{
List * indexoidlist ;
List * indexoidlist ;
ListCell * indexoidscan ;
ListCell * indexoidscan ;
@ -12113,6 +12239,8 @@ transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
makeString ( pstrdup ( NameStr ( * attnumAttName ( pkrel , pkattno ) ) ) ) ) ;
makeString ( pstrdup ( NameStr ( * attnumAttName ( pkrel , pkattno ) ) ) ) ) ;
}
}
* pk_has_without_overlaps = indexStruct - > indisexclusion ;
ReleaseSysCache ( indexTuple ) ;
ReleaseSysCache ( indexTuple ) ;
return i ;
return i ;
@ -12126,14 +12254,16 @@ transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
*
*
* Returns the OID of the unique index supporting the constraint and
* Returns the OID of the unique index supporting the constraint and
* populates the caller - provided ' opclasses ' array with the opclasses
* populates the caller - provided ' opclasses ' array with the opclasses
* associated with the index columns .
* associated with the index columns . Also sets whether the index
* uses WITHOUT OVERLAPS .
*
*
* Raises an ERROR on validation failure .
* Raises an ERROR on validation failure .
*/
*/
static Oid
static Oid
transformFkeyCheckAttrs ( Relation pkrel ,
transformFkeyCheckAttrs ( Relation pkrel ,
int numattrs , int16 * attnums ,
int numattrs , int16 * attnums ,
Oid * opclasses )
bool with_period , Oid * opclasses ,
bool * pk_has_without_overlaps )
{
{
Oid indexoid = InvalidOid ;
Oid indexoid = InvalidOid ;
bool found = false ;
bool found = false ;
@ -12180,12 +12310,12 @@ transformFkeyCheckAttrs(Relation pkrel,
indexStruct = ( Form_pg_index ) GETSTRUCT ( indexTuple ) ;
indexStruct = ( Form_pg_index ) GETSTRUCT ( indexTuple ) ;
/*
/*
* Must have the right number of columns ; must be unique and not a
* Must have the right number of columns ; must be unique ( or if
* partial index ; forget it if there are any expressions , too . Invalid
* temporal then exclusion instead ) and not a partial index ; forget it
* indexes are out as well .
* if there are any expressions , too . Invalid indexes are out as well .
*/
*/
if ( indexStruct - > indnkeyatts = = numattrs & &
if ( indexStruct - > indnkeyatts = = numattrs & &
indexStruct - > indisunique & &
( with_period ? indexStruct - > indisexclusion : indexStruct - > indisunique ) & &
indexStruct - > indisvalid & &
indexStruct - > indisvalid & &
heap_attisnull ( indexTuple , Anum_pg_index_indpred , NULL ) & &
heap_attisnull ( indexTuple , Anum_pg_index_indpred , NULL ) & &
heap_attisnull ( indexTuple , Anum_pg_index_indexprs , NULL ) )
heap_attisnull ( indexTuple , Anum_pg_index_indexprs , NULL ) )
@ -12223,6 +12353,13 @@ transformFkeyCheckAttrs(Relation pkrel,
if ( ! found )
if ( ! found )
break ;
break ;
}
}
/* The last attribute in the index must be the PERIOD FK part */
if ( found & & with_period )
{
int16 periodattnum = attnums [ numattrs - 1 ] ;
found = ( periodattnum = = indexStruct - > indkey . values [ numattrs - 1 ] ) ;
}
/*
/*
* Refuse to use a deferrable unique / primary key . This is per SQL
* Refuse to use a deferrable unique / primary key . This is per SQL
@ -12238,6 +12375,10 @@ transformFkeyCheckAttrs(Relation pkrel,
found_deferrable = true ;
found_deferrable = true ;
found = false ;
found = false ;
}
}
/* We need to know whether the index has WITHOUT OVERLAPS */
if ( found )
* pk_has_without_overlaps = indexStruct - > indisexclusion ;
}
}
ReleaseSysCache ( indexTuple ) ;
ReleaseSysCache ( indexTuple ) ;
if ( found )
if ( found )
@ -12332,7 +12473,8 @@ validateForeignKeyConstraint(char *conname,
Relation rel ,
Relation rel ,
Relation pkrel ,
Relation pkrel ,
Oid pkindOid ,
Oid pkindOid ,
Oid constraintOid )
Oid constraintOid ,
bool hasperiod )
{
{
TupleTableSlot * slot ;
TupleTableSlot * slot ;
TableScanDesc scan ;
TableScanDesc scan ;
@ -12360,9 +12502,11 @@ validateForeignKeyConstraint(char *conname,
/*
/*
* See if we can do it with a single LEFT JOIN query . A false result
* 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 .
* indicates we must proceed with the fire - the - trigger method . We can ' t do
* a LEFT JOIN for temporal FKs yet , but we can once we support temporal
* left joins .
*/
*/
if ( RI_Initial_Check ( & trig , rel , pkrel ) )
if ( ! hasperiod & & RI_Initial_Check ( & trig , rel , pkrel ) )
return ;
return ;
/*
/*
@ -12513,6 +12657,7 @@ createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstr
fk_trigger - > whenClause = NULL ;
fk_trigger - > whenClause = NULL ;
fk_trigger - > transitionRels = NIL ;
fk_trigger - > transitionRels = NIL ;
fk_trigger - > constrrel = NULL ;
fk_trigger - > constrrel = NULL ;
switch ( fkconstraint - > fk_del_action )
switch ( fkconstraint - > fk_del_action )
{
{
case FKCONSTR_ACTION_NOACTION :
case FKCONSTR_ACTION_NOACTION :
@ -12573,6 +12718,7 @@ createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstr
fk_trigger - > whenClause = NULL ;
fk_trigger - > whenClause = NULL ;
fk_trigger - > transitionRels = NIL ;
fk_trigger - > transitionRels = NIL ;
fk_trigger - > constrrel = NULL ;
fk_trigger - > constrrel = NULL ;
switch ( fkconstraint - > fk_upd_action )
switch ( fkconstraint - > fk_upd_action )
{
{
case FKCONSTR_ACTION_NOACTION :
case FKCONSTR_ACTION_NOACTION :