Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1xAMgK-00000002bNF-0EJU for pgsql-bugs@arkaria.postgresql.org; Sat, 26 Sep 2026 07:18:48 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.98.2) (envelope-from ) id 1xAMgJ-00000003nom-1Bhh for pgsql-bugs@arkaria.postgresql.org; Sat, 26 Sep 2026 07:18:47 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1xABlE-00000002Rgj-0e4t for pgsql-bugs@lists.postgresql.org; Fri, 25 Sep 2026 19:39:08 +0000 Received: from mahout.postgresql.org ([2001:4800:3e1:1::227]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1xABlB-00000001FNC-0wx2 for pgsql-bugs@lists.postgresql.org; Fri, 25 Sep 2026 19:39:07 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=postgresql.org; s=20171124; h=Message-ID:Date:Reply-To:Cc:From:To:Subject: Content-Transfer-Encoding:MIME-Version:Content-Type:Sender:Content-ID: Content-Description:In-Reply-To:References; bh=7+UY9I9T4sJEiRgxZ0y9/YNr753kk7uBKthWSoCrdUM=; b=tFv5/feph0Yq3SQceNnhq2RVa4 jIqBVjKXIGpm/Jh8PGoYsW/kStV3YtojxtOxjIBNP00fJCoV4vOKBieZQpH7pqtTPOIrfU4kRIUbb +26r7O5YIdhBFbqOlNuQDF6lglMkE0r+GCmBJaXGoQcLa3j5Dad19r17qYybqXNcYmt/VMmOblLF/ W9eUW61rae9QzObR6jiyXtiAbLytKIBNJvix/smRL2V9zyVjfJQ3NVUrNr7KmqrAt3IHkRB3I2ACN LbLnQNKtenKnhw2L5SaAPSjNfIbGzBzGQwLqwYnxFIVusAfRXE6vjtcH2h6aQEGOoymWdUuYq5p+S 1nYfitkA==; Received: from wrigleys.postgresql.org ([2a02:16a8:dc51::60]) by mahout.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1xABlB-003gF3-0m for pgsql-bugs@lists.postgresql.org; Fri, 25 Sep 2026 19:39:05 +0000 Received: from localhost ([127.0.0.1] helo=wrigleys.postgresql.org) by wrigleys.postgresql.org with esmtp (Exim 4.98.2) (envelope-from ) id 1xABl8-0000000AYBg-41Xu for pgsql-bugs@lists.postgresql.org; Fri, 25 Sep 2026 19:39:03 +0000 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Subject: BUG #19720: pg_trgm GiST index corruption from gtrgm_union() dropping SIGNKEY To: pgsql-bugs@lists.postgresql.org From: PG Bug reporting form Cc: natec425@gmail.com Reply-To: natec425@gmail.com, pgsql-bugs@lists.postgresql.org Date: Fri, 25 Sep 2026 19:38:25 +0000 Message-ID: <19720-b3e83f5e99c485c5@postgresql.org> X-Auto-Response-Suppress: All Auto-Submitted: auto-generated List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk The following bug has been logged on the website: Bug reference: 19720 Logged by: Nate Clark Email address: natec425@gmail.com PostgreSQL version: 18.6 Operating system: macOS 26 (aarch64) Description: =20 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 =3D ALLISTRUE` when it determin= es 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 `|=3D 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 `|=3D` 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=3D4)); -- 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 =3D 1 and not 'leaf' =3D 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 =3D 'test';