From 1842214bfbaff31a26f2641f9f81872835e041d3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Herrera?= <alvherre@alvh.no-ip.org>
Date: Sun, 6 Apr 2025 19:45:52 +0200
Subject: [PATCH v8 2/3] Remove attnotnullvalid again

TupleDesc construction already has a pg_constraint scan.  This
introduces a bit of ugliness, but the benefit is that we don't need
another pg_attribute column.  However, some regression tests aren't
passing, and it seems to be because we don't set attnullability
correctly everywhere.
---
 doc/src/sgml/catalogs.sgml                 | 11 +---
 src/backend/access/common/tupdesc.c        | 17 ++----
 src/backend/bootstrap/bootstrap.c          |  3 -
 src/backend/catalog/genbki.pl              |  3 -
 src/backend/catalog/heap.c                 |  8 ---
 src/backend/commands/tablecmds.c           | 47 +++++++--------
 src/backend/optimizer/util/plancat.c       | 11 ++--
 src/backend/utils/cache/catcache.c         |  1 -
 src/backend/utils/cache/relcache.c         | 66 +++++++++++++++++++---
 src/include/access/tupdesc.h               |  9 +--
 src/include/catalog/pg_attribute.h         |  3 -
 src/test/regress/expected/sanity_check.out | 20 -------
 src/test/regress/sql/sanity_check.sql      | 14 -----
 13 files changed, 100 insertions(+), 113 deletions(-)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 3ab7d7b68aa..cbd4e40a320 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1260,16 +1260,7 @@
        <structfield>attnotnull</structfield> <type>bool</type>
       </para>
       <para>
-       This column has a (possibly unvalidated) not-null constraint.
-      </para></entry>
-     </row>
-
-     <row>
-      <entry role="catalog_table_entry"><para role="column_definition">
-       <structfield>attnotnullvalid</structfield> <type>bool</type>
-      </para>
-      <para>
-       Whether the not-null constraint, if one exists, has been validated.
+       This column has a (possibly invalid) not-null constraint.
       </para></entry>
      </row>
 
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 320de2cbda0..5831788cbff 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -74,8 +74,8 @@ populate_compact_attribute_internal(Form_pg_attribute src,
 	dst->atthasmissing = src->atthasmissing;
 	dst->attisdropped = src->attisdropped;
 	dst->attgenerated = (src->attgenerated != '\0');
-	dst->attnullability = !src->attnotnull ? ATTNULLABLE_NONE :
-		src->attnotnullvalid ? ATTNULLABLE_VALID : ATTNULLABLE_INVALID;
+	dst->attnullability = !src->attnotnull ? ATTNULLABLE_UNRESTRICTED :
+		ATTNULLABLE_UNKNOWN;
 
 	switch (src->attalign)
 	{
@@ -145,9 +145,10 @@ verify_compact_attribute(TupleDesc tupdesc, int attnum)
 
 	/*
 	 * Make the attcacheoff match since it's been reset to -1 by
-	 * populate_compact_attribute_internal.
+	 * populate_compact_attribute_internal.  Same with attnullability.
 	 */
 	tmp.attcacheoff = cattr->attcacheoff;
+	tmp.attnullability = cattr->attnullability;
 
 	/* Check the freshly populated CompactAttribute matches the TupleDesc's */
 	Assert(memcmp(&tmp, cattr, sizeof(CompactAttribute)) == 0);
@@ -253,7 +254,6 @@ CreateTupleDescCopy(TupleDesc tupdesc)
 		Form_pg_attribute att = TupleDescAttr(desc, i);
 
 		att->attnotnull = false;
-		att->attnotnullvalid = false;
 		att->atthasdef = false;
 		att->atthasmissing = false;
 		att->attidentity = '\0';
@@ -300,7 +300,6 @@ CreateTupleDescTruncatedCopy(TupleDesc tupdesc, int natts)
 		Form_pg_attribute att = TupleDescAttr(desc, i);
 
 		att->attnotnull = false;
-		att->attnotnullvalid = false;
 		att->atthasdef = false;
 		att->atthasmissing = false;
 		att->attidentity = '\0';
@@ -421,7 +420,6 @@ TupleDescCopy(TupleDesc dst, TupleDesc src)
 		Form_pg_attribute att = TupleDescAttr(dst, i);
 
 		att->attnotnull = false;
-		att->attnotnullvalid = false;
 		att->atthasdef = false;
 		att->atthasmissing = false;
 		att->attidentity = '\0';
@@ -468,7 +466,6 @@ TupleDescCopyEntry(TupleDesc dst, AttrNumber dstAttno,
 
 	/* since we're not copying constraints or defaults, clear these */
 	dstAtt->attnotnull = false;
-	dstAtt->attnotnullvalid = false;
 	dstAtt->atthasdef = false;
 	dstAtt->atthasmissing = false;
 	dstAtt->attidentity = '\0';
@@ -567,6 +564,8 @@ DecrTupleDescRefCount(TupleDesc tupdesc)
 
 /*
  * Compare two TupleDesc structures for logical equality
+ *
+ * XXX should we compare CompactAttribute->attnullability here?
  */
 bool
 equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
@@ -618,8 +617,6 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
 			return false;
 		if (attr1->attnotnull != attr2->attnotnull)
 			return false;
-		if (attr1->attnotnullvalid != attr2->attnotnullvalid)
-			return false;
 		if (attr1->atthasdef != attr2->atthasdef)
 			return false;
 		if (attr1->attidentity != attr2->attidentity)
@@ -848,7 +845,6 @@ TupleDescInitEntry(TupleDesc desc,
 	att->attndims = attdim;
 
 	att->attnotnull = false;
-	att->attnotnullvalid = false;
 	att->atthasdef = false;
 	att->atthasmissing = false;
 	att->attidentity = '\0';
@@ -912,7 +908,6 @@ TupleDescInitBuiltinEntry(TupleDesc desc,
 	att->attndims = attdim;
 
 	att->attnotnull = false;
-	att->attnotnullvalid = false;
 	att->atthasdef = false;
 	att->atthasmissing = false;
 	att->attidentity = '\0';
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 44ec9e58520..6db864892d0 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -615,9 +615,6 @@ DefineAttr(char *name, char *type, int attnum, int nullness)
 				attrtypes[attnum]->attnotnull = true;
 		}
 	}
-
-	/* Not-null constraints on system catalogs are always valid. */
-	attrtypes[attnum]->attnotnullvalid = attrtypes[attnum]->attnotnull;
 }
 
 
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index bdf55d7dc8d..df3231fcd41 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -986,9 +986,6 @@ sub morph_row_for_pgattr
 		$row->{attnotnull} = 'f';
 	}
 
-	# Not-null constraints on system catalogs are always valid.
-	$row->{attnotnullvalid} = $row->{attnotnull};
-
 	Catalog::AddDefaultValues($row, $pgattr_schema, 'pg_attribute');
 	return;
 }
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 503e7cb3f58..fbaed5359ad 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -151,7 +151,6 @@ static const FormData_pg_attribute a1 = {
 	.attalign = TYPALIGN_SHORT,
 	.attstorage = TYPSTORAGE_PLAIN,
 	.attnotnull = true,
-	.attnotnullvalid = true,
 	.attislocal = true,
 };
 
@@ -165,7 +164,6 @@ static const FormData_pg_attribute a2 = {
 	.attalign = TYPALIGN_INT,
 	.attstorage = TYPSTORAGE_PLAIN,
 	.attnotnull = true,
-	.attnotnullvalid = true,
 	.attislocal = true,
 };
 
@@ -179,7 +177,6 @@ static const FormData_pg_attribute a3 = {
 	.attalign = TYPALIGN_INT,
 	.attstorage = TYPSTORAGE_PLAIN,
 	.attnotnull = true,
-	.attnotnullvalid = true,
 	.attislocal = true,
 };
 
@@ -193,7 +190,6 @@ static const FormData_pg_attribute a4 = {
 	.attalign = TYPALIGN_INT,
 	.attstorage = TYPSTORAGE_PLAIN,
 	.attnotnull = true,
-	.attnotnullvalid = true,
 	.attislocal = true,
 };
 
@@ -207,7 +203,6 @@ static const FormData_pg_attribute a5 = {
 	.attalign = TYPALIGN_INT,
 	.attstorage = TYPSTORAGE_PLAIN,
 	.attnotnull = true,
-	.attnotnullvalid = true,
 	.attislocal = true,
 };
 
@@ -227,7 +222,6 @@ static const FormData_pg_attribute a6 = {
 	.attalign = TYPALIGN_INT,
 	.attstorage = TYPSTORAGE_PLAIN,
 	.attnotnull = true,
-	.attnotnullvalid = true,
 	.attislocal = true,
 };
 
@@ -759,7 +753,6 @@ InsertPgAttributeTuples(Relation pg_attribute_rel,
 		slot[slotCount]->tts_values[Anum_pg_attribute_attstorage - 1] = CharGetDatum(attrs->attstorage);
 		slot[slotCount]->tts_values[Anum_pg_attribute_attcompression - 1] = CharGetDatum(attrs->attcompression);
 		slot[slotCount]->tts_values[Anum_pg_attribute_attnotnull - 1] = BoolGetDatum(attrs->attnotnull);
-		slot[slotCount]->tts_values[Anum_pg_attribute_attnotnullvalid - 1] = BoolGetDatum(attrs->attnotnullvalid);
 		slot[slotCount]->tts_values[Anum_pg_attribute_atthasdef - 1] = BoolGetDatum(attrs->atthasdef);
 		slot[slotCount]->tts_values[Anum_pg_attribute_atthasmissing - 1] = BoolGetDatum(attrs->atthasmissing);
 		slot[slotCount]->tts_values[Anum_pg_attribute_attidentity - 1] = CharGetDatum(attrs->attidentity);
@@ -1721,7 +1714,6 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
 
 	/* Remove any not-null constraint the column may have */
 	attStruct->attnotnull = false;
-	attStruct->attnotnullvalid = false;
 
 	/* Unset this so no one tries to look up the generation expression */
 	attStruct->attgenerated = '\0';
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 0c7b1231c2e..b86c55c7430 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -1427,7 +1427,7 @@ BuildDescForRelation(const List *columns)
 		TupleDescInitEntryCollation(desc, attnum, attcollation);
 
 		/* Fill in additional stuff not handled by TupleDescInitEntry */
-		att->attnotnull = att->attnotnullvalid = entry->is_not_null;
+		att->attnotnull = entry->is_not_null;
 		att->attislocal = entry->is_local;
 		att->attinhcount = entry->inhcount;
 		att->attidentity = entry->identity;
@@ -6222,16 +6222,18 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
 		 */
 		for (i = 0; i < newTupDesc->natts; i++)
 		{
-			Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
+			CompactAttribute *attr = TupleDescCompactAttr(newTupDesc, i);
 
-			if (attr->attnotnull && attr->attnotnullvalid &&
+			if (attr->attnullability != ATTNULLABLE_UNRESTRICTED &&
 				!attr->attisdropped)
 			{
-				if (attr->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
-					notnull_attrs = lappend_int(notnull_attrs, attr->attnum);
+				Form_pg_attribute	wholeatt = TupleDescAttr(newTupDesc, i);
+
+				if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
+					notnull_attrs = lappend_int(notnull_attrs, wholeatt->attnum);
 				else
 					notnull_virtual_attrs = lappend_int(notnull_virtual_attrs,
-														attr->attnum);
+														wholeatt->attnum);
 			}
 		}
 		if (notnull_attrs || notnull_virtual_attrs)
@@ -7795,7 +7797,7 @@ ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
 
 	/*
 	 * Find the constraint that makes this column NOT NULL, and drop it.
-	 * dropconstraint_internal() resets attnotnull/attnotnullvalid.
+	 * dropconstraint_internal() resets attnotnull.
 	 */
 	conTup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
 	if (conTup == NULL)
@@ -7820,10 +7822,10 @@ ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
  *		Helper to update/validate the pg_attribute status of a not-null
  *		constraint
  *
- * pg_attribute.attnotnull is set true, if it isn't already.  If is_valid
- * is true, also set pg_attribute.attnotnullvalid.  If queue_validation is
- * true, also set up wqueue to validate the constraint.  wqueue may be given
- * as NULL when validation is not needed (e.g., on table creation).
+ * pg_attribute.attnotnull is set true, if it isn't already.
+ * If queue_validation is true, also set up wqueue to validate the constraint.
+ * wqueue may be given as NULL when validation is not needed (e.g., on table
+ * creation).
  */
 static void
 set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
@@ -7843,7 +7845,7 @@ set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
 	if (attr->attisdropped)
 		return;
 
-	if (!attr->attnotnull || (is_valid && !attr->attnotnullvalid))
+	if (!attr->attnotnull)
 	{
 		Relation	attr_rel;
 		HeapTuple	tuple;
@@ -7858,7 +7860,6 @@ set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
 		attr = (Form_pg_attribute) GETSTRUCT(tuple);
 
 		attr->attnotnull = true;
-		attr->attnotnullvalid = is_valid;
 		CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
 
 		/*
@@ -9899,8 +9900,8 @@ ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		/*
 		 * If adding a valid not-null constraint, set the pg_attribute flag
 		 * and tell phase 3 to verify existing rows, if needed.  For an
-		 * invalid constraint, just set attnotnull and attnotnullvalid,
-		 * without queueing verification.
+		 * invalid constraint, just set attnotnull, without queueing
+		 * verification.
 		 */
 		if (constr->contype == CONSTR_NOTNULL)
 			set_attnotnull(wqueue, rel, ccon->attnum,
@@ -14091,11 +14092,10 @@ dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior beha
 									   false),
 						   RelationGetRelationName(rel)));
 
-		/* All good -- reset attnotnull and attnotnullvalid if needed */
+		/* All good -- reset attnotnull if needed */
 		if (attForm->attnotnull)
 		{
 			attForm->attnotnull = false;
-			attForm->attnotnullvalid = false;
 			CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
 		}
 
@@ -19945,18 +19945,19 @@ PartConstraintImpliedByRelConstraint(Relation scanrel,
 
 		for (i = 1; i <= natts; i++)
 		{
-			Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
+			CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
 
-			/* invalid not-null constraint must be ignored */
-			if (att->attnotnull && att->attnotnullvalid && !att->attisdropped)
+			/* invalid not-null constraint must be ignored here */
+			if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
 			{
+				Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
 				NullTest   *ntest = makeNode(NullTest);
 
 				ntest->arg = (Expr *) makeVar(1,
 											  i,
-											  att->atttypid,
-											  att->atttypmod,
-											  att->attcollation,
+											  wholeatt->atttypid,
+											  wholeatt->atttypmod,
+											  wholeatt->attcollation,
 											  0);
 				ntest->nulltesttype = IS_NOT_NULL;
 
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 191a14b876e..b3d41596b87 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1352,17 +1352,18 @@ get_relation_constraints(PlannerInfo *root,
 
 			for (i = 1; i <= natts; i++)
 			{
-				Form_pg_attribute att = TupleDescAttr(relation->rd_att, i - 1);
+				CompactAttribute *att = TupleDescCompactAttr(relation->rd_att, i - 1);
 
-				if (att->attnotnull && att->attnotnullvalid && !att->attisdropped)
+				if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
 				{
+					Form_pg_attribute wholeatt = TupleDescAttr(relation->rd_att, i - 1);
 					NullTest   *ntest = makeNode(NullTest);
 
 					ntest->arg = (Expr *) makeVar(varno,
 												  i,
-												  att->atttypid,
-												  att->atttypmod,
-												  att->attcollation,
+												  wholeatt->atttypid,
+												  wholeatt->atttypmod,
+												  wholeatt->attcollation,
 												  0);
 					ntest->nulltesttype = IS_NOT_NULL;
 
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 70c11529b90..9ad7681f155 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1142,7 +1142,6 @@ CatalogCacheInitializeCache(CatCache *cache)
 			keytype = attr->atttypid;
 			/* cache key columns should always be NOT NULL */
 			Assert(attr->attnotnull);
-			Assert(attr->attnotnullvalid);
 		}
 		else
 		{
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index c80e40cd47a..4128cfa8314 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -307,7 +307,7 @@ static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
 static void AttrDefaultFetch(Relation relation, int ndef);
 static int	AttrDefaultCmp(const void *a, const void *b);
-static void CheckConstraintFetch(Relation relation);
+static void CheckNNConstraintFetch(Relation relation);
 static int	CheckConstraintCmp(const void *a, const void *b);
 static void InitIndexAmRoutine(Relation relation);
 static void IndexSupportInitialize(oidvector *indclass,
@@ -592,7 +592,7 @@ RelationBuildTupleDesc(Relation relation)
 
 		/* Update constraint/default info */
 		if (attp->attnotnull)
-			constr->has_not_null = true;	/* invalid ones included */
+			constr->has_not_null = true;
 		if (attp->attgenerated == ATTRIBUTE_GENERATED_STORED)
 			constr->has_generated_stored = true;
 		if (attp->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
@@ -693,9 +693,38 @@ RelationBuildTupleDesc(Relation relation)
 
 		constr->missing = attrmiss;
 
-		if (relation->rd_rel->relchecks > 0)	/* CHECKs */
-			CheckConstraintFetch(relation);
-		else
+		/* CHECK and NOT NULLs */
+		if (!IsCatalogRelation(relation) &&
+			(relation->rd_rel->relchecks > 0 || constr->has_not_null))
+			CheckNNConstraintFetch(relation);
+
+		/*
+		 * Any not-null constraint that wasn't marked invalid by
+		 * CheckNNConstraintFetch must necessarily be valid; make it so in the
+		 * CompactAttribute array.  In catalog relations however, any not-null
+		 * constraint is necessarily valid.
+		 */
+		for (int i = 0; i < relation->rd_rel->relnatts - 1; i++)
+		{
+			CompactAttribute *attr;
+
+			attr = TupleDescCompactAttr(relation->rd_att, i);
+
+			if (IsCatalogRelation(relation))
+			{
+				if (attr->attnullability == ATTNULLABLE_UNKNOWN)
+					attr->attnullability = ATTNULLABLE_VALID;
+				continue;
+			}
+
+			if (attr->attnullability == ATTNULLABLE_UNKNOWN)
+				attr->attnullability = ATTNULLABLE_VALID;
+			else
+				Assert(attr->attnullability == ATTNULLABLE_INVALID ||
+					   attr->attnullability == ATTNULLABLE_UNRESTRICTED);
+		}
+
+		if (relation->rd_rel->relchecks == 0)
 			constr->num_check = 0;
 	}
 	else
@@ -3573,7 +3602,6 @@ RelationBuildLocalRelation(const char *relname,
 		datt->attidentity = satt->attidentity;
 		datt->attgenerated = satt->attgenerated;
 		datt->attnotnull = satt->attnotnull;
-		datt->attnotnullvalid = satt->attnotnullvalid;
 		has_not_null |= satt->attnotnull;
 		populate_compact_attribute(rel->rd_att, i);
 	}
@@ -4534,13 +4562,14 @@ AttrDefaultCmp(const void *a, const void *b)
 }
 
 /*
- * Load any check constraints for the relation.
+ * Load any check constraints for the relation, and update not-null validity
+ * of invalid constraints.
  *
  * As with defaults, if we don't find the expected number of them, just warn
  * here.  The executor should throw an error if an INSERT/UPDATE is attempted.
  */
 static void
-CheckConstraintFetch(Relation relation)
+CheckNNConstraintFetch(Relation relation)
 {
 	ConstrCheck *check;
 	int			ncheck = relation->rd_rel->relchecks;
@@ -4571,6 +4600,27 @@ CheckConstraintFetch(Relation relation)
 		Datum		val;
 		bool		isnull;
 
+		/*
+		 * Consider only invalid not-null constraints and mark the TupleDesc
+		 * entry invalid.
+		 */
+		if (!IsCatalogRelation(relation) &&
+			conform->contype == CONSTRAINT_NOTNULL)
+		{
+			if (!conform->convalidated)
+			{
+				AttrNumber	attnum;
+
+				attnum = extractNotNullColumn(htup);
+				Assert(relation->rd_att->compact_attrs[attnum - 1].attnullability ==
+					   ATTNULLABLE_UNKNOWN);
+				relation->rd_att->compact_attrs[attnum - 1].attnullability =
+					ATTNULLABLE_INVALID;
+			}
+
+			continue;
+		}
+
 		/* We want check constraints only */
 		if (conform->contype != CONSTRAINT_CHECK)
 			continue;
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index 5523cfcf5aa..1600f967032 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -76,14 +76,15 @@ typedef struct CompactAttribute
 	bool		atthasmissing;	/* as FormData_pg_attribute.atthasmissing */
 	bool		attisdropped;	/* as FormData_pg_attribute.attisdropped */
 	bool		attgenerated;	/* FormData_pg_attribute.attgenerated != '\0' */
-	char		attnullability; /* attnotnull + attnotnullvalid */
+	char		attnullability; /* status of not-null constraint, see below */
 	uint8		attalignby;		/* alignment requirement in bytes */
 } CompactAttribute;
 
 /* Valid values for CompactAttribute->attnullability */
-#define		ATTNULLABLE_VALID		'v' /* valid constraint exists */
-#define		ATTNULLABLE_INVALID		'i' /* constraint exists, marked invalid */
-#define		ATTNULLABLE_NONE		'f' /* no constraint exists */
+#define	ATTNULLABLE_UNRESTRICTED 'f' /* No constraint exists */
+#define	ATTNULLABLE_UNKNOWN		'u' /* constraint exists, validity unknown */
+#define	ATTNULLABLE_VALID		'v' /* valid constraint exists */
+#define	ATTNULLABLE_INVALID		'i' /* constraint exists, marked invalid */
 
 /*
  * This struct is passed around within the backend to describe the structure
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index 9b1236e7a90..df7a77ee224 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -120,9 +120,6 @@ CATALOG(pg_attribute,1249,AttributeRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(75,
 	/* Whether a not-null constraint exists for the column */
 	bool		attnotnull;
 
-	/* Whether the not-null constraint, if it exists, is valid */
-	bool		attnotnullvalid;
-
 	/* Has DEFAULT value or not */
 	bool		atthasdef BKI_DEFAULT(f);
 
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 2e39037e3fb..8370c1561cc 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -25,23 +25,3 @@ SELECT relname, relkind
 ---------+---------
 (0 rows)
 
--- pg_attribute sanity check: attnotnullvalid can only be true when
--- attnotnull is valid.
-select pa.attrelid::regclass, pa.attname, pa.attnum,
-       pa.attnotnull, pa.attnotnullvalid
-from    pg_attribute pa
-where not pa.attnotnull and pa.attnotnullvalid;
- attrelid | attname | attnum | attnotnull | attnotnullvalid 
-----------+---------+--------+------------+-----------------
-(0 rows)
-
--- pg_attribute's attnotnullvalid should match pg_constraint.convalidated
-select att.attrelid::regclass, att.attname, att.attnotnull,
-       att.attnotnullvalid, con.convalidated
-  from pg_attribute att join pg_constraint con
-    on (att.attnum = con.conkey[1] and att.attrelid = con.conrelid)
- where contype = 'n' and con.convalidated <> att.attnotnullvalid;
- attrelid | attname | attnotnull | attnotnullvalid | convalidated 
-----------+---------+------------+-----------------+--------------
-(0 rows)
-
diff --git a/src/test/regress/sql/sanity_check.sql b/src/test/regress/sql/sanity_check.sql
index 6ef36ab84b7..162e5324b5d 100644
--- a/src/test/regress/sql/sanity_check.sql
+++ b/src/test/regress/sql/sanity_check.sql
@@ -19,17 +19,3 @@ SELECT relname, relkind
   FROM pg_class
  WHERE relkind IN ('v', 'c', 'f', 'p', 'I')
        AND relfilenode <> 0;
-
--- pg_attribute sanity check: attnotnullvalid can only be true when
--- attnotnull is valid.
-select pa.attrelid::regclass, pa.attname, pa.attnum,
-       pa.attnotnull, pa.attnotnullvalid
-from    pg_attribute pa
-where not pa.attnotnull and pa.attnotnullvalid;
-
--- pg_attribute's attnotnullvalid should match pg_constraint.convalidated
-select att.attrelid::regclass, att.attname, att.attnotnull,
-       att.attnotnullvalid, con.convalidated
-  from pg_attribute att join pg_constraint con
-    on (att.attnum = con.conkey[1] and att.attrelid = con.conrelid)
- where contype = 'n' and con.convalidated <> att.attnotnullvalid;
-- 
2.39.5

