From f7b89485dc12b71384378ea1170c1e2b0b97ae70 Mon Sep 17 00:00:00 2001 From: shihao zhong Date: Tue, 3 Nov 2026 22:46:12 -0500 Subject: [PATCH] Fix gtsvector_picksplit() with only two entries Fill the cache for the last entry before forming the initial unions. Existing single-column indexes may need a REINDEX. Reported-by: Alexander Lakhin Discussion: https://postgr.es/m/19780-93fb5c0e4ec2af18@postgresql.org Backpatch-through: 14 --- src/backend/utils/adt/tsgistidx.c | 6 ++++-- src/test/regress/expected/tsearch.out | 16 ++++++++++++++++ src/test/regress/sql/tsearch.sql | 12 ++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/backend/utils/adt/tsgistidx.c b/src/backend/utils/adt/tsgistidx.c index 1befe4fbedf..f384cf205d6 100644 --- a/src/backend/utils/adt/tsgistidx.c +++ b/src/backend/utils/adt/tsgistidx.c @@ -678,6 +678,10 @@ gtsvector_picksplit(PG_FUNCTION_ARGS) seed_2 = 2; } + /* seed_2 can be the last entry, so fill its cache first */ + maxoff = OffsetNumberNext(maxoff); + fillcache(&cache[maxoff], GETENTRY(entryvec, maxoff), siglen); + /* form initial .. */ datum_l = gtsvector_alloc(SIGNKEY | (cache[seed_1].allistrue ? ALLISTRUE : 0), siglen, cache[seed_1].sign); @@ -685,8 +689,6 @@ gtsvector_picksplit(PG_FUNCTION_ARGS) siglen, cache[seed_2].sign); union_l = GETSIGN(datum_l); union_r = GETSIGN(datum_r); - maxoff = OffsetNumberNext(maxoff); - fillcache(&cache[maxoff], GETENTRY(entryvec, maxoff), siglen); /* sort before ... */ costvector = palloc_array(SPLITCOST, maxoff); for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j)) diff --git a/src/test/regress/expected/tsearch.out b/src/test/regress/expected/tsearch.out index 5b7c2123f37..22effde4efa 100644 --- a/src/test/regress/expected/tsearch.out +++ b/src/test/regress/expected/tsearch.out @@ -870,6 +870,22 @@ RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; DROP INDEX wowidx; +-- Two index tuples that don't fit on one page +CREATE TABLE test_tsvector_wide (a tsvector, b bytea); +CREATE INDEX ON test_tsvector_wide USING gist (a) INCLUDE (b); +INSERT INTO test_tsvector_wide + SELECT 'foo', decode(string_agg(md5(g::text), ''), 'hex') + FROM generate_series(1, 300) g; +INSERT INTO test_tsvector_wide SELECT 'bar', b FROM test_tsvector_wide; +SET enable_seqscan = off; +SELECT count(*) FROM test_tsvector_wide WHERE a @@ 'bar'; + count +------- + 1 +(1 row) + +RESET enable_seqscan; +DROP TABLE test_tsvector_wide; ALTER TABLE test_tsvector SET (parallel_workers = 2); CREATE INDEX wowidx ON test_tsvector USING gin (a); SET enable_seqscan=OFF; diff --git a/src/test/regress/sql/tsearch.sql b/src/test/regress/sql/tsearch.sql index 8b3d700f57c..cea3b1fca02 100644 --- a/src/test/regress/sql/tsearch.sql +++ b/src/test/regress/sql/tsearch.sql @@ -222,6 +222,18 @@ RESET enable_bitmapscan; DROP INDEX wowidx; +-- Two index tuples that don't fit on one page +CREATE TABLE test_tsvector_wide (a tsvector, b bytea); +CREATE INDEX ON test_tsvector_wide USING gist (a) INCLUDE (b); +INSERT INTO test_tsvector_wide + SELECT 'foo', decode(string_agg(md5(g::text), ''), 'hex') + FROM generate_series(1, 300) g; +INSERT INTO test_tsvector_wide SELECT 'bar', b FROM test_tsvector_wide; +SET enable_seqscan = off; +SELECT count(*) FROM test_tsvector_wide WHERE a @@ 'bar'; +RESET enable_seqscan; +DROP TABLE test_tsvector_wide; + ALTER TABLE test_tsvector SET (parallel_workers = 2); CREATE INDEX wowidx ON test_tsvector USING gin (a); -- 2.37.1 (Apple Git-137.1)