BUG: pg_class.relchecks overflow, making table undroppable

Started by Matthias van de Meent28 days ago8 messageshackers
Beta feature

Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.

won't retrysuccessCI history

This thread has been committed, so CI has stopped here. Anything below is the last result it produced.

You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:

docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t253927
psql -h localhost -U postgres

Built from patchset v7 (message #7), September 27, 2026 at 12:48 PM.

Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:

git clone --branch t253927_7 https://github.com/hackorum-dev/postgres.git

In a checkout you already have, add the fork once:

git remote add hackorum https://github.com/hackorum-dev/postgres.git

then, for this patchset and every later one:

git fetch hackorum t253927_7 && git checkout t253927_7

Patchset v7 (message #7) is on t253927_7

Jump to latest
#1Matthias van de Meent
boekewurm+postgres@gmail.com

Hi,

There's probably no user that finds good use for this many check
constraints, but users *can* create > 2^15 CHECK constraints on a
table, and doing so will overflow the int16 (sqltype int2) column in
which the relation's count of those is stored.

This causes various issues, such as possible WARNING spam to users
that need to load that relation into relcache, and an inability to
drop the relation because dropping the table requires the constraints
to be dropped first, and dropping a constraint decrements the counter
that has a check is in place to avoid the counter ever dropping below
zero (with an error if you try to decrement non-positive counter
values).

I think we should forbid creating such large amounts of CHECK
constraints (as attached, backpatch-safe), or drop the "relchecks"
field wholesale/replace it with a 'haschecks' field.

Also attached is an SQL script that shows the issue, and which doesn't
fail in the unsafe manner once PG is patched with the attached patch.

Note: the attached patch does not solve any issues in existing
databases with overflowed relchecks fields.

Kind regards,

Matthias van de Meent
Databricks (https://www.databricks.com)

Attachments:

t253927_1
v1-0001-Enforce-the-pg_class.relchecks-limit.patchapplication/octet-stream; name=v1-0001-Enforce-the-pg_class.relchecks-limit.patchDownload+22-1
scratch_67.sqlapplication/octet-stream; name=scratch_67.sqlDownload
#2Sivaprasad
sivaprasad.postgres@gmail.com
In reply to: Matthias van de Meent (#1)
Re: BUG: pg_class.relchecks overflow, making table undroppable

Hi, sending v2 to the list as well. This version changes the limit
check from `>=` to `>`, allowing the documented maximum of 32,767
CHECK constraints.

On Thu, Sep 24, 2026 at 10:34 PM Matthias van de Meent
<boekewurm+postgres@gmail.com> wrote:

Show quoted text

Hi,

There's probably no user that finds good use for this many check
constraints, but users *can* create > 2^15 CHECK constraints on a
table, and doing so will overflow the int16 (sqltype int2) column in
which the relation's count of those is stored.

This causes various issues, such as possible WARNING spam to users
that need to load that relation into relcache, and an inability to
drop the relation because dropping the table requires the constraints
to be dropped first, and dropping a constraint decrements the counter
that has a check is in place to avoid the counter ever dropping below
zero (with an error if you try to decrement non-positive counter
values).

I think we should forbid creating such large amounts of CHECK
constraints (as attached, backpatch-safe), or drop the "relchecks"
field wholesale/replace it with a 'haschecks' field.

Also attached is an SQL script that shows the issue, and which doesn't
fail in the unsafe manner once PG is patched with the attached patch.

Note: the attached patch does not solve any issues in existing
databases with overflowed relchecks fields.

Kind regards,

Matthias van de Meent
Databricks (https://www.databricks.com)

Attachments:

t253927_2
v2-0001-Enforce-the-pg_class.relchecks-limit.patchtext/x-patch; charset=US-ASCII; name=v2-0001-Enforce-the-pg_class.relchecks-limit.patchDownload+22-1
#3Michael Paquier
michael@paquier.xyz
In reply to: Matthias van de Meent (#1)
Re: BUG: pg_class.relchecks overflow, making table undroppable

On Thu, Sep 24, 2026 at 07:03:45PM +0200, Matthias van de Meent wrote:

This causes various issues, such as possible WARNING spam to users
that need to load that relation into relcache, and an inability to
drop the relation because dropping the table requires the constraints
to be dropped first, and dropping a constraint decrements the counter
that has a check is in place to avoid the counter ever dropping below
zero (with an error if you try to decrement non-positive counter
values).

I think we should forbid creating such large amounts of CHECK
constraints (as attached, backpatch-safe), or drop the "relchecks"
field wholesale/replace it with a 'haschecks' field.

Also attached is an SQL script that shows the issue, and which doesn't
fail in the unsafe manner once PG is patched with the attached patch.

Ahah, fun one! That's in the same line as the recent trigger fix in
b99b74144f9f where catalogs could overflow. Even if it's something
that people would not do in practice, this deserves a backpatch. Will
look at that later..
--
Michael

#4Bertrand Drouvot
bertranddrouvot.pg@gmail.com
In reply to: Michael Paquier (#3)
Re: BUG: pg_class.relchecks overflow, making table undroppable

Hi,

On Fri, Sep 25, 2026 at 07:59:22AM +0900, Michael Paquier wrote:

On Thu, Sep 24, 2026 at 07:03:45PM +0200, Matthias van de Meent wrote:

This causes various issues, such as possible WARNING spam to users
that need to load that relation into relcache, and an inability to
drop the relation because dropping the table requires the constraints
to be dropped first, and dropping a constraint decrements the counter
that has a check is in place to avoid the counter ever dropping below
zero (with an error if you try to decrement non-positive counter
values).

I think we should forbid creating such large amounts of CHECK
constraints (as attached, backpatch-safe), or drop the "relchecks"
field wholesale/replace it with a 'haschecks' field.

Also attached is an SQL script that shows the issue, and which doesn't
fail in the unsafe manner once PG is patched with the attached patch.

Ahah, fun one! That's in the same line as the recent trigger fix in
b99b74144f9f where catalogs could overflow. Even if it's something
that people would not do in practice, this deserves a backpatch.

+1

Just a few comments:

=== 1

It needs a rebase due to 926627bf902

=== 2

+					ereport(ERROR,
+							errmsg("too many check constraints on relation \"%s\"",
+								   RelationGetQualifiedRelationName(rel)));

I think ERRCODE_PROGRAM_LIMIT_EXCEEDED would be appropriate here?

=== 3

 				numchecks++;
+
+				if (numchecks >= PG_INT16_MAX)
+					ereport(ERROR,
+							errmsg("too many check constraints on relation \"%s\"",
+								   RelationGetQualifiedRelationName(rel)));

I wonder if it wouldn't make more sense to check numchecks >= PG_INT16_MAX before
calling StoreRelCheck()? That would avoid inserting the constraint, recording its
dependencies and invoking the post create hook for an object that will be rejected.

Regards,

--
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com

#5jian he
jian.universality@gmail.com
In reply to: Bertrand Drouvot (#4)
Re: BUG: pg_class.relchecks overflow, making table undroppable

On Fri, Sep 25, 2026 at 4:41 PM Bertrand Drouvot
<bertranddrouvot.pg@gmail.com> wrote:

numchecks++;
+
+                               if (numchecks >= PG_INT16_MAX)
+                                       ereport(ERROR,
+                                                       errmsg("too many check constraints on relation \"%s\"",
+                                                                  RelationGetQualifiedRelationName(rel)));

Hi.

In MergeWithExistingConstraint, we have
```
else if (pg_add_s16_overflow(con->coninhcount, 1,
&con->coninhcount))
ereport(ERROR,
errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many inheritance parents"));
```

here, we can also use pg_add_s16_overflow?

--
jian
https://www.enterprisedb.com/

#6Michael Paquier
michael@paquier.xyz
In reply to: Bertrand Drouvot (#4)
Re: BUG: pg_class.relchecks overflow, making table undroppable

On Fri, Sep 25, 2026 at 08:41:38AM +0000, Bertrand Drouvot wrote:

Just a few comments:

=== 1

It needs a rebase due to 926627bf902

=== 2

+                    ereport(ERROR,
+                            errmsg("too many check constraints on relation \"%s\"",
+                                   RelationGetQualifiedRelationName(rel)));

I think ERRCODE_PROGRAM_LIMIT_EXCEEDED would be appropriate here?

An error record would be appropriate, yes. That's reachable with SQL.

numchecks++;
+
+                if (numchecks >= PG_INT16_MAX)
+                    ereport(ERROR,
+                            errmsg("too many check constraints on relation \"%s\"",
+                                   RelationGetQualifiedRelationName(rel)));

I wonder if it wouldn't make more sense to check numchecks >= PG_INT16_MAX before
calling StoreRelCheck()? That would avoid inserting the constraint, recording its
dependencies and invoking the post create hook for an object that will be rejected.

Yeah, let's do that. That's unlikely but it would just be a waste and
that's just switching the order of things.

If somebody is willing to send an updated patch, feel free, I don't
have the energy for that today.. Jian also mentioned
pg_add_s16_overflow(), which is a good idea for this case. :D
--
Michael

#7Matthias van de Meent
boekewurm+postgres@gmail.com
In reply to: Michael Paquier (#6)
Re: BUG: pg_class.relchecks overflow, making table undroppable

On Fri, 25 Sept 2026 at 11:05, Michael Paquier <michael@paquier.xyz> wrote:

On Fri, Sep 25, 2026 at 08:41:38AM +0000, Bertrand Drouvot wrote:

Just a few comments:

=== 1

It needs a rebase due to 926627bf902

Done

=== 2

+                    ereport(ERROR,
+                            errmsg("too many check constraints on relation \"%s\"",
+                                   RelationGetQualifiedRelationName(rel)));

I think ERRCODE_PROGRAM_LIMIT_EXCEEDED would be appropriate here?

Done.

numchecks++;
+
+                if (numchecks >= PG_INT16_MAX)
+                    ereport(ERROR,
+                            errmsg("too many check constraints on relation \"%s\"",
+                                   RelationGetQualifiedRelationName(rel)));

I wonder if it wouldn't make more sense to check numchecks >= PG_INT16_MAX before
calling StoreRelCheck()? That would avoid inserting the constraint, recording its
dependencies and invoking the post create hook for an object that will be rejected.

Yeah, let's do that. That's unlikely but it would just be a waste and
that's just switching the order of things.

Also done. Thanks for the fast replies.

Attached v3:

* Add errcode(PROGRAM_LIMIT_EXCEEDED) to the ereports.
* Use pg_add_s16_overflow() to detect the overflows.
This includes changing the type of local numchecks variables to
int16. SetRelationNumChecks's signature is unchanged.
* Move the overflow checks to before StoreRelCheck.
This saves one dirty tuple in catalog tables when that overflow happens.

Kind regards,

Matthias van de Meent
Databricks (https://www.databricks.com)

Attachments:

t253927_7
v3-0001-Enforce-the-pg_class.relchecks-limit.patchapplication/octet-stream; name=v3-0001-Enforce-the-pg_class.relchecks-limit.patchDownload+30-6
#8Michael Paquier
michael@paquier.xyz
In reply to: Matthias van de Meent (#7)
Re: BUG: pg_class.relchecks overflow, making table undroppable

On Fri, Sep 25, 2026 at 02:33:57PM +0200, Matthias van de Meent wrote:

Also done. Thanks for the fast replies.

Attached v3:

* Add errcode(PROGRAM_LIMIT_EXCEEDED) to the ereports.
* Use pg_add_s16_overflow() to detect the overflows.
This includes changing the type of local numchecks variables to
int16. SetRelationNumChecks's signature is unchanged.
* Move the overflow checks to before StoreRelCheck.
This saves one dirty tuple in catalog tables when that overflow happens.

That looks pretty good here, so applied and backpatched as the
consequences of keeping an overflowed counter can be really annoying.
--
Michael