From ced278119623b4f6bf705680291ab6ff18085c23 Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <lic@highgo.com>
Date: Sun, 4 Jan 2026 10:11:45 +0800
Subject: [PATCH v1] intarray: fix compare overflow, add test

Use a safe comparator in compare_val_int4 to avoid int32 overflow in
MCELEM bsearch. Add a regression test that forces the negative-middle
case so EXPLAIN distinguishes MCELEM lookup from the DEFAULT_EQ_SEL
fallback.

Author: Chao Li <lic@highgo.com>
---
 contrib/intarray/_int_selfuncs.c   |  8 +++++++-
 contrib/intarray/expected/_int.out | 26 ++++++++++++++++++++++++++
 contrib/intarray/sql/_int.sql      | 24 ++++++++++++++++++++++++
 3 files changed, 57 insertions(+), 1 deletion(-)

diff --git a/contrib/intarray/_int_selfuncs.c b/contrib/intarray/_int_selfuncs.c
index ddffd69cb6e..1fad896c180 100644
--- a/contrib/intarray/_int_selfuncs.c
+++ b/contrib/intarray/_int_selfuncs.c
@@ -330,6 +330,12 @@ compare_val_int4(const void *a, const void *b)
 {
 	int32		key = *(int32 *) a;
 	const Datum *t = (const Datum *) b;
+	int32		v = DatumGetInt32(*t);
 
-	return key - DatumGetInt32(*t);
+	if (key < v)
+		return -1;
+	else if (key > v)
+		return 1;
+	else
+		return 0;
 }
diff --git a/contrib/intarray/expected/_int.out b/contrib/intarray/expected/_int.out
index d0e68d0447f..388a162e28f 100644
--- a/contrib/intarray/expected/_int.out
+++ b/contrib/intarray/expected/_int.out
@@ -1011,3 +1011,29 @@ SELECT count(*) from test__int WHERE a @@ '!2733 & (2738 | 254)';
 (1 row)
 
 RESET enable_seqscan;
+-- Check selectivity estimation for INT32_MAX (2147483647) with
+-- MCELEM lookup. The MCE list includes -1 at the middle position
+-- so bsearch hits it first; a buggy comparator can overflow and
+-- miss INT32_MAX, falling back to DEFAULT_EQ_SEL.
+CREATE TABLE test__mce (a int[]);
+INSERT INTO test__mce SELECT ARRAY[-2] FROM generate_series(1, 50);
+INSERT INTO test__mce SELECT ARRAY[-1] FROM generate_series(1, 50);
+INSERT INTO test__mce SELECT ARRAY[2147483647] FROM generate_series(1, 50);
+ANALYZE test__mce;
+-- Force cost estimation to be 0, so that we can see effect on
+-- rows alone.
+SET seq_page_cost = 0;
+SET cpu_tuple_cost = 0;
+SET cpu_operator_cost = 0;
+-- Estimated rows should be 50
+EXPLAIN SELECT 1 FROM test__mce WHERE a @@ '2147483647';
+                        QUERY PLAN                        
+----------------------------------------------------------
+ Seq Scan on test__mce  (cost=0.00..0.00 rows=50 width=4)
+   Filter: (a @@ '2147483647'::query_int)
+(2 rows)
+
+RESET seq_page_cost;
+RESET cpu_tuple_cost;
+RESET cpu_operator_cost;
+DROP TABLE test__mce;
diff --git a/contrib/intarray/sql/_int.sql b/contrib/intarray/sql/_int.sql
index 5668ab40704..2fcbf097762 100644
--- a/contrib/intarray/sql/_int.sql
+++ b/contrib/intarray/sql/_int.sql
@@ -239,3 +239,27 @@ SELECT count(*) from test__int WHERE a @@ '!2733 & (2738 | 254)';
 
 
 RESET enable_seqscan;
+
+-- Check selectivity estimation for INT32_MAX (2147483647) with
+-- MCELEM lookup. The MCE list includes -1 at the middle position
+-- so bsearch hits it first; a buggy comparator can overflow and
+-- miss INT32_MAX, falling back to DEFAULT_EQ_SEL.
+CREATE TABLE test__mce (a int[]);
+INSERT INTO test__mce SELECT ARRAY[-2] FROM generate_series(1, 50);
+INSERT INTO test__mce SELECT ARRAY[-1] FROM generate_series(1, 50);
+INSERT INTO test__mce SELECT ARRAY[2147483647] FROM generate_series(1, 50);
+ANALYZE test__mce;
+-- Force cost estimation to be 0, so that we can see effect on
+-- rows alone.
+SET seq_page_cost = 0;
+SET cpu_tuple_cost = 0;
+SET cpu_operator_cost = 0;
+
+-- Estimated rows should be 50
+EXPLAIN SELECT 1 FROM test__mce WHERE a @@ '2147483647';
+
+RESET seq_page_cost;
+RESET cpu_tuple_cost;
+RESET cpu_operator_cost;
+
+DROP TABLE test__mce;
-- 
2.39.5 (Apple Git-154)

