<!ENTITY mvcc SYSTEM "mvcc.sgml">
<!ENTITY perform SYSTEM "perform.sgml">
<!ENTITY queries SYSTEM "queries.sgml">
-<!entity rangetypes SYSTEM "rangetypes.sgml">
+<!ENTITY rangetypes SYSTEM "rangetypes.sgml">
<!ENTITY rowtypes SYSTEM "rowtypes.sgml">
<!ENTITY syntax SYSTEM "syntax.sgml">
<!ENTITY textsearch SYSTEM "textsearch.sgml">
<sect1 id="functions-range">
<title>Range Functions and Operators</title>
+ <para>
+ See <xref linkend="rangetypes"> for an overview of range types.
+ </para>
+
<para>
<xref linkend="range-operators-table"> shows the operators
available for range types.
<row>
<entry> <literal>&<</literal> </entry>
- <entry>Does not extend to the right of?</entry>
+ <entry>does not extend to the right of</entry>
<entry><literal>int8range(1,20) &< int8range(18,20)</literal></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&></literal> </entry>
- <entry>Does not extend to the left of?</entry>
+ <entry>does not extend to the left of</entry>
<entry><literal>int8range(7,20) &> int8range(5,10)</literal></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>-|-</literal> </entry>
- <entry>adjacent?</entry>
+ <entry>is adjacent to</entry>
<entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>+</literal> </entry>
- <entry>Union</entry>
+ <entry>union</entry>
<entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
<entry><literal>[5,20)</literal></entry>
</row>
- <row>
- <entry> <literal>-</literal> </entry>
- <entry>Difference</entry>
- <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
- <entry><literal>[5,10)</literal></entry>
- </row>
-
<row>
<entry> <literal>*</literal> </entry>
- <entry>Intersection</entry>
+ <entry>intersection</entry>
<entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
<entry><literal>[10,15)</literal></entry>
</row>
+ <row>
+ <entry> <literal>-</literal> </entry>
+ <entry>difference</entry>
+ <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
+ <entry><literal>[5,10)</literal></entry>
+ </row>
+
</tbody>
</tgroup>
</table>
<para>
- Range comparisons compare the lower bounds first, and only if
- equal, compare the upper bounds. This is generally most useful for
- B-tree indexes, rather than being useful comparisons by themselves.
+ The simple comparison operators <literal><</literal>,
+ <literal>></literal>, <literal><=</literal>, and
+ <literal>>=</literal> compare the lower bounds first, and only if those
+ are equal, compare the upper bounds. These comparisons are not usually
+ very useful for ranges, but are provided to allow B-tree indexes to be
+ constructed on ranges.
</para>
<para>
- See <xref linkend="rangetypes"> for more details about range operator
- behavior.
+ The left-of/right-of/adjacent operators always return false when an empty
+ range is involved; that is, an empty range is not considered to be either
+ before or after any other range.
+ </para>
+
+ <para>
+ The union and difference operators will fail if the resulting range would
+ need to contain two disjoint sub-ranges, as such a range cannot be
+ represented.
</para>
<para>
<xref linkend="range-functions-table"> shows the functions
- available for use with range types. See <xref linkend="rangetypes">
- for more information and examples of the use of these functions.
+ available for use with range types.
</para>
<indexterm>
<function>lower</function>(<type>anyrange</type>)
</literal>
</entry>
- <entry><type>anyrange</type></entry>
+ <entry>range's element type</entry>
<entry>lower bound of range</entry>
<entry><literal>lower(numrange(1.1,2.2))</literal></entry>
<entry><literal>1.1</literal></entry>
<function>upper</function>(<type>anyrange</type>)
</literal>
</entry>
- <entry><type>anyrange</type></entry>
+ <entry>range's element type</entry>
<entry>upper bound of range</entry>
<entry><literal>upper(numrange(1.1,2.2))</literal></entry>
<entry><literal>2.2</literal></entry>
<function>isempty</function>(<type>anyrange</type>)
</literal>
</entry>
- <entry><type>anyrange</type></entry>
+ <entry><type>boolean</type></entry>
<entry>is the range empty?</entry>
<entry><literal>isempty(numrange(1.1,2.2))</literal></entry>
<entry><literal>false</literal></entry>
<function>lower_inc</function>(<type>anyrange</type>)
</literal>
</entry>
- <entry><type>anyrange</type></entry>
- <entry>is the lower bound of the range inclusive?</entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound inclusive?</entry>
<entry><literal>lower_inc(numrange(1.1,2.2))</literal></entry>
<entry><literal>true</literal></entry>
</row>
<function>upper_inc</function>(<type>anyrange</type>)
</literal>
</entry>
- <entry><type>anyrange</type></entry>
- <entry>is the upper bound of the range inclusive?</entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound inclusive?</entry>
<entry><literal>upper_inc(numrange(1.1,2.2))</literal></entry>
<entry><literal>false</literal></entry>
</row>
<function>lower_inf</function>(<type>anyrange</type>)
</literal>
</entry>
- <entry><type>anyrange</type></entry>
- <entry>is the lower bound of the range infinite?</entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound infinite?</entry>
<entry><literal>lower_inf('(,)'::daterange)</literal></entry>
<entry><literal>true</literal></entry>
</row>
<function>upper_inf</function>(<type>anyrange</type>)
</literal>
</entry>
- <entry><type>anyrange</type></entry>
- <entry>is the upper bound of the range infinite?</entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound infinite?</entry>
<entry><literal>upper_inf('(,)'::daterange)</literal></entry>
<entry><literal>true</literal></entry>
</row>
lower1.rngtypid != upper2.rngtypid)
elog(ERROR, "range types do not match");
+ /* An empty range is neither before nor after any other range */
if (empty1 || empty2)
- ereport(ERROR,
- (errcode(ERRCODE_DATA_EXCEPTION),
- errmsg("input range is empty")));
-
- if (range_cmp_bounds(fcinfo, &upper1, &lower2) < 0)
- PG_RETURN_BOOL(true);
- else
PG_RETURN_BOOL(false);
+
+ PG_RETURN_BOOL(range_cmp_bounds(fcinfo, &upper1, &lower2) < 0);
}
Datum
lower1.rngtypid != upper2.rngtypid)
elog(ERROR, "range types do not match");
+ /* An empty range is neither before nor after any other range */
if (empty1 || empty2)
- ereport(ERROR,
- (errcode(ERRCODE_DATA_EXCEPTION),
- errmsg("input range is empty")));
-
- if (range_cmp_bounds(fcinfo, &lower1, &upper2) > 0)
- PG_RETURN_BOOL(true);
- else
PG_RETURN_BOOL(false);
+
+ PG_RETURN_BOOL(range_cmp_bounds(fcinfo, &lower1, &upper2) > 0);
}
Datum
lower1.rngtypid != upper2.rngtypid)
elog(ERROR, "range types do not match");
+ /* An empty range is not adjacent to any other range */
if (empty1 || empty2)
- ereport(ERROR,
- (errcode(ERRCODE_DATA_EXCEPTION),
- errmsg("undefined for empty ranges")));
+ PG_RETURN_BOOL(false);
/*
* For two ranges to be adjacent, the lower boundary of one range has to
lower1.rngtypid != upper2.rngtypid)
elog(ERROR, "range types do not match");
+ /* An empty range does not overlap any other range */
if (empty1 || empty2)
PG_RETURN_BOOL(false);
lower1.rngtypid != upper2.rngtypid)
elog(ERROR, "range types do not match");
+ /* An empty range is neither before nor after any other range */
if (empty1 || empty2)
PG_RETURN_BOOL(false);
lower1.rngtypid != upper2.rngtypid)
elog(ERROR, "range types do not match");
+ /* An empty range is neither before nor after any other range */
if (empty1 || empty2)
PG_RETURN_BOOL(false);
lower1.rngtypid != upper2.rngtypid)
elog(ERROR, "range types do not match");
+ /* if either is empty, r1 is the correct answer */
if (empty1 || empty2)
PG_RETURN_RANGE(r1);
range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+ /* if either is empty, the other is the correct answer */
if (empty1)
PG_RETURN_RANGE(r2);
if (empty2)
lower1.rngtypid != upper2.rngtypid)
elog(ERROR, "range types do not match");
+ /* For b-tree use, empty ranges sort before all else */
if (empty1 && empty2)
PG_RETURN_INT32(0);
else if (empty1)
(!,"[")
(1 row)
-drop type textrange;
--
-- create some test data and test the operators
--
[01-10-2000,01-21-2000)
(1 row)
+-- test GiST index that's been built incrementally
create table test_range_gist(ir int4range);
create index test_range_gist_idx on test_range_gist using gist (ir);
insert into test_range_gist select int4range(g, g+10) from generate_series(1,2000) g;
insert into test_range_gist select int4range(NULL,g*10,'(]') from generate_series(1,100) g;
insert into test_range_gist select int4range(g*10,NULL,'(]') from generate_series(1,100) g;
insert into test_range_gist select int4range(g, g+10) from generate_series(1,2000) g;
-BEGIN;
-SET LOCAL enable_seqscan = t;
-SET LOCAL enable_bitmapscan = f;
-SET LOCAL enable_indexscan = f;
+-- first, verify non-indexed results
+SET enable_seqscan = t;
+SET enable_indexscan = f;
+SET enable_bitmapscan = f;
select count(*) from test_range_gist where ir @> 'empty'::int4range;
count
-------
1062
(1 row)
-select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir << int4range(100,500);
+select count(*) from test_range_gist where ir << int4range(100,500);
count
-------
189
(1 row)
-select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir >> int4range(100,500);
+select count(*) from test_range_gist where ir >> int4range(100,500);
count
-------
3554
(1 row)
-select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir &< int4range(100,500);
+select count(*) from test_range_gist where ir &< int4range(100,500);
count
-------
1029
(1 row)
-select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir &> int4range(100,500);
+select count(*) from test_range_gist where ir &> int4range(100,500);
count
-------
4794
(1 row)
-select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir -|- int4range(100,500);
+select count(*) from test_range_gist where ir -|- int4range(100,500);
count
-------
5
(1 row)
-COMMIT;
-BEGIN;
-SET LOCAL enable_seqscan = f;
-SET LOCAL enable_bitmapscan = f;
-SET LOCAL enable_indexscan = t;
+-- now check same queries using index
+SET enable_seqscan = f;
+SET enable_indexscan = t;
+SET enable_bitmapscan = f;
select count(*) from test_range_gist where ir @> 'empty'::int4range;
count
-------
5
(1 row)
-COMMIT;
+-- now check same queries using a bulk-loaded index
drop index test_range_gist_idx;
create index test_range_gist_idx on test_range_gist using gist (ir);
-BEGIN;
-SET LOCAL enable_seqscan = f;
-SET LOCAL enable_bitmapscan = f;
-SET LOCAL enable_indexscan = t;
select count(*) from test_range_gist where ir @> 'empty'::int4range;
count
-------
5
(1 row)
-COMMIT;
-drop table test_range_gist;
+RESET enable_seqscan;
+RESET enable_indexscan;
+RESET enable_bitmapscan;
--
-- Btree_gist is not included by default, so to test exclusion
-- constraints with range types, use singleton int ranges for the "="
values(int4range(125), int4range(1), '[2010-01-02 10:10, 2010-01-02 11:10)');
ERROR: conflicting key value violates exclusion constraint "test_range_excl_speaker_during_excl"
DETAIL: Key (speaker, during)=([1,2), ["Sat Jan 02 10:10:00 2010","Sat Jan 02 11:10:00 2010")) conflicts with existing key (speaker, during)=([1,2), ["Sat Jan 02 10:00:00 2010","Sat Jan 02 11:00:00 2010")).
-drop table test_range_excl;
-- test bigint ranges
select int8range(10000000000::int8, 20000000000::int8,'(]');
int8range
["{1,2}","{2,1}")
(1 row)
-drop type arrayrange;
--
-- OUT/INOUT/TABLE functions
--
student | f
tenk1 | t
tenk2 | t
+ test_range_excl | t
+ test_range_gist | t
test_tsvector | f
text_tbl | f
time_tbl | f
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
-(148 rows)
+(150 rows)
--
-- another sanity check: every system catalog that has OIDs should have
subselect_tbl
tenk1
tenk2
+ test_range_excl
+ test_range_gist
test_tsvector
text_tbl
time_tbl
toyemp
varchar_tbl
xacttest
-(102 rows)
+(104 rows)
SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer')));
name
select '(!,()'::textrange;
select '(!,[)'::textrange;
-drop type textrange;
-
--
-- create some test data and test the operators
--
select daterange('2000-01-10'::date, '2000-01-20'::date,'(]');
select daterange('2000-01-10'::date, '2000-01-20'::date,'[]');
+-- test GiST index that's been built incrementally
create table test_range_gist(ir int4range);
create index test_range_gist_idx on test_range_gist using gist (ir);
insert into test_range_gist select int4range(g*10,NULL,'(]') from generate_series(1,100) g;
insert into test_range_gist select int4range(g, g+10) from generate_series(1,2000) g;
-BEGIN;
-SET LOCAL enable_seqscan = t;
-SET LOCAL enable_bitmapscan = f;
-SET LOCAL enable_indexscan = f;
+-- first, verify non-indexed results
+SET enable_seqscan = t;
+SET enable_indexscan = f;
+SET enable_bitmapscan = f;
select count(*) from test_range_gist where ir @> 'empty'::int4range;
select count(*) from test_range_gist where ir = int4range(10,20);
select count(*) from test_range_gist where ir @> int4range(10,20);
select count(*) from test_range_gist where ir && int4range(10,20);
select count(*) from test_range_gist where ir <@ int4range(10,50);
-select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir << int4range(100,500);
-select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir >> int4range(100,500);
-select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir &< int4range(100,500);
-select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir &> int4range(100,500);
-select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir -|- int4range(100,500);
-COMMIT;
-
-BEGIN;
-SET LOCAL enable_seqscan = f;
-SET LOCAL enable_bitmapscan = f;
-SET LOCAL enable_indexscan = t;
+select count(*) from test_range_gist where ir << int4range(100,500);
+select count(*) from test_range_gist where ir >> int4range(100,500);
+select count(*) from test_range_gist where ir &< int4range(100,500);
+select count(*) from test_range_gist where ir &> int4range(100,500);
+select count(*) from test_range_gist where ir -|- int4range(100,500);
+
+-- now check same queries using index
+SET enable_seqscan = f;
+SET enable_indexscan = t;
+SET enable_bitmapscan = f;
select count(*) from test_range_gist where ir @> 'empty'::int4range;
select count(*) from test_range_gist where ir = int4range(10,20);
select count(*) from test_range_gist where ir &< int4range(100,500);
select count(*) from test_range_gist where ir &> int4range(100,500);
select count(*) from test_range_gist where ir -|- int4range(100,500);
-COMMIT;
+-- now check same queries using a bulk-loaded index
drop index test_range_gist_idx;
create index test_range_gist_idx on test_range_gist using gist (ir);
-BEGIN;
-SET LOCAL enable_seqscan = f;
-SET LOCAL enable_bitmapscan = f;
-SET LOCAL enable_indexscan = t;
-
select count(*) from test_range_gist where ir @> 'empty'::int4range;
select count(*) from test_range_gist where ir = int4range(10,20);
select count(*) from test_range_gist where ir @> 10;
select count(*) from test_range_gist where ir &< int4range(100,500);
select count(*) from test_range_gist where ir &> int4range(100,500);
select count(*) from test_range_gist where ir -|- int4range(100,500);
-COMMIT;
-drop table test_range_gist;
+RESET enable_seqscan;
+RESET enable_indexscan;
+RESET enable_bitmapscan;
--
-- Btree_gist is not included by default, so to test exclusion
insert into test_range_excl
values(int4range(125), int4range(1), '[2010-01-02 10:10, 2010-01-02 11:10)');
-drop table test_range_excl;
-
-- test bigint ranges
select int8range(10000000000::int8, 20000000000::int8,'(]');
-- test tstz ranges
select arrayrange(ARRAY[1,2], ARRAY[2,1]);
-drop type arrayrange;
-
--
-- OUT/INOUT/TABLE functions
--