1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

Enforce pg_class.relchecks limit (32,767 or INT16_MAX)

pg_class.relchecks reports the number of CHECK constraints in a table,
and is stored within a smallint.  It was possible to define a table that
could overflow the counter when specifying more than 32k constraints
(new table or adding a constraint).  This would cause the table to
become undroppable, as this counter should never be negative when
dropping these constraints.

Author: Matthias van de Meent <boekewurm+postgres@gmail.com>
Reviewed-by: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Jian He <jian.universality@gmail.com>
Reviewed-by: Sivaprasad <sivaprasad.postgres@gmail.com>
Discussion: https://postgr.es/m/CAEze2WjSW4qaHYnvkTtbknp85eGEirGJTLMZatotdJJKiA+_hA@mail.gmail.com
Backpatch-through: 14
This commit is contained in:
Michael Paquier
2026-09-28 06:55:05 +09:00
parent 054eb938e5
commit b80d5b17ea
2 changed files with 30 additions and 5 deletions

View File

@@ -62,6 +62,13 @@
below</entry>
</row>
<row>
<entry><literal>CHECK</literal> constraints per table</entry>
<!-- 2^15 - 1 -->
<entry>32,767</entry>
<entry>constrained by the size of pg_class.relchecks</entry>
</row>
<row>
<entry>columns in a result set</entry>
<entry>1,664</entry>

View File

@@ -2330,7 +2330,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)
@@ -2354,12 +2354,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:
@@ -2417,7 +2423,7 @@ AddRelationNewConstraints(Relation rel,
int numoldchecks;
ParseState *pstate;
ParseNamespaceItem *nsitem;
int numchecks;
int16 numchecks;
List *checknames;
List *nnnames;
Node *expr;
@@ -2618,6 +2624,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.
*/
@@ -2627,8 +2643,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;
@@ -3198,6 +3212,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);