From f413904b2b6b611968d3b6d7c37edf97b8eb3835 Mon Sep 17 00:00:00 2001 From: Narek Galstyan Date: Mon, 2 Nov 2026 20:30:41 -0500 Subject: [PATCH 1/2] Add a test for btree_gist bit indexes built from leaf-key unions gbt_var_union() takes its first entry as is. For bit and varbit, internal and leaf representations are different. This causes index corruption when leaf keys get unioned. The commit adds a test to reproduce the issue. Regression asserts the correct behavior, so contrib/btree_gist's bit test fails until the next commit. --- contrib/btree_gist/expected/bit.out | 26 ++++++++++++++++++++++++++ contrib/btree_gist/sql/bit.sql | 13 +++++++++++++ 2 files changed, 39 insertions(+) diff --git a/contrib/btree_gist/expected/bit.out b/contrib/btree_gist/expected/bit.out index e57871f310b..8642c83fe68 100644 --- a/contrib/btree_gist/expected/bit.out +++ b/contrib/btree_gist/expected/bit.out @@ -74,3 +74,29 @@ SELECT a FROM bittmp WHERE a BETWEEN '1000000' and '1000001'; Index Cond: ((a >= '1000000'::"bit") AND (a <= '1000001'::"bit")) (2 rows) +-- Page splits of a multicolumn index union leaf keys; afterwards an index +-- probe on each row's own value must find that row exactly once +CREATE TABLE bitmulti (a bit(16), b int); +CREATE INDEX bitmultiidx ON bitmulti USING gist (a, b); +INSERT INTO bitmulti SELECT i::bit(16), i FROM generate_series(1, 300) i; +EXPLAIN (COSTS OFF) +SELECT t.a FROM bitmulti t, + LATERAL (SELECT count(*) AS n FROM bitmulti u WHERE u.a = t.a) p +WHERE p.n <> 1; + QUERY PLAN +------------------------------------------------------------- + Nested Loop + -> Index Only Scan using bitmultiidx on bitmulti t + -> Aggregate + Filter: (count(*) <> 1) + -> Index Only Scan using bitmultiidx on bitmulti u + Index Cond: (a = t.a) +(6 rows) + +SELECT t.a FROM bitmulti t, + LATERAL (SELECT count(*) AS n FROM bitmulti u WHERE u.a = t.a) p +WHERE p.n <> 1; + a +--- +(0 rows) + diff --git a/contrib/btree_gist/sql/bit.sql b/contrib/btree_gist/sql/bit.sql index a733042023f..8ff3b3ea6cc 100644 --- a/contrib/btree_gist/sql/bit.sql +++ b/contrib/btree_gist/sql/bit.sql @@ -34,3 +34,16 @@ SELECT count(*) FROM bittmp WHERE a > '011011000100010111011000110000100'; SET enable_bitmapscan=off; EXPLAIN (COSTS OFF) SELECT a FROM bittmp WHERE a BETWEEN '1000000' and '1000001'; + +-- Page splits of a multicolumn index union leaf keys; afterwards an index +-- probe on each row's own value must find that row exactly once +CREATE TABLE bitmulti (a bit(16), b int); +CREATE INDEX bitmultiidx ON bitmulti USING gist (a, b); +INSERT INTO bitmulti SELECT i::bit(16), i FROM generate_series(1, 300) i; +EXPLAIN (COSTS OFF) +SELECT t.a FROM bitmulti t, + LATERAL (SELECT count(*) AS n FROM bitmulti u WHERE u.a = t.a) p +WHERE p.n <> 1; +SELECT t.a FROM bitmulti t, + LATERAL (SELECT count(*) AS n FROM bitmulti u WHERE u.a = t.a) p +WHERE p.n <> 1; -- 2.50.1 (Apple Git-155)