Skip to main content

Range

PostgreSQL's range types (int4range, int8range, numrange, daterange, tsrange, tstzrange, and their multirange counterparts) all decode to a Range<T>, a pair of bounds each of which may be absent (unbounded) and each of which may or may not be included. The bounds are the element type's own JS values, so a tstzrange holds Dates and an int4range holds numbers.

const r = new Range(1, 10); // default bounds, [1,10)
r.lowerInclusive; // true
r.upperInclusive; // false
String(r); // '[1,10)'

new Range(null, 10); // '(,10)' - unbounded below
new Range(1, 10, '[]'); // '[1,10]'
Range.empty(); // 'empty' - no values at all, not the same as '(,)'

A multirange decodes to a plain Range[].

Constructor​

new Range<T>(lower?, upper?, bounds?, oid?)

ArgumentTypeDefaultDescription
lowerT | nullnullLower bound, or null for unbounded below
upperT | nullnullUpper bound, or null for unbounded above
bounds'[)' | '[]' | '()' | '(]''[)'Which ends include their bound
oidOIDWhich range type this is — see below

An absent bound is never inclusive regardless of bounds; the server prints (/) for it whatever was asked.

Properties​

KeyTypeReadonlyDescription
lowerT | nullfalse
upperT | nullfalse
lowerInclusivebooleanfalse
upperInclusivebooleanfalse
isEmptybooleanfalsetrue for empty

Methods​

MethodReturnsDescription
Range.empty<T>(oid?)Range<T>Static. The empty range
toString()stringAs PostgreSQL prints it — casts back through the range's own type
toPostgres()stringSame as toString() — the pg-convention name for "write yourself back"
toJSON()object{ lower, upper, lowerInclusive, upperInclusive, isEmpty }

Naming the type: oid and BindParam​

All six range types answer instanceof Range, and nothing about a Range on its own says which one it is: a Range of numbers could be int4range or numrange, a Range of Dates could be daterange, tsrange or tstzrange. A Range decoded from a query already carries the OID it came from (internally and invisibly), so it works when passed back as a parameter without naming it again:

const r = (await connection.query('select int4range(1,10) as r')).rows[0][0];
await connection.query('select $1::int4range', { params: [r] }); // r already knows it's int4range

A Range you build yourself needs the OID given explicitly, either as the constructor's fourth argument or via BindParam at the call site. Otherwise it throws instead of being silently sent as JSON:

import { Range, DataTypeOIDs, BindParam } from 'postgrejs';

new Range(1, 10, '[)', DataTypeOIDs.int4range);
// or:
await connection.query('select $1', { params: [new BindParam(DataTypeOIDs.int4range, new Range(1, 10))] });

Note what a discrete element type does to the bounds: PostgreSQL normalizes int4range(1,10,'[]') to [1,11), since those describe the same set of integers. So upperInclusive comes back false for every int4range/int8range/daterange, and that is the server normalizing, not this class losing information.

See Data Types & Type Mapping for the full guide.