@ -160,7 +160,7 @@ typedef struct AlteredTableInfo
/* Information saved by Phases 1/2 for Phase 3: */
List * constraints ; /* List of NewConstraint */
List * newvals ; /* List of NewColumnValue */
bool new_notnull ; /* T if we added new NOT NULL constraints */
bool verify_new_notnull ; /* T if we should recheck NOT NULL */
int rewrite ; /* Reason for forced rewrite, if any */
Oid newTableSpace ; /* new tablespace; 0 means no change */
bool chgPersistence ; /* T if SET LOGGED/UNLOGGED is used */
@ -372,6 +372,9 @@ static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, LOCKMO
static void ATPrepSetNotNull ( Relation rel , bool recurse , bool recursing ) ;
static ObjectAddress ATExecSetNotNull ( AlteredTableInfo * tab , Relation rel ,
const char * colName , LOCKMODE lockmode ) ;
static bool NotNullImpliedByRelConstraints ( Relation rel , Form_pg_attribute attr ) ;
static bool ConstraintImpliedByRelConstraint ( Relation scanrel ,
List * partConstraint , List * existedConstraints ) ;
static ObjectAddress ATExecColumnDefault ( Relation rel , const char * colName ,
Node * newDefault , LOCKMODE lockmode ) ;
static ObjectAddress ATExecAddIdentity ( Relation rel , const char * colName ,
@ -4550,10 +4553,11 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
else
{
/*
* Test the current data within the table against new constraints
* generated by ALTER TABLE commands , but don ' t rebuild data .
* If required , test the current data within the table against new
* constraints generated by ALTER TABLE commands , but don ' t rebuild
* data .
*/
if ( tab - > constraints ! = NIL | | tab - > new_notnull | |
if ( tab - > constraints ! = NIL | | tab - > verify_ new_notnull | |
tab - > partition_constraint ! = NULL )
ATRewriteTable ( tab , InvalidOid , lockmode ) ;
@ -4714,13 +4718,13 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
}
notnull_attrs = NIL ;
if ( newrel | | tab - > new_notnull )
if ( newrel | | tab - > verify_ new_notnull)
{
/*
* If we are rebuilding the tuples OR if we added any new NOT NULL
* constraints , check all not - null constraints . This is a bit of
* overkill but it minimizes risk of bugs , and heap_attisnull is a
* pretty cheap test anyway .
* If we are rebuilding the tuples OR if we added any new but not
* verified NOT NULL constraints , check all not - null constraints .
* This is a bit of overkill but it minimizes risk of bugs , and
* heap_attisnull is a pretty cheap test anyway .
*/
for ( i = 0 ; i < newTupDesc - > natts ; i + + )
{
@ -5749,11 +5753,9 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
{
/*
* If the new column is NOT NULL , and there is no missing value ,
* tell Phase 3 it needs to test that . ( Note we don ' t do this for
* an OID column . OID will be marked not null , but since it ' s
* filled specially , there ' s no need to test anything . )
* tell Phase 3 it needs to check for NULLs .
*/
tab - > new_notnull | = colDef - > is_not_null ;
tab - > verify_new_notnull | = colDef - > is_not_null ;
}
}
@ -6121,8 +6123,19 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
CatalogTupleUpdate ( attr_rel , & tuple - > t_self , tuple ) ;
/* Tell Phase 3 it needs to test the constraint */
tab - > new_notnull = true ;
/*
* Ordinarily phase 3 must ensure that no NULLs exist in columns that
* are set NOT NULL ; however , if we can find a constraint which proves
* this then we can skip that . We needn ' t bother looking if
* we ' ve already found that we must verify some other NOT NULL
* constraint .
*/
if ( ! tab - > verify_new_notnull & &
! NotNullImpliedByRelConstraints ( rel , ( Form_pg_attribute ) GETSTRUCT ( tuple ) ) )
{
/* Tell Phase 3 it needs to test the constraint */
tab - > verify_new_notnull = true ;
}
ObjectAddressSubSet ( address , RelationRelationId ,
RelationGetRelid ( rel ) , attnum ) ;
@ -6138,6 +6151,42 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
return address ;
}
/*
* NotNullImpliedByRelConstraints
* Does rel ' s existing constraints imply NOT NULL for the given attribute ?
*/
static bool
NotNullImpliedByRelConstraints ( Relation rel , Form_pg_attribute attr )
{
NullTest * nnulltest = makeNode ( NullTest ) ;
nnulltest - > arg = ( Expr * ) makeVar ( 1 ,
attr - > attnum ,
attr - > atttypid ,
attr - > atttypmod ,
attr - > attcollation ,
0 ) ;
nnulltest - > nulltesttype = IS_NOT_NULL ;
/*
* argisrow = false is correct even for a composite column , because
* attnotnull does not represent a SQL - spec IS NOT NULL test in such a
* case , just IS DISTINCT FROM NULL .
*/
nnulltest - > argisrow = false ;
nnulltest - > location = - 1 ;
if ( ConstraintImpliedByRelConstraint ( rel , list_make1 ( nnulltest ) , NIL ) )
{
ereport ( DEBUG1 ,
( errmsg ( " existing constraints on column \" %s \" . \" %s \" are sufficient to prove that it does not contain nulls " ,
RelationGetRelationName ( rel ) , NameStr ( attr - > attname ) ) ) ) ;
return true ;
}
return false ;
}
/*
* ALTER TABLE ALTER COLUMN SET / DROP DEFAULT
*
@ -14416,8 +14465,7 @@ PartConstraintImpliedByRelConstraint(Relation scanrel,
{
List * existConstraint = NIL ;
TupleConstr * constr = RelationGetDescr ( scanrel ) - > constr ;
int num_check ,
i ;
int i ;
if ( constr & & constr - > has_not_null )
{
@ -14451,6 +14499,27 @@ PartConstraintImpliedByRelConstraint(Relation scanrel,
}
}
return ConstraintImpliedByRelConstraint ( scanrel , partConstraint , existConstraint ) ;
}
/*
* ConstraintImpliedByRelConstraint
* Do scanrel ' s existing constraints imply the given constraint ?
*
* testConstraint is the constraint to validate . provenConstraint is a
* caller - provided list of conditions which this function may assume
* to be true . Both provenConstraint and testConstraint must be in
* implicit - AND form , must only contain immutable clauses , and must
* contain only Vars with varno = 1.
*/
bool
ConstraintImpliedByRelConstraint ( Relation scanrel , List * testConstraint , List * provenConstraint )
{
List * existConstraint = list_copy ( provenConstraint ) ;
TupleConstr * constr = RelationGetDescr ( scanrel ) - > constr ;
int num_check ,
i ;
num_check = ( constr ! = NULL ) ? constr - > num_check : 0 ;
for ( i = 0 ; i < num_check ; i + + )
{
@ -14481,13 +14550,13 @@ PartConstraintImpliedByRelConstraint(Relation scanrel,
/*
* Try to make the proof . Since we are comparing CHECK constraints , we
* need to use weak implication , i . e . , we assume existConstraint is
* not - false and try to prove the same for par tConstraint.
* not - false and try to prove the same for tes tConstraint.
*
* Note that predicate_implied_by assumes its first argument is known
* immutable . That should always be true for partition constraints , so we
* don ' t test it here .
* immutable . That should always be true for both NOT NULL and
* partition constraints , so we don ' t test it here .
*/
return predicate_implied_by ( par tConstraint, existConstraint , true ) ;
return predicate_implied_by ( tes tConstraint, existConstraint , true ) ;
}
/*