From f9b4cdeeb4420ad41a8ef965c5ed71a3fee3a961 Mon Sep 17 00:00:00 2001 From: Rushabh Lathia Date: Mon, 7 Apr 2025 09:51:47 +0530 Subject: [PATCH 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 @@ attnotnull bool - This column has a (possibly unvalidated) not-null constraint. - - - - - - attnotnullvalid bool - - - Whether the not-null constraint, if one exists, has been validated. + This column has a (possibly invalid) not-null constraint. 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.43.0