0001-b-split-TID-lists-when-flushing.patch

text/x-patch

Filename: 0001-b-split-TID-lists-when-flushing.patch
Type: text/x-patch
Part: 1
Message: Re: PG18 GIN parallel index build crash - invalid memory alloc request size

Patch

Format: format-patch
Series: patch 0001
Subject: b: split TID lists when flushing
File+
src/backend/access/gin/gininsert.c 26 11
From 1c47f44483939a0a7a47073a838164a3296a51c3 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@vondra.me>
Date: Sun, 26 Oct 2025 21:14:44 +0100
Subject: [PATCH] b: split TID lists when flushing

---
 src/backend/access/gin/gininsert.c | 37 +++++++++++++++++++++---------
 1 file changed, 26 insertions(+), 11 deletions(-)

diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.c
index 3d71b442aa9..eff78cc622d 100644
--- a/src/backend/access/gin/gininsert.c
+++ b/src/backend/access/gin/gininsert.c
@@ -152,7 +152,9 @@ typedef struct
 	 * only in the leader process.
 	 */
 	GinLeader  *bs_leader;
-	int			bs_worker_id;
+
+	/* number of participating workers (including leader) */
+	int			bs_num_workers;
 
 	/* used to pass information from workers to leader */
 	double		bs_numtuples;
@@ -494,27 +496,39 @@ ginFlushBuildState(GinBuildState *buildstate, Relation index)
 	OffsetNumber attnum;
 	TupleDesc	tdesc = RelationGetDescr(index);
 
+	/* how many TIDs fit into a regular allocation (50% to keep slack) */
+	uint32		maxsize = (MaxAllocSize / sizeof(ItemPointerData) / 2);
+	maxsize /= buildstate->bs_num_workers;	/* fair share per worker */
+
 	ginBeginBAScan(&buildstate->accum);
 	while ((list = ginGetBAEntry(&buildstate->accum,
 								 &attnum, &key, &category, &nlist)) != NULL)
 	{
 		/* information about the key */
 		CompactAttribute *attr = TupleDescCompactAttr(tdesc, (attnum - 1));
+		uint32	start = 0;
 
-		/* GIN tuple and tuple length */
-		GinTuple   *tup;
-		Size		tuplen;
+		/* split the list into smaller parts with up to maxsize items */
+		while (start < nlist)
+		{
+			/* GIN tuple and tuple length */
+			GinTuple   *tup;
+			Size		tuplen;
 
-		/* there could be many entries, so be willing to abort here */
-		CHECK_FOR_INTERRUPTS();
+			/* there could be many entries, so be willing to abort here */
+			CHECK_FOR_INTERRUPTS();
 
-		tup = _gin_build_tuple(attnum, category,
-							   key, attr->attlen, attr->attbyval,
-							   list, nlist, &tuplen);
+			tup = _gin_build_tuple(attnum, category,
+								   key, attr->attlen, attr->attbyval,
+								   &list[start], Min(maxsize, nlist - start),
+								   &tuplen);
 
-		tuplesort_putgintuple(buildstate->bs_worker_sort, tup, tuplen);
+			start += maxsize;
 
-		pfree(tup);
+			tuplesort_putgintuple(buildstate->bs_worker_sort, tup, tuplen);
+
+			pfree(tup);
+		}
 	}
 
 	MemoryContextReset(buildstate->tmpCtx);
@@ -2016,6 +2030,7 @@ _gin_parallel_scan_and_build(GinBuildState *state,
 
 	/* remember how much space is allowed for the accumulated entries */
 	state->work_mem = (sortmem / 2);
+	state->bs_num_workers = ginshared->scantuplesortstates;
 
 	/* Begin "partial" tuplesort */
 	state->bs_sortstate = tuplesort_begin_index_gin(heap, index,
-- 
2.51.0