From ced278119623b4f6bf705680291ab6ff18085c23 Mon Sep 17 00:00:00 2001 From: "Chao Li (Evan)" 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 --- 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)