Re: BUG #19720: pg_trgm GiST index corruption from gtrgm_union() dropping SIGNKEY

From: Kirill Reshke <reshkekirill(at)gmail(dot)com>
To: natec425(at)gmail(dot)com, pgsql-bugs(at)lists(dot)postgresql(dot)org
Subject: Re: BUG #19720: pg_trgm GiST index corruption from gtrgm_union() dropping SIGNKEY
Date: 2026-09-26 09:02:48
Message-ID: CALdSSPi6LG3Vbrgr2Uo6h5Qih880XcWka=EYFyPR4bUSK=Apfg@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

On Sat, 26 Sept 2026 at 12:15, PG Bug reporting form
<noreply(at)postgresql(dot)org> wrote:
>
> The following bug has been logged on the website:
>
> Bug reference: 19720
> Logged by: Nate Clark
> Email address: natec425(at)gmail(dot)com
> PostgreSQL version: 18.6
> Operating system: macOS 26 (aarch64)
> Description:
>
> Hey all,
>
> I experienced index corruption for a trigram GiST index at work. I believe
> it is due to an invalid `TRGM->flag` state.
>
> Currently, `gtrgm_union` sets `result->flag = ALLISTRUE` when it determines
> that the signature is all true, but this drops the `SIGNKEY` bit. Downstream
> of this, `unionkey` branches on the flag and defaults to the array handling
> else branch. This array branch interprets the state as a 0 length array and
> produces an empty signature. These two pieces together mean that an insert
> in this bad flag state will produce a downlink with only the signature bits
> for that new inserted value rather than the true union of the children.
>
> I believe the fix is to change it to `|= ALLISTRUE` (similar to the other
> GiST op classes). I've tested this change locally against the following
> repro script.
>
> I get the following output against `master` and 18.6 (with some small jitter
> in total count):
>
> what | count
> -----------------------+-------
> tests (total minus 3) | 0
> total | 33120
>
> With the `|=` patch I get:
>
> what | count
> -----------------------+-------
> tests (total minus 3) | 33130
> total | 33133
>
> Thanks so much for your time,
> Nate Clark
>
> -- repro script
> create extension if not exists pg_trgm;
> create extension if not exists pageinspect;
> drop table if exists t;
> create table t (v text);
> create index t_idx on t
> -- siglen 4 to make it simpler to hit ALLISTRUE
> using gist (v gist_trgm_ops(siglen=4));
>
> -- Insert until the root splits as an internal node
> -- so we get signature, not array, logic.
> do $$
> begin
> -- insert a null to hit the null handling branch involving gtrgm_union
> insert into t values (null);
> -- insert an ALLISTRUE input
> insert into t select string_agg(i::text, ' ')
> from generate_series(1, 100) i;
> -- insert until the root splits
> loop
> insert into t values ('test');
> exit when exists (
> select from gist_page_items_bytea(get_raw_page('t_idx', 0)) r,
> gist_page_opaque_info(get_raw_page('t_idx',
> (r.ctid::text::point)[0]::int)) c
> where r.itemoffset = 1 and not 'leaf' = any(c.flags));
> end loop;
> end $$;
>
> -- Insert one row whose trigrams set 0 bits.
> -- This causes all children to be unreachable.
> insert into t values ('');
>
> select 'total' as what, count(*) from t
> union
> select 'tests (total minus 3)', count(*) from t where v = 'test';
>
>
>

Hi!
I checked this report at 1a846a55, and it looks like there is indeed
corruption.

```
reshke=# set enable_seqscan to off;
SET
reshke=# select 'total' as what, count(*) from t
union
select 'tests (total minus 3)', count(*) from t where v = 'test';
what | count
-----------------------+-------
tests (total minus 3) | 0
total | 33117
(2 rows)

reshke=# set enable_seqscan to on;
SET
reshke=# select 'total' as what, count(*) from t
union
select 'tests (total minus 3)', count(*) from t where v = 'test';
what | count
-----------------------+-------
tests (total minus 3) | 33114
total | 33117
(2 rows)
```

Your analysis also looks correct for me, would you share a patch with
result->flag = ALLISTRUE; -> result->flag |= ALLISTRUE; ?

I also used patch from [0] and it complans with ERROR: index "t_idx"
has inconsistent records on page 293 offset 1

[0] https://commitfest.postgresql.org/patch/5879/
--
Best regards,
Kirill Reshke

In response to

Responses

Browse pgsql-bugs by date

  From Date Subject
Next Message Kirill Reshke 2026-09-26 09:31:59 Re: BUG #19720: pg_trgm GiST index corruption from gtrgm_union() dropping SIGNKEY
Previous Message PG Bug reporting form 2026-09-26 09:00:00 BUG #19723: CREATE INDEX racing with ALTER INDEX ATTACH PARTITION triggers unexpected internal error