Skip to content

Commit bf4f96b

Browse files
committed
Fix range_cmp_bounds for the case of equal-valued exclusive bounds.
Also improve its comments and related regression tests. Jeff Davis, with some further adjustments by Tom
1 parent ef27c81 commit bf4f96b

File tree

3 files changed

+126
-26
lines changed

3 files changed

+126
-26
lines changed

src/backend/utils/adt/rangetypes.c

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1622,33 +1622,89 @@ make_range(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper,
16221622
return range;
16231623
}
16241624

1625+
/*
1626+
* Compare two range boundary points, returning <0, 0, or >0 according to
1627+
* whether b1 is less than, equal to, or greater than b2.
1628+
*
1629+
* The boundaries can be any combination of upper and lower; so it's useful
1630+
* for a variety of operators.
1631+
*
1632+
* The simple case is when b1 and b2 are both finite and inclusive, in which
1633+
* case the result is just a comparison of the values held in b1 and b2.
1634+
*
1635+
* If a bound is exclusive, then we need to know whether it's a lower bound,
1636+
* in which case we treat the boundary point as "just greater than" the held
1637+
* value; or an upper bound, in which case we treat the boundary point as
1638+
* "just less than" the held value.
1639+
*
1640+
* If a bound is infinite, it represents minus infinity (less than every other
1641+
* point) if it's a lower bound; or plus infinity (greater than every other
1642+
* point) if it's an upper bound.
1643+
*
1644+
* There is only one case where two boundaries compare equal but are not
1645+
* identical: when both bounds are inclusive and hold the same finite value,
1646+
* but one is an upper bound and the other a lower bound.
1647+
*/
16251648
int
16261649
range_cmp_bounds(TypeCacheEntry *typcache, RangeBound *b1, RangeBound *b2)
16271650
{
16281651
int32 result;
16291652

1653+
/*
1654+
* First, handle cases involving infinity, which don't require invoking
1655+
* the comparison proc.
1656+
*/
16301657
if (b1->infinite && b2->infinite)
16311658
{
1659+
/*
1660+
* Both are infinity, so they are equal unless one is lower and the
1661+
* other not.
1662+
*/
16321663
if (b1->lower == b2->lower)
16331664
return 0;
16341665
else
1635-
return (b1->lower) ? -1 : 1;
1666+
return b1->lower ? -1 : 1;
16361667
}
1637-
else if (b1->infinite && !b2->infinite)
1638-
return (b1->lower) ? -1 : 1;
1639-
else if (!b1->infinite && b2->infinite)
1640-
return (b2->lower) ? 1 : -1;
1668+
else if (b1->infinite)
1669+
return b1->lower ? -1 : 1;
1670+
else if (b2->infinite)
1671+
return b2->lower ? 1 : -1;
16411672

1673+
/*
1674+
* Both boundaries are finite, so compare the held values.
1675+
*/
16421676
result = DatumGetInt32(FunctionCall2Coll(&typcache->rng_cmp_proc_finfo,
16431677
typcache->rng_collation,
16441678
b1->val, b2->val));
16451679

1680+
/*
1681+
* If the comparison is anything other than equal, we're done. If they
1682+
* compare equal though, we still have to consider whether the boundaries
1683+
* are inclusive or exclusive.
1684+
*/
16461685
if (result == 0)
16471686
{
1648-
if (b1->inclusive && !b2->inclusive)
1649-
return (b2->lower) ? -1 : 1;
1650-
else if (!b1->inclusive && b2->inclusive)
1651-
return (b1->lower) ? 1 : -1;
1687+
if (!b1->inclusive && !b2->inclusive)
1688+
{
1689+
/* both are exclusive */
1690+
if (b1->lower == b2->lower)
1691+
return 0;
1692+
else
1693+
return b1->lower ? 1 : -1;
1694+
}
1695+
else if (!b1->inclusive)
1696+
return b1->lower ? 1 : -1;
1697+
else if (!b2->inclusive)
1698+
return b2->lower ? -1 : 1;
1699+
else
1700+
{
1701+
/*
1702+
* Both are inclusive and the values held are equal, so they are
1703+
* equal regardless of whether they are upper or lower boundaries,
1704+
* or a mix.
1705+
*/
1706+
return 0;
1707+
}
16521708
}
16531709

16541710
return result;

src/test/regress/expected/rangetypes.out

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,30 @@ ERROR: malformed range literal: "(a,])"
4848
LINE 1: select '(a,])'::textrange;
4949
^
5050
DETAIL: Junk after right parenthesis or bracket.
51+
select '( , )'::textrange;
52+
ERROR: range lower bound must be less than or equal to range upper bound
53+
LINE 1: select '( , )'::textrange;
54+
^
55+
select '("","")'::textrange;
56+
ERROR: range lower bound must be less than or equal to range upper bound
57+
LINE 1: select '("","")'::textrange;
58+
^
59+
select '(",",",")'::textrange;
60+
ERROR: range lower bound must be less than or equal to range upper bound
61+
LINE 1: select '(",",",")'::textrange;
62+
^
63+
select '("\\","\\")'::textrange;
64+
ERROR: range lower bound must be less than or equal to range upper bound
65+
LINE 1: select '("\\","\\")'::textrange;
66+
^
67+
select '[a,a)'::textrange;
68+
ERROR: range lower bound must be less than or equal to range upper bound
69+
LINE 1: select '[a,a)'::textrange;
70+
^
71+
select '(a,a]'::textrange;
72+
ERROR: range lower bound must be less than or equal to range upper bound
73+
LINE 1: select '(a,a]'::textrange;
74+
^
5175
-- should succeed
5276
select ' empty '::textrange;
5377
textrange
@@ -91,35 +115,36 @@ select '[a,]'::textrange;
91115
[a,)
92116
(1 row)
93117

94-
select '( , )'::textrange;
118+
select '(,)'::textrange;
95119
textrange
96120
-----------
97-
(" "," ")
121+
(,)
98122
(1 row)
99123

100-
select '("","")'::textrange;
124+
select '["",""]'::textrange;
101125
textrange
102126
-----------
103-
("","")
127+
["",""]
104128
(1 row)
105129

106-
select '["",""]'::textrange;
130+
select '[",",","]'::textrange;
107131
textrange
108132
-----------
109-
["",""]
133+
[",",","]
110134
(1 row)
111135

112-
select '(",",",")'::textrange;
136+
select '["\\","\\"]'::textrange;
137+
textrange
138+
-------------
139+
["\\","\\"]
140+
(1 row)
141+
142+
select '(\\,a)'::textrange;
113143
textrange
114144
-----------
115-
(",",",")
145+
("\\",a)
116146
(1 row)
117147

118-
select '("\\","\\")'::textrange
119-
select '(\\,a)'::textrange;
120-
ERROR: syntax error at or near "select"
121-
LINE 2: select '(\\,a)'::textrange;
122-
^
123148
select '((,z)'::textrange;
124149
textrange
125150
-----------
@@ -307,6 +332,18 @@ select numrange(1.0, 2.0) << numrange(3.0, 4.0);
307332
t
308333
(1 row)
309334

335+
select numrange(1.0, 3.0,'[]') << numrange(3.0, 4.0,'[]');
336+
?column?
337+
----------
338+
f
339+
(1 row)
340+
341+
select numrange(1.0, 3.0,'()') << numrange(3.0, 4.0,'()');
342+
?column?
343+
----------
344+
t
345+
(1 row)
346+
310347
select numrange(1.0, 2.0) >> numrange(3.0, 4.0);
311348
?column?
312349
----------

src/test/regress/sql/rangetypes.sql

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ select '(),a)'::textrange;
1515
select '(a,))'::textrange;
1616
select '(],a)'::textrange;
1717
select '(a,])'::textrange;
18+
select '( , )'::textrange;
19+
select '("","")'::textrange;
20+
select '(",",",")'::textrange;
21+
select '("\\","\\")'::textrange;
22+
select '[a,a)'::textrange;
23+
select '(a,a]'::textrange;
1824

1925
-- should succeed
2026
select ' empty '::textrange;
@@ -24,11 +30,10 @@ select '(,z)'::textrange;
2430
select '(a,)'::textrange;
2531
select '[,z]'::textrange;
2632
select '[a,]'::textrange;
27-
select '( , )'::textrange;
28-
select '("","")'::textrange;
33+
select '(,)'::textrange;
2934
select '["",""]'::textrange;
30-
select '(",",",")'::textrange;
31-
select '("\\","\\")'::textrange
35+
select '[",",","]'::textrange;
36+
select '["\\","\\"]'::textrange;
3237
select '(\\,a)'::textrange;
3338
select '((,z)'::textrange;
3439
select '([,z)'::textrange;
@@ -81,6 +86,8 @@ select range_minus(numrange(10.1,12.2,'[]'), numrange(0.0,120.2,'(]'));
8186

8287
select numrange(4.5, 5.5, '[]') && numrange(5.5, 6.5);
8388
select numrange(1.0, 2.0) << numrange(3.0, 4.0);
89+
select numrange(1.0, 3.0,'[]') << numrange(3.0, 4.0,'[]');
90+
select numrange(1.0, 3.0,'()') << numrange(3.0, 4.0,'()');
8491
select numrange(1.0, 2.0) >> numrange(3.0, 4.0);
8592
select numrange(3.0, 70.0) &< numrange(6.6, 100.0);
8693

0 commit comments

Comments
 (0)