From 53c1c378014dfa6cd9addeb63fce5365a0a33679 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@vondra.me>
Date: Fri, 30 Oct 2026 16:58:13 +0100
Subject: [PATCH v18 3/3] Handle overflow when analyzing nummultirange values

The numrange_subdiff() function was calculating the difference simply by
calling numeric_sub and numeric_float8, but those functions are not
overflow-safe. It's possible to insert valid numeric ranges for which
the difference overflows either numeric or float8. When that happened,
the ANALYZE on the table failed.

Fixed by handling overflows in numrange_subdiff() - extreme differences
are treated as infinity.

Author: Kirill Reshke <reshkekirill@gmail.com>
Backpatch-through: 14
Discussion: https://postgr.es/m/CALdSSPgBvXz-VSF0p-5k%3D_uFfN38cWrERmm_AC4K67N6MWQhmg%40mail.gmail.com
---
 src/backend/utils/adt/rangetypes.c            | 28 +++++++++++++++----
 src/test/regress/expected/multirangetypes.out |  7 +++++
 src/test/regress/sql/multirangetypes.sql      |  9 ++++++
 3 files changed, 38 insertions(+), 6 deletions(-)

diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 66cc0acf4a7..6ef12003218 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -41,7 +41,9 @@
 #include "optimizer/optimizer.h"
 #include "utils/builtins.h"
 #include "utils/date.h"
+#include "utils/float.h"
 #include "utils/lsyscache.h"
+#include "utils/numeric.h"
 #include "utils/rangetypes.h"
 #include "utils/sortsupport.h"
 #include "utils/timestamp.h"
@@ -1702,15 +1704,29 @@ int8range_subdiff(PG_FUNCTION_ARGS)
 Datum
 numrange_subdiff(PG_FUNCTION_ARGS)
 {
-	Datum		v1 = PG_GETARG_DATUM(0);
-	Datum		v2 = PG_GETARG_DATUM(1);
-	Datum		numresult;
+	Numeric		v1 = PG_GETARG_NUMERIC(0);
+	Numeric		v2 = PG_GETARG_NUMERIC(1);
+	Numeric		numresult;
 	float8		floatresult;
+	bool		have_error = false;
 
-	numresult = DirectFunctionCall2(numeric_sub, v1, v2);
+	/*
+	 * The difference can exceed numeric's range, or be unrepresentable
+	 * in float8.  Neither caller is prepared to handle an error, so
+	 * saturate to infinity (or round to zero) instead.
+	 */
+	numresult = numeric_sub_opt_error(v1, v2, &have_error);
+	if (have_error)
+		floatresult = get_float8_infinity();
+	else
+	{
+		floatresult = DatumGetFloat8(DirectFunctionCall1(
+			numeric_float8_no_overflow, NumericGetDatum(numresult)));
+		pfree(numresult);
+	}
 
-	floatresult = DatumGetFloat8(DirectFunctionCall1(numeric_float8,
-													 numresult));
+	PG_FREE_IF_COPY(v1, 0);
+	PG_FREE_IF_COPY(v2, 1);
 
 	PG_RETURN_FLOAT8(floatresult);
 }
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
index 76ab1430c4c..e10e7debba6 100644
--- a/src/test/regress/expected/multirangetypes.out
+++ b/src/test/regress/expected/multirangetypes.out
@@ -3081,6 +3081,13 @@ ERROR:  type "intr_multirange" already exists
 DETAIL:  Failed while creating a multirange type for type "intr".
 HINT:  You can manually specify a multirange type name using the "multirange_type_name" attribute.
 drop type intr_multirange;
+---
+-- Check that numeric overflow during analyze does not make it fail
+---
+create table multirange_analyze_test (r nummultirange);
+insert into multirange_analyze_test values ('{[1e1000, 2e1000], [-9e131071, 9e131071]}'::nummultirange);
+analyze multirange_analyze_test;
+drop table multirange_analyze_test;
 --
 -- Test multiple multirange types over the same subtype and manual naming of
 -- the multirange type.
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
index c7953aad295..770ac522e84 100644
--- a/src/test/regress/sql/multirangetypes.sql
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -680,6 +680,15 @@ create type intr_multirange as (x int, y int);
 create type intr as range(subtype=int); -- should fail
 drop type intr_multirange;
 
+---
+-- Check that numeric overflow during analyze does not make it fail
+---
+
+create table multirange_analyze_test (r nummultirange);
+insert into multirange_analyze_test values ('{[1e1000, 2e1000], [-9e131071, 9e131071]}'::nummultirange);
+analyze multirange_analyze_test;
+drop table multirange_analyze_test;
+
 --
 -- Test multiple multirange types over the same subtype and manual naming of
 -- the multirange type.
-- 
2.55.0

