From 834ec73bad5967b99a1ab2497d02b4af532567bf Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@vondra.me>
Date: Fri, 24 Jul 2026 14:33:58 +0200
Subject: [PATCH v2 1/2] Initialize bs_reltuples field in GIN parallel builds

In parallel GIN builds, each worker reports the number of processed
table rows and reports it to the leader, who then stores the total as
thhe table's pg_class.reltuples. But gin_parallel_build_main failed to
initialize the bs_reltuples field of the worker's GinBuildState, before
accumulating the number of scanned tuples into it.

The initial value is therefore whatever was on the stack, and it may be
set to values like Infinity, NaN or extremely high value. That means the
total stored in pg_class.reltuples may be arbitrary too.

This can have serious consequences. The pg_class.reltuples value is used
to decide when a table is due for autovacuum or autoanalyze, and if it
happens to such bogus value, that may never happen. The value is also
used by query optimizer when calculating various costs.

Fixed by properly initializing bs_reltuples together with the rest of
the build state. The bs_numtuples was initialized later, but it seems
cleaner to just initialize all the fields at once.

Backpatch to 18, where parallel GIN builds were introduced.

Reported-by: Jan Nidzwetzki <jan@planetscale.com>
Discussion: https://postgr.es/m/518BA772-8026-412A-AA8F-A7FE4C6B3717@planetscale.com
Backpatch-through: 18
---
 src/backend/access/gin/gininsert.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.c
index cb9ed3b563c..16abb76c001 100644
--- a/src/backend/access/gin/gininsert.c
+++ b/src/backend/access/gin/gininsert.c
@@ -1875,9 +1875,6 @@ _gin_process_worker_data(GinBuildState *state, Tuplesortstate *worker_sort,
 
 	tuplesort_performsort(state->bs_worker_sort);
 
-	/* reset the number of GIN tuples produced by this worker */
-	state->bs_numtuples = 0;
-
 	if (progress)
 		pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE,
 									 PROGRESS_GIN_PHASE_MERGE_1);
@@ -2158,6 +2155,11 @@ _gin_parallel_build_main(dsm_segment *seg, shm_toc *toc)
 	/* initialize the GIN build state */
 	initGinState(&buildstate.ginstate, indexRel);
 	buildstate.indtuples = 0;
+
+	/* Initialize counters used to report tuple counts to the leader */
+	buildstate.bs_numtuples = 0;
+	buildstate.bs_reltuples = 0;
+
 	memset(&buildstate.buildStats, 0, sizeof(GinStatsData));
 	memset(&buildstate.tid, 0, sizeof(ItemPointerData));
 
-- 
2.55.0

