Re: Avoid overwiriting cache entry (src/backend/utils/cache/relcache.c)

From: Ranier Vilela <ranier(dot)vf(at)gmail(dot)com>
To: Álvaro Herrera <alvherre(at)kurilemu(dot)de>
Cc: Pg Hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: Avoid overwiriting cache entry (src/backend/utils/cache/relcache.c)
Date: 2025-08-29 16:48:40
Message-ID: CAEudQAqQwOf6xtARHbzYMk2yAVpX5vUejdQynH7Fi0uRd1UPdQ@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Em sex., 29 de ago. de 2025 às 07:58, Álvaro Herrera <alvherre(at)kurilemu(dot)de>
escreveu:

> On 2025-Aug-26, Ranier Vilela wrote:
>
> > In function *CheckNNConstraintFetch* the array index is
> > incremented only when "null combin" is not found.
> >
> > IMO the last cache entry can be overwriting if the next tuple is null,
> > because, the fill array fields is not syncronized with array index
> > increment.
>
> Hmm, yeah this is wrong. However, If you do hit the case where conbin
> is null the behavior is unpleasant but I don't see any crashers, and the
> case would amount to catalog corruption. Overall I agree that it seems
> better to not modify the entry at all unless we increment the counter,
> which your patch achieves.
>
> Not very excited about this though. For instance, I don't think we
> reach the point of _using_ the corrupted entry:
>
I think that the main point of the patch is to avoid losing a cache entry.

>
> =# create table bar (a int check (a > 0), b int constraint tssk check
> (null));
> CREATE TABLE
>
> =# update pg_constraint set conbin = null where conname = 'tssk';
> UPDATE 1
>
> =# insert into bar values (-1);
> WARNING: null conbin for relation "bar"
> LÍNEA 1: insert into bar values (-1);
> ^
> WARNING: 1 pg_constraint record(s) missing for relation "bar"
> LÍNEA 1: insert into bar values (-1);
> ^
> ERROR: 1 pg_constraint record(s) missing for relation "bar"
>
> The WARNINGs come from CheckNNConstraintFetch itself, but the error
> comes from ExecRelCheck. Did you find a way to make the code behave
> worse than this?

No.

> Can you find any crashers?
>
No.

>
>
> Now consider this:
>
> =# alter table bar add check (b > 0);
> WARNING: unexpected pg_constraint record found for relation "bar"
> ALTER TABLE
>
> For some reason, here we set relchecks to 0, which makes no sense.
> Which leads to
>
> =# drop table bar;
> WARNING: unexpected pg_constraint record found for relation "bar"
> WARNING: unexpected pg_constraint record found for relation "bar"
> ERROR: relation "bar" has relchecks = 0
>
> This suggests that we should relax that error and make it a warning,
> perhaps like
>
> diff --git a/src/backend/catalog/pg_constraint.c
> b/src/backend/catalog/pg_constraint.c
> index 6002fd0002f..9944e4bd2d1 100644
> --- a/src/backend/catalog/pg_constraint.c
> +++ b/src/backend/catalog/pg_constraint.c
> @@ -937,10 +937,12 @@ RemoveConstraintById(Oid conId)
> con->conrelid);
> classForm = (Form_pg_class) GETSTRUCT(relTup);
>
> - if (classForm->relchecks == 0) /* should not
> happen */
> - elog(ERROR, "relation \"%s\" has relchecks
> = 0",
> - RelationGetRelationName(rel));
> - classForm->relchecks--;
> + if (classForm->relchecks > 0)
> + classForm->relchecks--;
> + else
> + /* should not happen */
> + elog(WARNING, "relation \"%s\" has
> relchecks = %d",
> + RelationGetRelationName(rel),
> classForm->relchecks);
>
> CatalogTupleUpdate(pgrel, &relTup->t_self, relTup);
>
With this change and the patch applied the behavior is:

postgres=# create table bar (a int check (a > 0), b int constraint tssk
check (null));
CREATE TABLE
postgres=# update pg_constraint set conbin = null where conname = 'tssk';
UPDATE 1
postgres=# insert into bar values (-1);
WARNING: null conbin for relation "bar"
LINE 1: insert into bar values (-1);
^
WARNING: 1 pg_constraint record(s) missing for relation "bar"
LINE 1: insert into bar values (-1);
^
ERROR: 1 pg_constraint record(s) missing for relation "bar"
postgres=# alter table bar add check (b > 0);
WARNING: unexpected pg_constraint record found for relation "bar"
ALTER TABLE
postgres=# drop table bar;
WARNING: unexpected pg_constraint record found for relation "bar"
WARNING: unexpected pg_constraint record found for relation "bar"
WARNING: relation "bar" has relchecks = 0
DROP TABLE

>
>
> (There's another decrement for coninhcount in line 1155 of the same
> file. Maybe that deserves the same treatment, not sure.)
>
I'm not have an opinion about this.

best regards,
Ranier Vilela

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Joel Jacobson 2025-08-29 16:51:30 Re: Assert single row returning SQL-standard functions
Previous Message Andres Freund 2025-08-29 16:32:58 Re: aio/README.md comments