GiST multirange index scans can fail to return rows

Started by Peter Geoghegan9 days ago7 messageshackers
Jump to latest

The attached regression test patch (written by Claude Code) shows that
a GiST index on a multirange column can fail to return rows during
scans that use the contained-by operator.

The multirangetypes test already runs every GiST query twice, once
with a sequential scan and once with the index, and the two must
agree. The added query asks for rows contained in
'{[1,11),[21,31),[41,51)}', which is a value the test table already
contains. The sequential scan returns 501 and the index scan returns
500. The row it loses has a multirange that exactly matches the one
from the query.

The multirange GiST support was added by db6335b5b. This is an issue
on 14 and up.

--
Peter Geoghegan

Attachments:

0001-Add-regression-test-for-multirange-GiST-containment-.patchapplication/octet-stream; name=0001-Add-regression-test-for-multirange-GiST-containment-.patchDownload+14-1
#2Andrey Borodin
amborodin@acm.org
In reply to: Peter Geoghegan (#1)
Re: GiST multirange index scans can fail to return rows

On 1 Aug 2026, at 20:01, Peter Geoghegan <pg@bowt.ie> wrote:

<0001-Add-regression-test-for-multirange-GiST-containment-.patch>

Hi Peter,

I agree with the test and also think this is a bug. The check in
multirange_gist_consistent() is too strict and rejects tuples that
should reach the recheck. I think we should use
range_gist_consistent_int_multirange() for this case. Using the
internal predicate for every strategy would also be correct, but would
weaken filtering and, IMO, cause unnecessary heap rechecks.

PFA v2 patch set. The first patch is your test, and the second is the
fix as I see it.

Thanks!

Best regards, Andrey Borodin.

Attachments:

v2-0001-Add-regression-test-for-multirange-GiST-contained.patchapplication/octet-stream; name=v2-0001-Add-regression-test-for-multirange-GiST-contained.patch; x-unix-mode=0644Download+14-1
v2-0001-Add-regression-test-for-multirange-GiST-contained.patchapplication/octet-stream; name=v2-0001-Add-regression-test-for-multirange-GiST-contained.patch; x-unix-mode=0644Download+14-1
In reply to: Andrey Borodin (#2)
Re: GiST multirange index scans can fail to return rows

Hi Andrey,

I think that you forgot to attach the patch with the actual fix?

--
Peter Geoghegan

On Sun, Aug 2, 2026 at 2:12 AM Andrey Borodin <x4mmm@yandex-team.ru> wrote:

On 1 Aug 2026, at 20:01, Peter Geoghegan <pg@bowt.ie> wrote:

<0001-Add-regression-test-for-multirange-GiST-containment-.patch>

Hi Peter,

I agree with the test and also think this is a bug. The check in
multirange_gist_consistent() is too strict and rejects tuples that
should reach the recheck. I think we should use
range_gist_consistent_int_multirange() for this case. Using the
internal predicate for every strategy would also be correct, but would
weaken filtering and, IMO, cause unnecessary heap rechecks.

PFA v2 patch set. The first patch is your test, and the second is the
fix as I see it.

Thanks!

Best regards, Andrey Borodin.

--
Peter Geoghegan

#4Andrey Borodin
amborodin@acm.org
In reply to: Peter Geoghegan (#3)
Re: GiST multirange index scans can fail to return rows

On 2 Aug 2026, at 12:03, Peter Geoghegan <pg@bowt.ie> wrote:

I think that you forgot to attach the patch with the actual fix?

Oops. Sorry.

Best regards, Andrey Borodin.

Attachments:

v2-0002-Fix-GiST-contained-by-scans-of-multiranges.patchapplication/octet-stream; name=v2-0002-Fix-GiST-contained-by-scans-of-multiranges.patch; x-unix-mode=0644Download+16-3
v2-0001-Add-regression-test-for-multirange-GiST-contained.patchapplication/octet-stream; name=v2-0001-Add-regression-test-for-multirange-GiST-contained.patch; x-unix-mode=0644Download+14-1
#5Alexander Korotkov
aekorotkov@gmail.com
In reply to: Andrey Borodin (#4)
Re: GiST multirange index scans can fail to return rows

Hi Peter,
Hi Andrey,

On Sun, Aug 2, 2026 at 9:55 AM Andrey Borodin <x4mmm@yandex-team.ru> wrote:

On 2 Aug 2026, at 12:03, Peter Geoghegan <pg@bowt.ie> wrote:

I think that you forgot to attach the patch with the actual fix?

Oops. Sorry.

Peter, thank you for catching this. Andrey, thank you for the patch.
Yes, multirange_gist_consistent() re-uses consistent functions from
range opclass. And range_gist_consistent_leaf_multirange() generate
false negatives when applied to union range of multirange. I've
rechecked, and it appears to be the only case among the predicates.

Regarding this contained-by case, I think we can build more precise
test function. If we have multiranges q and k, and union() produces
union range from multirange.

q @> r doesn't mean q @> union(r)
But if q @> r then union(q) @> union(r)

------
Regards,
Alexander Korotkov
Supabase

Attachments:

v3-0001-Fix-GiST-contained-by-scans-of-multiranges.patchapplication/octet-stream; name=v3-0001-Fix-GiST-contained-by-scans-of-multiranges.patchDownload+71-3
#6Kirill Reshke
reshkekirill@gmail.com
In reply to: Alexander Korotkov (#5)
Re: GiST multirange index scans can fail to return rows

On Sun, 2 Aug 2026 at 20:30, Alexander Korotkov <aekorotkov@gmail.com> wrote:

Hi Peter,
Hi Andrey,

On Sun, Aug 2, 2026 at 9:55 AM Andrey Borodin <x4mmm@yandex-team.ru> wrote:

On 2 Aug 2026, at 12:03, Peter Geoghegan <pg@bowt.ie> wrote:

I think that you forgot to attach the patch with the actual fix?

Oops. Sorry.

Peter, thank you for catching this. Andrey, thank you for the patch.
Yes, multirange_gist_consistent() re-uses consistent functions from
range opclass. And range_gist_consistent_leaf_multirange() generate
false negatives when applied to union range of multirange. I've
rechecked, and it appears to be the only case among the predicates.

Regarding this contained-by case, I think we can build more precise
test function. If we have multiranges q and k, and union() produces
union range from multirange.

q @> r doesn't mean q @> union(r)
But if q @> r then union(q) @> union(r)

Hi!
I reviewed v3.

multirange_contains_union_range function comment contains typo at 'give'

'Every multiranges contains an infinite number of empty multiranges,
even an empty one.' also contain typos

Return statement can be :
return range_cmp_bounds(rangetyp, &lower1, &lower2) <= 0 &&
range_cmp_bounds(rangetyp, &upper1, &upper2) >= 0;

But I don't insist. Better for my taste.

--
Best regards,
Kirill Reshke

#7Alexander Korotkov
aekorotkov@gmail.com
In reply to: Kirill Reshke (#6)
Re: GiST multirange index scans can fail to return rows

On Sun, Aug 2, 2026 at 8:04 PM Kirill Reshke <reshkekirill@gmail.com> wrote:

On Sun, 2 Aug 2026 at 20:30, Alexander Korotkov <aekorotkov@gmail.com> wrote:

On Sun, Aug 2, 2026 at 9:55 AM Andrey Borodin <x4mmm@yandex-team.ru> wrote:

On 2 Aug 2026, at 12:03, Peter Geoghegan <pg@bowt.ie> wrote:

I think that you forgot to attach the patch with the actual fix?

Oops. Sorry.

Peter, thank you for catching this. Andrey, thank you for the patch.
Yes, multirange_gist_consistent() re-uses consistent functions from
range opclass. And range_gist_consistent_leaf_multirange() generate
false negatives when applied to union range of multirange. I've
rechecked, and it appears to be the only case among the predicates.

Regarding this contained-by case, I think we can build more precise
test function. If we have multiranges q and k, and union() produces
union range from multirange.

q @> r doesn't mean q @> union(r)
But if q @> r then union(q) @> union(r)

Hi!
I reviewed v3.

multirange_contains_union_range function comment contains typo at 'give'

'Every multiranges contains an infinite number of empty multiranges,
even an empty one.' also contain typos

Thank you, fixed.

Return statement can be :
return range_cmp_bounds(rangetyp, &lower1, &lower2) <= 0 &&
range_cmp_bounds(rangetyp, &upper1, &upper2) >= 0;

But I don't insist. Better for my taste.

Corrected as you proposed.

------
Regards,
Alexander Korotkov
Supabase

Attachments:

v4-0001-Fix-GiST-contained-by-scans-of-multiranges.patchapplication/octet-stream; name=v4-0001-Fix-GiST-contained-by-scans-of-multiranges.patchDownload+68-3