From 03b41532199a3657dbb2d4b156347f18f025f942 Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <pg@bowt.ie>
Date: Sat, 1 Aug 2026 09:44:10 -0400
Subject: [PATCH] Add regression test for multirange GiST containment with a
 gapped query

A GiST leaf key for a multirange is its union range: a single range spanning
from the lowest bound to the highest, with the interior gaps flattened away.
That is a lossy summary, so a leaf match has to be rechecked against the real
value before it can be reported as a definitive answer.

For the "contained by" strategy the leaf helper tests the union range as though
it were the indexed multirange itself, and reports a definitive no-match.  The
row is dropped at the index level and never rechecked.

It takes a gap on both sides to see it.  The indexed value {[1,11),[21,31),
[41,51)} has union range [1,51); querying for rows contained in {[1,11),
[21,31),[41,51)} should match it, because each of its three parts fits inside a
part of the query.  But the query has gaps at [11,21) and [31,41), and those
lie inside [1,51), so the union range is not contained and the row is discarded.
A contiguous query multirange makes the two tests agree, which is why the
existing coverage does not catch this.

The test needs no new data: the row is already in test_multirange_gist, and the
file already runs every query twice, once with a sequential scan and once with
the index.  The sequential scan returns 501 and the index returns 500.

The expected file records the correct behaviour, so the test is red until this
is fixed -- deliberately, to keep the case documented in the tree.

Multiranges arrived in PostgreSQL 14, so this affects 14 and up.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---
 src/test/regress/expected/multirangetypes.out | 12 ++++++++++++
 src/test/regress/sql/multirangetypes.sql      |  2 ++
 2 files changed, 14 insertions(+)

diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
index d47ce4b6d..f6b1158ac 100644
--- a/src/test/regress/expected/multirangetypes.out
+++ b/src/test/regress/expected/multirangetypes.out
@@ -2610,6 +2610,12 @@ select count(*) from test_multirange_gist where mr <@ '{(10,30),(40,60),(70,90)}
    500
 (1 row)
 
+select count(*) from test_multirange_gist where mr <@ '{[1,11),[21,31),[41,51)}'::int4multirange;
+ count 
+-------
+   501
+(1 row)
+
 select count(*) from test_multirange_gist where mr << int4multirange(int4range(100,200), int4range(400,500));
  count 
 -------
@@ -2842,6 +2848,12 @@ select count(*) from test_multirange_gist where mr <@ '{(10,30),(40,60),(70,90)}
    500
 (1 row)
 
+select count(*) from test_multirange_gist where mr <@ '{[1,11),[21,31),[41,51)}'::int4multirange;
+ count 
+-------
+   501
+(1 row)
+
 select count(*) from test_multirange_gist where mr << int4multirange(int4range(100,200), int4range(400,500));
  count 
 -------
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
index cf0fff6fd..5ed7f9cbd 100644
--- a/src/test/regress/sql/multirangetypes.sql
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -505,6 +505,7 @@ select count(*) from test_multirange_gist where mr @> '{}'::int4multirange;
 select count(*) from test_multirange_gist where mr @> int4multirange(int4range(10,20), int4range(30,40));
 select count(*) from test_multirange_gist where mr && '{(10,20),(30,40),(50,60)}'::int4multirange;
 select count(*) from test_multirange_gist where mr <@ '{(10,30),(40,60),(70,90)}'::int4multirange;
+select count(*) from test_multirange_gist where mr <@ '{[1,11),[21,31),[41,51)}'::int4multirange;
 select count(*) from test_multirange_gist where mr << int4multirange(int4range(100,200), int4range(400,500));
 select count(*) from test_multirange_gist where mr >> int4multirange(int4range(100,200), int4range(400,500));
 select count(*) from test_multirange_gist where mr &< int4multirange(int4range(100,200), int4range(400,500));
@@ -550,6 +551,7 @@ select count(*) from test_multirange_gist where mr @> '{}'::int4multirange;
 select count(*) from test_multirange_gist where mr @> int4multirange(int4range(10,20), int4range(30,40));
 select count(*) from test_multirange_gist where mr && '{(10,20),(30,40),(50,60)}'::int4multirange;
 select count(*) from test_multirange_gist where mr <@ '{(10,30),(40,60),(70,90)}'::int4multirange;
+select count(*) from test_multirange_gist where mr <@ '{[1,11),[21,31),[41,51)}'::int4multirange;
 select count(*) from test_multirange_gist where mr << int4multirange(int4range(100,200), int4range(400,500));
 select count(*) from test_multirange_gist where mr >> int4multirange(int4range(100,200), int4range(400,500));
 select count(*) from test_multirange_gist where mr &< int4multirange(int4range(100,200), int4range(400,500));
-- 
2.53.0

