From fc0c342f4946149914854eea8b5b7f88405d87f8 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@vondra.me>
Date: Fri, 30 Oct 2026 16:01:29 +0100
Subject: [PATCH vmaster 2/4] Fix BRIN minmax-multi distance for tid values

The distance function for tid values maps item pointers to float8 as

  (block * MaxHeapTuplesPerPage + offset)

but the multiplication was done in uint32 arithmetic, so for high block
values (above 14759338, ~112GB) the result wrapped around. The distance
would then be very inaccurate (possibly negative), resulting in a less
efficient index.

Fix by doing the arithmetics in double.

It's also possible the (user-supplied) item pointers have offsets
greater than MaxHeapTuplesPerPage, confusing the mapping to double
value. This commit does not try to address that, it's not clear what
would be a better mapping.

Author: Tomas Vondra <tomas@vondra.me>
Backpatch-through: 14
Discussion: https://postgr.es/m/CALdSSPgBvXz-VSF0p-5k%3D_uFfN38cWrERmm_AC4K67N6MWQhmg%40mail.gmail.com
---
 src/backend/access/brin/brin_minmax_multi.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 8bb5bd1b1e4..08e68cb48f4 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -2005,10 +2005,10 @@ brin_minmax_multi_distance_tid(PG_FUNCTION_ARGS)
 	 * We use the no-check variants here, because user-supplied values may
 	 * have (ip_posid == 0). See ItemPointerCompare.
 	 */
-	da1 = ItemPointerGetBlockNumberNoCheck(pa1) * MaxHeapTuplesPerPage +
+	da1 = (double) ItemPointerGetBlockNumberNoCheck(pa1) * MaxHeapTuplesPerPage +
 		ItemPointerGetOffsetNumberNoCheck(pa1);
 
-	da2 = ItemPointerGetBlockNumberNoCheck(pa2) * MaxHeapTuplesPerPage +
+	da2 = (double) ItemPointerGetBlockNumberNoCheck(pa2) * MaxHeapTuplesPerPage +
 		ItemPointerGetOffsetNumberNoCheck(pa2);
 
 	PG_RETURN_FLOAT8(da2 - da1);
-- 
2.55.0

