From c51ba611a16177f8950a9c7ed999bec12c02080b Mon Sep 17 00:00:00 2001
From: Rushabh Lathia <rushabh.lathia@enterprisedb.com>
Date: Mon, 10 Feb 2025 15:39:16 +0530
Subject: [PATCH 1/3] Convert pg_attribut.attnotnull to char type.

This commit change the pg_attribut.attnotnull to char type. Now
attnotnull holds three values, where attnotnull can be either
TRUE, FALSE and INVALID.  INVALID is when name not null constrint
is NOT VALIDATED.
---
 src/backend/access/common/tupdesc.c        | 12 ++++----
 src/backend/bootstrap/bootstrap.c          | 13 ++++----
 src/backend/catalog/heap.c                 | 16 +++++-----
 src/backend/catalog/index.c                |  2 +-
 src/backend/catalog/indexing.c             |  3 +-
 src/backend/catalog/information_schema.sql |  4 +--
 src/backend/commands/tablecmds.c           | 35 ++++++++++++----------
 src/backend/executor/execMain.c            |  3 +-
 src/backend/jit/llvm/llvmjit_deform.c      | 10 ++++---
 src/backend/optimizer/util/plancat.c       |  5 ++--
 src/include/access/tupdesc.h               |  2 +-
 src/include/catalog/pg_attribute.h         |  5 +++-
 12 files changed, 63 insertions(+), 47 deletions(-)

diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index ed2195f14b..58698812c0 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -251,7 +251,7 @@ CreateTupleDescCopy(TupleDesc tupdesc)
 	{
 		Form_pg_attribute att = TupleDescAttr(desc, i);
 
-		att->attnotnull = false;
+		att->attnotnull = ATTRIBUTE_NOTNULL_FALSE;
 		att->atthasdef = false;
 		att->atthasmissing = false;
 		att->attidentity = '\0';
@@ -297,7 +297,7 @@ CreateTupleDescTruncatedCopy(TupleDesc tupdesc, int natts)
 	{
 		Form_pg_attribute att = TupleDescAttr(desc, i);
 
-		att->attnotnull = false;
+		att->attnotnull = ATTRIBUTE_NOTNULL_FALSE;
 		att->atthasdef = false;
 		att->atthasmissing = false;
 		att->attidentity = '\0';
@@ -417,7 +417,7 @@ TupleDescCopy(TupleDesc dst, TupleDesc src)
 	{
 		Form_pg_attribute att = TupleDescAttr(dst, i);
 
-		att->attnotnull = false;
+		att->attnotnull = ATTRIBUTE_NOTNULL_FALSE;
 		att->atthasdef = false;
 		att->atthasmissing = false;
 		att->attidentity = '\0';
@@ -463,7 +463,7 @@ TupleDescCopyEntry(TupleDesc dst, AttrNumber dstAttno,
 	dstAtt->attnum = dstAttno;
 
 	/* since we're not copying constraints or defaults, clear these */
-	dstAtt->attnotnull = false;
+	dstAtt->attnotnull = ATTRIBUTE_NOTNULL_FALSE;
 	dstAtt->atthasdef = false;
 	dstAtt->atthasmissing = false;
 	dstAtt->attidentity = '\0';
@@ -840,7 +840,7 @@ TupleDescInitEntry(TupleDesc desc,
 	att->attnum = attributeNumber;
 	att->attndims = attdim;
 
-	att->attnotnull = false;
+	att->attnotnull = ATTRIBUTE_NOTNULL_FALSE;
 	att->atthasdef = false;
 	att->atthasmissing = false;
 	att->attidentity = '\0';
@@ -903,7 +903,7 @@ TupleDescInitBuiltinEntry(TupleDesc desc,
 	att->attnum = attributeNumber;
 	att->attndims = attdim;
 
-	att->attnotnull = false;
+	att->attnotnull = ATTRIBUTE_NOTNULL_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 6db864892d..1e95dc32f4 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -582,14 +582,17 @@ DefineAttr(char *name, char *type, int attnum, int nullness)
 
 	attrtypes[attnum]->atttypmod = -1;
 	attrtypes[attnum]->attislocal = true;
+	/* set default to false */
+	attrtypes[attnum]->attnotnull = ATTRIBUTE_NOTNULL_FALSE;
+
 
 	if (nullness == BOOTCOL_NULL_FORCE_NOT_NULL)
 	{
-		attrtypes[attnum]->attnotnull = true;
+		attrtypes[attnum]->attnotnull = ATTRIBUTE_NOTNULL_TRUE;
 	}
 	else if (nullness == BOOTCOL_NULL_FORCE_NULL)
 	{
-		attrtypes[attnum]->attnotnull = false;
+		attrtypes[attnum]->attnotnull = ATTRIBUTE_NOTNULL_FALSE;
 	}
 	else
 	{
@@ -608,11 +611,11 @@ DefineAttr(char *name, char *type, int attnum, int nullness)
 			for (i = 0; i < attnum; i++)
 			{
 				if (attrtypes[i]->attlen <= 0 ||
-					!attrtypes[i]->attnotnull)
+					attrtypes[i]->attnotnull == ATTRIBUTE_NOTNULL_FALSE)
 					break;
 			}
 			if (i == attnum)
-				attrtypes[attnum]->attnotnull = true;
+				attrtypes[attnum]->attnotnull = ATTRIBUTE_NOTNULL_TRUE;
 		}
 	}
 }
@@ -696,7 +699,7 @@ InsertOneNull(int i)
 {
 	elog(DEBUG4, "inserting column %d NULL", i);
 	Assert(i >= 0 && i < MAXATTR);
-	if (TupleDescAttr(boot_reldesc->rd_att, i)->attnotnull)
+	if (TupleDescAttr(boot_reldesc->rd_att, i)->attnotnull == ATTRIBUTE_NOTNULL_TRUE)
 		elog(ERROR,
 			 "NULL value specified for not-null column \"%s\" of relation \"%s\"",
 			 NameStr(TupleDescAttr(boot_reldesc->rd_att, i)->attname),
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 956f196fc9..6dc94e3693 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -149,7 +149,7 @@ static const FormData_pg_attribute a1 = {
 	.attbyval = false,
 	.attalign = TYPALIGN_SHORT,
 	.attstorage = TYPSTORAGE_PLAIN,
-	.attnotnull = true,
+	.attnotnull = ATTRIBUTE_NOTNULL_TRUE,
 	.attislocal = true,
 };
 
@@ -162,7 +162,7 @@ static const FormData_pg_attribute a2 = {
 	.attbyval = true,
 	.attalign = TYPALIGN_INT,
 	.attstorage = TYPSTORAGE_PLAIN,
-	.attnotnull = true,
+	.attnotnull = ATTRIBUTE_NOTNULL_TRUE,
 	.attislocal = true,
 };
 
@@ -175,7 +175,7 @@ static const FormData_pg_attribute a3 = {
 	.attbyval = true,
 	.attalign = TYPALIGN_INT,
 	.attstorage = TYPSTORAGE_PLAIN,
-	.attnotnull = true,
+	.attnotnull = ATTRIBUTE_NOTNULL_TRUE,
 	.attislocal = true,
 };
 
@@ -188,7 +188,7 @@ static const FormData_pg_attribute a4 = {
 	.attbyval = true,
 	.attalign = TYPALIGN_INT,
 	.attstorage = TYPSTORAGE_PLAIN,
-	.attnotnull = true,
+	.attnotnull = ATTRIBUTE_NOTNULL_TRUE,
 	.attislocal = true,
 };
 
@@ -201,7 +201,7 @@ static const FormData_pg_attribute a5 = {
 	.attbyval = true,
 	.attalign = TYPALIGN_INT,
 	.attstorage = TYPSTORAGE_PLAIN,
-	.attnotnull = true,
+	.attnotnull = ATTRIBUTE_NOTNULL_TRUE,
 	.attislocal = true,
 };
 
@@ -220,7 +220,7 @@ static const FormData_pg_attribute a6 = {
 	.attbyval = true,
 	.attalign = TYPALIGN_INT,
 	.attstorage = TYPSTORAGE_PLAIN,
-	.attnotnull = true,
+	.attnotnull = ATTRIBUTE_NOTNULL_TRUE,
 	.attislocal = true,
 };
 
@@ -751,7 +751,7 @@ InsertPgAttributeTuples(Relation pg_attribute_rel,
 		slot[slotCount]->tts_values[Anum_pg_attribute_attalign - 1] = CharGetDatum(attrs->attalign);
 		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_attnotnull - 1] = CharGetDatum(attrs->attnotnull);
 		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);
@@ -1710,7 +1710,7 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
 	attStruct->atttypid = InvalidOid;
 
 	/* Remove any not-null constraint the column may have */
-	attStruct->attnotnull = false;
+	attStruct->attnotnull = ATTRIBUTE_NOTNULL_FALSE;
 
 	/* Unset this so no one tries to look up the generation expression */
 	attStruct->attgenerated = '\0';
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index cdabf78024..44177047eb 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -261,7 +261,7 @@ index_check_primary_key(Relation heapRel,
 				 attnum, RelationGetRelid(heapRel));
 		attform = (Form_pg_attribute) GETSTRUCT(atttuple);
 
-		if (!attform->attnotnull)
+		if (attform->attnotnull == ATTRIBUTE_NOTNULL_FALSE)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
 					 errmsg("primary key column \"%s\" is not marked NOT NULL",
diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c
index 25c4b6bdc8..a2211d7556 100644
--- a/src/backend/catalog/indexing.c
+++ b/src/backend/catalog/indexing.c
@@ -207,7 +207,8 @@ CatalogTupleCheckConstraints(Relation heapRel, HeapTuple tup)
 		{
 			Form_pg_attribute thisatt = TupleDescAttr(tupdesc, attnum);
 
-			Assert(!(thisatt->attnotnull && att_isnull(attnum, bp)));
+			Assert(!(thisatt->attnotnull == ATTRIBUTE_NOTNULL_TRUE &&
+					 att_isnull(attnum, bp)));
 		}
 	}
 }
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index a7bffca93d..4f59bc46e7 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -292,7 +292,7 @@ CREATE VIEW attributes AS
            CAST(a.attname AS sql_identifier) AS attribute_name,
            CAST(a.attnum AS cardinal_number) AS ordinal_position,
            CAST(pg_get_expr(ad.adbin, ad.adrelid) AS character_data) AS attribute_default,
-           CAST(CASE WHEN a.attnotnull OR (t.typtype = 'd' AND t.typnotnull) THEN 'NO' ELSE 'YES' END
+           CAST(CASE WHEN a.attnotnull = 't' OR (t.typtype = 'd' AND t.typnotnull) THEN 'NO' ELSE 'YES' END
              AS yes_or_no)
              AS is_nullable, -- This column was apparently removed between SQL:2003 and SQL:2008.
 
@@ -671,7 +671,7 @@ CREATE VIEW columns AS
            CAST(a.attname AS sql_identifier) AS column_name,
            CAST(a.attnum AS cardinal_number) AS ordinal_position,
            CAST(CASE WHEN a.attgenerated = '' THEN pg_get_expr(ad.adbin, ad.adrelid) END AS character_data) AS column_default,
-           CAST(CASE WHEN a.attnotnull OR (t.typtype = 'd' AND t.typnotnull) THEN 'NO' ELSE 'YES' END
+           CAST(CASE WHEN a.attnotnull = 't'  OR (t.typtype = 'd' AND t.typnotnull) THEN 'NO' ELSE 'YES' END
              AS yes_or_no)
              AS is_nullable,
 
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 9d8754be7e..c47ac4d1d3 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -1398,7 +1398,10 @@ BuildDescForRelation(const List *columns)
 		TupleDescInitEntryCollation(desc, attnum, attcollation);
 
 		/* Fill in additional stuff not handled by TupleDescInitEntry */
-		att->attnotnull = entry->is_not_null;
+		if (entry->is_not_null)
+			att->attnotnull = ATTRIBUTE_NOTNULL_TRUE;
+		else
+			att->attnotnull = ATTRIBUTE_NOTNULL_FALSE;
 		att->attislocal = entry->is_local;
 		att->attinhcount = entry->inhcount;
 		att->attidentity = entry->identity;
@@ -6187,7 +6190,8 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
 		{
 			Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
 
-			if (attr->attnotnull && !attr->attisdropped)
+			if (attr->attnotnull == ATTRIBUTE_NOTNULL_TRUE &&
+				!attr->attisdropped)
 				notnull_attrs = lappend_int(notnull_attrs, i);
 		}
 		if (notnull_attrs)
@@ -7637,7 +7641,7 @@ ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
 						RelationGetRelid(rel), attnum);
 
 	/* If the column is already nullable there's nothing to do. */
-	if (!attTup->attnotnull)
+	if (attTup->attnotnull == ATTRIBUTE_NOTNULL_FALSE)
 	{
 		table_close(attr_rel, RowExclusiveLock);
 		return InvalidObjectAddress;
@@ -7667,7 +7671,7 @@ ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
 		AttrNumber	parent_attnum;
 
 		parent_attnum = get_attnum(parentId, colName);
-		if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
+		if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull == ATTRIBUTE_NOTNULL_TRUE)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
 					 errmsg("column \"%s\" is marked NOT NULL in parent table",
@@ -7721,7 +7725,7 @@ set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
 	if (attr->attisdropped)
 		return;
 
-	if (!attr->attnotnull)
+	if (attr->attnotnull == ATTRIBUTE_NOTNULL_FALSE)
 	{
 		Relation	attr_rel;
 		HeapTuple	tuple;
@@ -7734,8 +7738,8 @@ set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
 				 attnum, RelationGetRelid(rel));
 
 		attr = (Form_pg_attribute) GETSTRUCT(tuple);
-		Assert(!attr->attnotnull);
-		attr->attnotnull = true;
+		Assert(attr->attnotnull == ATTRIBUTE_NOTNULL_FALSE);
+		attr->attnotnull = ATTRIBUTE_NOTNULL_TRUE;
 		CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
 
 		/*
@@ -8141,7 +8145,7 @@ ATExecAddIdentity(Relation rel, const char *colName,
 	 * to an existing column that is not NOT NULL would create a state that
 	 * cannot be reproduced without contortions.
 	 */
-	if (!attTup->attnotnull)
+	if (attTup->attnotnull == ATTRIBUTE_NOTNULL_FALSE)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
@@ -9343,7 +9347,7 @@ ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
 					elog(ERROR, "cache lookup failed for attribute %s of relation %u",
 						 attname, childrelid);
 				attrForm = (Form_pg_attribute) GETSTRUCT(tup);
-				if (!attrForm->attnotnull)
+				if (attrForm->attnotnull == ATTRIBUTE_NOTNULL_FALSE)
 					ereport(ERROR,
 							errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
 								   attname, get_rel_name(childrelid)));
@@ -13259,9 +13263,9 @@ dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior beha
 						   RelationGetRelationName(rel)));
 
 		/* All good -- reset attnotnull if needed */
-		if (attForm->attnotnull)
+		if (attForm->attnotnull == ATTRIBUTE_NOTNULL_TRUE)
 		{
-			attForm->attnotnull = false;
+			attForm->attnotnull = ATTRIBUTE_NOTNULL_FALSE;
 			CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
 		}
 
@@ -16588,7 +16592,8 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispart
 			 *
 			 * Other constraints are checked elsewhere.
 			 */
-			if (parent_att->attnotnull && !child_att->attnotnull)
+			if (parent_att->attnotnull == ATTRIBUTE_NOTNULL_TRUE &&
+				child_att->attnotnull == ATTRIBUTE_NOTNULL_FALSE)
 			{
 				HeapTuple	contup;
 
@@ -17631,7 +17636,7 @@ ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode
 							RelationGetRelationName(indexRel), attno)));
 
 		attr = TupleDescAttr(rel->rd_att, attno - 1);
-		if (!attr->attnotnull)
+		if (attr->attnotnull == ATTRIBUTE_NOTNULL_FALSE)
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 					 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
@@ -19109,7 +19114,7 @@ PartConstraintImpliedByRelConstraint(Relation scanrel,
 		{
 			Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
 
-			if (att->attnotnull && !att->attisdropped)
+			if (att->attnotnull == ATTRIBUTE_NOTNULL_TRUE && !att->attisdropped)
 			{
 				NullTest   *ntest = makeNode(NullTest);
 
@@ -20941,7 +20946,7 @@ verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
 		Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
 											  iinfo->ii_IndexAttrNumbers[i] - 1);
 
-		if (!att->attnotnull)
+		if (att->attnotnull == ATTRIBUTE_NOTNULL_FALSE)
 			ereport(ERROR,
 					errcode(ERRCODE_INVALID_TABLE_DEFINITION),
 					errmsg("invalid primary key definition"),
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 0493b7d536..d582231ab6 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -2070,7 +2070,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
 		{
 			Form_pg_attribute att = TupleDescAttr(tupdesc, attrChk - 1);
 
-			if (att->attnotnull && slot_attisnull(slot, attrChk))
+			if (att->attnotnull == ATTRIBUTE_NOTNULL_TRUE &&
+				slot_attisnull(slot, attrChk))
 			{
 				char	   *val_desc;
 				Relation	orig_rel = rel;
diff --git a/src/backend/jit/llvm/llvmjit_deform.c b/src/backend/jit/llvm/llvmjit_deform.c
index 5d169c7a40..e6444bc2c9 100644
--- a/src/backend/jit/llvm/llvmjit_deform.c
+++ b/src/backend/jit/llvm/llvmjit_deform.c
@@ -123,7 +123,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
 		 * combination of attisdropped && attnotnull combination shouldn't
 		 * exist.
 		 */
-		if (att->attnotnull &&
+		if (att->attnotnull == ATTRIBUTE_NOTNULL_TRUE &&
 			!att->atthasmissing &&
 			!att->attisdropped)
 			guaranteed_column_number = attnum;
@@ -438,7 +438,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
 		 * into account, because if they're present the heaptuple's natts
 		 * would have indicated that a slot_getmissingattrs() is needed.
 		 */
-		if (!att->attnotnull)
+		if (att->attnotnull == ATTRIBUTE_NOTNULL_FALSE)
 		{
 			LLVMBasicBlockRef b_ifnotnull;
 			LLVMBasicBlockRef b_ifnull;
@@ -604,7 +604,8 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
 			known_alignment = -1;
 			attguaranteedalign = false;
 		}
-		else if (att->attnotnull && attguaranteedalign && known_alignment >= 0)
+		else if (att->attnotnull == ATTRIBUTE_NOTNULL_TRUE &&
+				 attguaranteedalign && known_alignment >= 0)
 		{
 			/*
 			 * If the offset to the column was previously known, a NOT NULL &
@@ -614,7 +615,8 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
 			Assert(att->attlen > 0);
 			known_alignment += att->attlen;
 		}
-		else if (att->attnotnull && (att->attlen % alignto) == 0)
+		else if (att->attnotnull == ATTRIBUTE_NOTNULL_TRUE &&
+				 (att->attlen % alignto) == 0)
 		{
 			/*
 			 * After a NOT NULL fixed-width column with a length that is a
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 71abb01f65..e322098cfb 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -177,7 +177,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 		{
 			CompactAttribute *attr = TupleDescCompactAttr(relation->rd_att, i);
 
-			if (attr->attnotnull)
+			if (attr->attnotnull == ATTRIBUTE_NOTNULL_TRUE)
 			{
 				rel->notnullattnums = bms_add_member(rel->notnullattnums,
 													 i + 1);
@@ -1355,7 +1355,8 @@ get_relation_constraints(PlannerInfo *root,
 			{
 				Form_pg_attribute att = TupleDescAttr(relation->rd_att, i - 1);
 
-				if (att->attnotnull && !att->attisdropped)
+				if (att->attnotnull == ATTRIBUTE_NOTNULL_TRUE &&
+					!att->attisdropped)
 				{
 					NullTest   *ntest = makeNode(NullTest);
 
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index 396eeb7a0b..1eba67ca8e 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -76,7 +76,7 @@ typedef struct CompactAttribute
 	bool		atthasmissing;	/* as FormData_pg_attribute.atthasmissing */
 	bool		attisdropped;	/* as FormData_pg_attribute.attisdropped */
 	bool		attgenerated;	/* FormData_pg_attribute.attgenerated != '\0' */
-	bool		attnotnull;		/* as FormData_pg_attribute.attnotnull */
+	char		attnotnull;		/* as FormData_pg_attribute.attnotnull */
 	uint8		attalignby;		/* alignment requirement in bytes */
 } CompactAttribute;
 
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index deaa515fe5..b51a267095 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -118,7 +118,7 @@ CATALOG(pg_attribute,1249,AttributeRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(75,
 	char		attcompression BKI_DEFAULT('\0');
 
 	/* This flag represents the "NOT NULL" constraint */
-	bool		attnotnull;
+	char		attnotnull;
 
 	/* Has DEFAULT value or not */
 	bool		atthasdef BKI_DEFAULT(f);
@@ -227,6 +227,9 @@ MAKE_SYSCACHE(ATTNUM, pg_attribute_relid_attnum_index, 128);
 #define		  ATTRIBUTE_GENERATED_STORED	's'
 #define		  ATTRIBUTE_GENERATED_VIRTUAL	'v'
 
+#define		  ATTRIBUTE_NOTNULL_TRUE		't'
+#define		  ATTRIBUTE_NOTNULL_FALSE		'f'
+
 #endif							/* EXPOSE_TO_CLIENT_CODE */
 
 #endif							/* PG_ATTRIBUTE_H */
-- 
2.43.0

