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:
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user