From 021c19c4adc95044743b9fbb26d4bac9b7488019 Mon Sep 17 00:00:00 2001 From: Matthias van de Meent Date: Thu, 24 Sep 2026 17:02:25 +0200 Subject: [PATCH v3] Enforce the pg_class.relchecks limit Previously, the count could overflow if sufficiently many CHECK constraints were added to the table, which (if new CHECKs kept getting added) could cause the table to become undroppable due to enforcement of non-negative counts when dropping these constraints. --- doc/src/sgml/limits.sgml | 7 +++++++ src/backend/catalog/heap.c | 28 +++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/doc/src/sgml/limits.sgml b/doc/src/sgml/limits.sgml index 7f7eb51985e..44a14947bf2 100644 --- a/doc/src/sgml/limits.sgml +++ b/doc/src/sgml/limits.sgml @@ -62,6 +62,13 @@ below + + CHECK constraints per table + + 32,767 + constrained by the size of pg_class.relchecks + + columns in a result set 1,664 diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 1c188b7a0ff..a3919985503 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -2331,7 +2331,7 @@ StoreRelNotNull(Relation rel, const char *nnname, AttrNumber attnum, static void StoreConstraints(Relation rel, List *cooked_constraints, bool is_internal) { - int numchecks = 0; + int16 numchecks = 0; ListCell *lc; if (cooked_constraints == NIL) @@ -2355,12 +2355,18 @@ StoreConstraints(Relation rel, List *cooked_constraints, bool is_internal) is_internal); break; case CONSTR_CHECK: + if (pg_add_s16_overflow(numchecks, 1, &numchecks)) + ereport(ERROR, + errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("too many check constraints on relation \"%s\"", + RelationGetRelationName(rel))); + con->conoid = StoreRelCheck(rel, con->name, con->expr, con->is_enforced, !con->skip_validation, con->is_local, con->inhcount, con->is_no_inherit, is_internal); - numchecks++; + break; default: @@ -2418,7 +2424,7 @@ AddRelationNewConstraints(Relation rel, int numoldchecks; ParseState *pstate; ParseNamespaceItem *nsitem; - int numchecks; + int16 numchecks; List *checknames; List *nnnames; Node *expr; @@ -2619,6 +2625,16 @@ AddRelationNewConstraints(Relation rel, checknames = lappend(checknames, ccname); } + /* + * pg_class.relchecks stores this count in an int16, so we + * should avoid overflowing that field + */ + if (pg_add_s16_overflow(numchecks, 1, &numchecks)) + ereport(ERROR, + errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("too many check constraints on relation \"%s\"", + RelationGetRelationName(rel))); + /* * OK, store it. */ @@ -2628,8 +2644,6 @@ AddRelationNewConstraints(Relation rel, is_local ? 0 : 1, cdef->is_no_inherit, is_internal); - numchecks++; - cooked = palloc_object(CookedConstraint); cooked->contype = CONSTR_CHECK; cooked->conoid = constrOid; @@ -3199,6 +3213,10 @@ SetRelationNumChecks(Relation rel, int numchecks) if (relStruct->relchecks != numchecks) { + if (numchecks > INT16_MAX || numchecks < 0) + elog(ERROR, "invalid new relchecks %d for relation %u", + numchecks, RelationGetRelid(rel)); + relStruct->relchecks = numchecks; CatalogTupleUpdate(relrel, &reltup->t_self, reltup); -- 2.54.0 (Apple Git-157)