@@ -160,7 +160,7 @@ typedef struct AlteredTableInfo
160160/* Information saved by Phases 1/2 for Phase 3: */
161161List * constraints ;/* List of NewConstraint */
162162List * newvals ;/* List of NewColumnValue */
163- bool new_notnull ; /* T if weadded new NOT NULL constraints */
163+ bool verify_new_notnull ; /* T if weshould recheck NOT NULL */
164164int rewrite ;/* Reason for forced rewrite, if any */
165165Oid newTableSpace ;/* new tablespace; 0 means no change */
166166bool chgPersistence ;/* T if SET LOGGED/UNLOGGED is used */
@@ -372,6 +372,9 @@ static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, LOCKMO
372372static void ATPrepSetNotNull (Relation rel ,bool recurse ,bool recursing );
373373static ObjectAddress ATExecSetNotNull (AlteredTableInfo * tab ,Relation rel ,
374374const char * colName ,LOCKMODE lockmode );
375+ static bool NotNullImpliedByRelConstraints (Relation rel ,Form_pg_attribute attr );
376+ static bool ConstraintImpliedByRelConstraint (Relation scanrel ,
377+ List * partConstraint ,List * existedConstraints );
375378static ObjectAddress ATExecColumnDefault (Relation rel ,const char * colName ,
376379Node * newDefault ,LOCKMODE lockmode );
377380static ObjectAddress ATExecAddIdentity (Relation rel ,const char * colName ,
@@ -4550,10 +4553,11 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
45504553else
45514554{
45524555/*
4553- * Test the current data within the table against new constraints
4554- * generated by ALTER TABLE commands, but don't rebuild data.
4556+ * If required, test the current data within the table against new
4557+ * constraints generated by ALTER TABLE commands, but don't rebuild
4558+ * data.
45554559 */
4556- if (tab -> constraints != NIL || tab -> new_notnull ||
4560+ if (tab -> constraints != NIL || tab -> verify_new_notnull ||
45574561tab -> partition_constraint != NULL )
45584562ATRewriteTable (tab ,InvalidOid ,lockmode );
45594563
@@ -4714,13 +4718,13 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
47144718}
47154719
47164720notnull_attrs = NIL ;
4717- if (newrel || tab -> new_notnull )
4721+ if (newrel || tab -> verify_new_notnull )
47184722{
47194723/*
4720- * If we are rebuilding the tuples OR if we added any newNOT NULL
4721- * constraints, check all not-null constraints. This is a bit of
4722- * overkill but it minimizes risk of bugs, and heap_attisnull is a
4723- * pretty cheap test anyway.
4724+ * If we are rebuilding the tuples OR if we added any newbut not
4725+ *verified NOT NULL constraints, check all not-null constraints.
4726+ *This is a bit of overkill but it minimizes risk of bugs, and
4727+ *heap_attisnull is a pretty cheap test anyway.
47244728 */
47254729for (i = 0 ;i < newTupDesc -> natts ;i ++ )
47264730{
@@ -5749,11 +5753,9 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
57495753{
57505754/*
57515755 * If the new column is NOT NULL, and there is no missing value,
5752- * tell Phase 3 it needs to test that. (Note we don't do this for
5753- * an OID column. OID will be marked not null, but since it's
5754- * filled specially, there's no need to test anything.)
5756+ * tell Phase 3 it needs to check for NULLs.
57555757 */
5756- tab -> new_notnull |=colDef -> is_not_null ;
5758+ tab -> verify_new_notnull |=colDef -> is_not_null ;
57575759}
57585760}
57595761
@@ -6121,8 +6123,19 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
61216123
61226124CatalogTupleUpdate (attr_rel ,& tuple -> t_self ,tuple );
61236125
6124- /* Tell Phase 3 it needs to test the constraint */
6125- tab -> new_notnull = true;
6126+ /*
6127+ * Ordinarily phase 3 must ensure that no NULLs exist in columns that
6128+ * are set NOT NULL; however, if we can find a constraint which proves
6129+ * this then we can skip that. We needn't bother looking if
6130+ * we've already found that we must verify some other NOT NULL
6131+ * constraint.
6132+ */
6133+ if (!tab -> verify_new_notnull &&
6134+ !NotNullImpliedByRelConstraints (rel , (Form_pg_attribute )GETSTRUCT (tuple )))
6135+ {
6136+ /* Tell Phase 3 it needs to test the constraint */
6137+ tab -> verify_new_notnull = true;
6138+ }
61266139
61276140ObjectAddressSubSet (address ,RelationRelationId ,
61286141RelationGetRelid (rel ),attnum );
@@ -6138,6 +6151,42 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
61386151return address ;
61396152}
61406153
6154+ /*
6155+ * NotNullImpliedByRelConstraints
6156+ *Does rel's existing constraints imply NOT NULL for the given attribute?
6157+ */
6158+ static bool
6159+ NotNullImpliedByRelConstraints (Relation rel ,Form_pg_attribute attr )
6160+ {
6161+ NullTest * nnulltest = makeNode (NullTest );
6162+
6163+ nnulltest -> arg = (Expr * )makeVar (1 ,
6164+ attr -> attnum ,
6165+ attr -> atttypid ,
6166+ attr -> atttypmod ,
6167+ attr -> attcollation ,
6168+ 0 );
6169+ nnulltest -> nulltesttype = IS_NOT_NULL ;
6170+
6171+ /*
6172+ * argisrow = false is correct even for a composite column, because
6173+ * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
6174+ * case, just IS DISTINCT FROM NULL.
6175+ */
6176+ nnulltest -> argisrow = false;
6177+ nnulltest -> location = -1 ;
6178+
6179+ if (ConstraintImpliedByRelConstraint (rel ,list_make1 (nnulltest ),NIL ))
6180+ {
6181+ ereport (DEBUG1 ,
6182+ (errmsg ("existing constraints on column \"%s\".\"%s\" are sufficient to prove that it does not contain nulls" ,
6183+ RelationGetRelationName (rel ),NameStr (attr -> attname ))));
6184+ return true;
6185+ }
6186+
6187+ return false;
6188+ }
6189+
61416190/*
61426191 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
61436192 *
@@ -14416,8 +14465,7 @@ PartConstraintImpliedByRelConstraint(Relation scanrel,
1441614465{
1441714466List * existConstraint = NIL ;
1441814467TupleConstr * constr = RelationGetDescr (scanrel )-> constr ;
14419- int num_check ,
14420- i ;
14468+ int i ;
1442114469
1442214470if (constr && constr -> has_not_null )
1442314471{
@@ -14451,6 +14499,27 @@ PartConstraintImpliedByRelConstraint(Relation scanrel,
1445114499}
1445214500}
1445314501
14502+ return ConstraintImpliedByRelConstraint (scanrel ,partConstraint ,existConstraint );
14503+ }
14504+
14505+ /*
14506+ * ConstraintImpliedByRelConstraint
14507+ *Do scanrel's existing constraints imply the given constraint?
14508+ *
14509+ * testConstraint is the constraint to validate. provenConstraint is a
14510+ * caller-provided list of conditions which this function may assume
14511+ * to be true. Both provenConstraint and testConstraint must be in
14512+ * implicit-AND form, must only contain immutable clauses, and must
14513+ * contain only Vars with varno = 1.
14514+ */
14515+ bool
14516+ ConstraintImpliedByRelConstraint (Relation scanrel ,List * testConstraint ,List * provenConstraint )
14517+ {
14518+ List * existConstraint = list_copy (provenConstraint );
14519+ TupleConstr * constr = RelationGetDescr (scanrel )-> constr ;
14520+ int num_check ,
14521+ i ;
14522+
1445414523num_check = (constr != NULL ) ?constr -> num_check :0 ;
1445514524for (i = 0 ;i < num_check ;i ++ )
1445614525{
@@ -14481,13 +14550,13 @@ PartConstraintImpliedByRelConstraint(Relation scanrel,
1448114550/*
1448214551 * Try to make the proof. Since we are comparing CHECK constraints, we
1448314552 * need to use weak implication, i.e., we assume existConstraint is
14484- * not-false and try to prove the same forpartConstraint .
14553+ * not-false and try to prove the same fortestConstraint .
1448514554 *
1448614555 * Note that predicate_implied_by assumes its first argument is known
14487- * immutable. That should always be true forpartition constraints, so we
14488- * don't test it here.
14556+ * immutable. That should always be true forboth NOT NULL and
14557+ *partition constraints, so we don't test it here.
1448914558 */
14490- return predicate_implied_by (partConstraint ,existConstraint , true);
14559+ return predicate_implied_by (testConstraint ,existConstraint , true);
1449114560}
1449214561
1449314562/*