diff --git a/doc/src/sgml/bki.sgml b/doc/src/sgml/bki.sgml index e3ba73a9a8..d53cbf810f 100644 --- a/doc/src/sgml/bki.sgml +++ b/doc/src/sgml/bki.sgml @@ -122,10 +122,7 @@ if they are fixed-width and are not preceded by any nullable column. Where this rule is inadequate, you can force correct marking by using BKI_FORCE_NOT_NULL - and BKI_FORCE_NULL annotations as needed. But note - that NOT NULL constraints are only enforced in the - executor, not against tuples that are generated by random C code, - so care is still needed when manually creating or updating catalog rows. + and BKI_FORCE_NULL annotations as needed. diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c index a5f5bc46a9..674e6a8321 100644 --- a/src/backend/catalog/indexing.c +++ b/src/backend/catalog/indexing.c @@ -18,6 +18,8 @@ #include "access/htup_details.h" #include "catalog/index.h" #include "catalog/indexing.h" +#include "catalog/pg_subscription.h" +#include "catalog/pg_subscription_rel.h" #include "executor/executor.h" #include "utils/rel.h" @@ -164,6 +166,53 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple) ExecDropSingleTupleTableSlot(slot); } +/* + * Subroutine to verify that catalog constraints are honored. + * + * Tuples inserted via CatalogTupleInsert/CatalogTupleUpdate are generally + * "hand made", so that it's possible that they fail to satisfy constraints + * that would be checked if they were being inserted by the executor. That's + * a coding error, so we only bother to check for it in assert-enabled builds. + */ +#ifdef USE_ASSERT_CHECKING + +static void +CatalogTupleCheckConstraints(Relation heapRel, HeapTuple tup) +{ + /* + * Currently, the only constraints implemented for system catalogs are + * attnotnull constraints. + */ + if (HeapTupleHasNulls(tup)) + { + TupleDesc tupdesc = RelationGetDescr(heapRel); + bits8 *bp = tup->t_data->t_bits; + + for (int attnum = 0; attnum < tupdesc->natts; attnum++) + { + Form_pg_attribute thisatt = TupleDescAttr(tupdesc, attnum); + + /* + * Through an embarrassing oversight, pre-v13 installations have + * pg_subscription.subslotname and pg_subscription_rel.srsublsn + * marked as attnotnull, which they should not be. Ignore those + * flags. + */ + Assert(!(thisatt->attnotnull && att_isnull(attnum, bp) && + !((thisatt->attrelid == SubscriptionRelationId && + thisatt->attnum == Anum_pg_subscription_subslotname) || + (thisatt->attrelid == SubscriptionRelRelationId && + thisatt->attnum == Anum_pg_subscription_rel_srsublsn)))); + } + } +} + +#else /* !USE_ASSERT_CHECKING */ + +#define CatalogTupleCheckConstraints(heapRel, tup) ((void) 0) + +#endif /* USE_ASSERT_CHECKING */ + /* * CatalogTupleInsert - do heap and indexing work for a new catalog tuple * @@ -182,6 +231,8 @@ CatalogTupleInsert(Relation heapRel, HeapTuple tup) CatalogIndexState indstate; Oid oid; + CatalogTupleCheckConstraints(heapRel, tup); + indstate = CatalogOpenIndexes(heapRel); oid = simple_heap_insert(heapRel, tup); @@ -206,6 +257,8 @@ CatalogTupleInsertWithInfo(Relation heapRel, HeapTuple tup, { Oid oid; + CatalogTupleCheckConstraints(heapRel, tup); + oid = simple_heap_insert(heapRel, tup); CatalogIndexInsert(indstate, tup); @@ -229,6 +282,8 @@ CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup) { CatalogIndexState indstate; + CatalogTupleCheckConstraints(heapRel, tup); + indstate = CatalogOpenIndexes(heapRel); simple_heap_update(heapRel, otid, tup); @@ -249,6 +304,8 @@ void CatalogTupleUpdateWithInfo(Relation heapRel, ItemPointer otid, HeapTuple tup, CatalogIndexState indstate) { + CatalogTupleCheckConstraints(heapRel, tup); + simple_heap_update(heapRel, otid, tup); CatalogIndexInsert(indstate, tup);