diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c index 815175a654..099463cd86 100644 --- a/src/backend/utils/adt/rangetypes.c +++ b/src/backend/utils/adt/rangetypes.c @@ -183,8 +183,10 @@ range_recv(PG_FUNCTION_ARGS) flags &= (RANGE_EMPTY | RANGE_LB_INC | RANGE_LB_INF | + RANGE_LB_NULL | RANGE_UB_INC | - RANGE_UB_INF); + RANGE_UB_INF | + RANGE_UB_NULL); /* receive the bounds ... */ if (RANGE_HAS_LBOUND(flags)) @@ -444,6 +446,27 @@ range_lower(PG_FUNCTION_ARGS) PG_RETURN_DATUM(lower.val); } +Datum +range_start(PG_FUNCTION_ARGS) +{ + RangeType *r1 = PG_GETARG_RANGE_P(0); + TypeCacheEntry *typcache; + RangeBound lower; + RangeBound upper; + bool empty; + char flags = range_get_flags(r1); + + typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1)); + + range_deserialize(typcache, r1, &lower, &upper, &empty); + + /* Return NULL if there's no finite lower bound */ + if (!RANGE_HAS_LBOUND(flags)) + PG_RETURN_NULL(); + + PG_RETURN_DATUM(lower.val); +} + /* extract upper bound value */ Datum range_upper(PG_FUNCTION_ARGS) @@ -465,6 +488,27 @@ range_upper(PG_FUNCTION_ARGS) PG_RETURN_DATUM(upper.val); } +Datum +range_end(PG_FUNCTION_ARGS) +{ + RangeType *r1 = PG_GETARG_RANGE_P(0); + TypeCacheEntry *typcache; + RangeBound lower; + RangeBound upper; + bool empty; + char flags = range_get_flags(r1); + + typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1)); + + range_deserialize(typcache, r1, &lower, &upper, &empty); + + /* Return NULL if there's no finite upper bound */ + if (!RANGE_HAS_UBOUND(flags)) + PG_RETURN_NULL(); + + PG_RETURN_DATUM(upper.val); +} + /* range -> bool functions */ @@ -1677,7 +1721,7 @@ range_serialize(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper, Assert(!upper->lower); if (empty) - flags |= RANGE_EMPTY; + flags |= RANGE_EMPTY | RANGE_LB_NULL | RANGE_UB_NULL; else { cmp = range_cmp_bound_values(typcache, lower, upper); @@ -2188,7 +2232,7 @@ range_parse(const char *string, char *flags, char **lbound_str, if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL, strlen(RANGE_EMPTY_LITERAL)) == 0) { - *flags = RANGE_EMPTY; + *flags = RANGE_EMPTY | RANGE_LB_NULL | RANGE_UB_NULL; *lbound_str = NULL; *ubound_str = NULL; diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 1487710d59..27239c214e 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -9868,9 +9868,15 @@ { oid => '3848', descr => 'lower bound of range', proname => 'lower', prorettype => 'anyelement', proargtypes => 'anyrange', prosrc => 'range_lower' }, +{ oid => '8104', descr => 'range start position', + proname => 'range_start', prorettype => 'anyelement', proargtypes => 'anyrange', + prosrc => 'range_start' }, { oid => '3849', descr => 'upper bound of range', proname => 'upper', prorettype => 'anyelement', proargtypes => 'anyrange', prosrc => 'range_upper' }, +{ oid => '8105', descr => 'range end position', + proname => 'range_end', prorettype => 'anyelement', proargtypes => 'anyrange', + prosrc => 'range_end' }, { oid => '3850', descr => 'is the range empty?', proname => 'isempty', prorettype => 'bool', proargtypes => 'anyrange', prosrc => 'range_empty' }, diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h index 04c302c619..b099409048 100644 --- a/src/include/utils/rangetypes.h +++ b/src/include/utils/rangetypes.h @@ -40,18 +40,14 @@ typedef struct #define RANGE_UB_INC 0x04 /* upper bound is inclusive */ #define RANGE_LB_INF 0x08 /* lower bound is -infinity */ #define RANGE_UB_INF 0x10 /* upper bound is +infinity */ -#define RANGE_LB_NULL 0x20 /* lower bound is null (NOT USED) */ -#define RANGE_UB_NULL 0x40 /* upper bound is null (NOT USED) */ +#define RANGE_LB_NULL 0x20 /* lower bound is null */ +#define RANGE_UB_NULL 0x40 /* upper bound is null */ #define RANGE_CONTAIN_EMPTY 0x80 /* marks a GiST internal-page entry whose * subtree contains some empty ranges */ -#define RANGE_HAS_LBOUND(flags) (!((flags) & (RANGE_EMPTY | \ - RANGE_LB_NULL | \ - RANGE_LB_INF))) +#define RANGE_HAS_LBOUND(flags) (!((flags) & (RANGE_LB_NULL | RANGE_LB_INF))) -#define RANGE_HAS_UBOUND(flags) (!((flags) & (RANGE_EMPTY | \ - RANGE_UB_NULL | \ - RANGE_UB_INF))) +#define RANGE_HAS_UBOUND(flags) (!((flags) & (RANGE_UB_NULL | RANGE_UB_INF))) #define RangeIsEmpty(r) ((range_get_flags(r) & RANGE_EMPTY) != 0) #define RangeIsOrContainsEmpty(r) \ diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out index 05b882fde9..68d00e5a83 100644 --- a/src/test/regress/expected/rangetypes.out +++ b/src/test/regress/expected/rangetypes.out @@ -181,6 +181,78 @@ select '(a,a)'::textrange; empty (1 row) +select lower('[a,a)'::textrange); + lower +------- + +(1 row) + +select lower('(a,a]'::textrange); + lower +------- + +(1 row) + +select lower('(a,a)'::textrange); + lower +------- + +(1 row) + +select upper('[a,a)'::textrange); + upper +------- + +(1 row) + +select upper('(a,a]'::textrange); + upper +------- + +(1 row) + +select upper('(a,a)'::textrange); + upper +------- + +(1 row) + +select range_start('[a,a)'::textrange); + range_start +------------- + a +(1 row) + +select range_start('(a,a]'::textrange); + range_start +------------- + a +(1 row) + +select range_start('(a,a)'::textrange); + range_start +------------- + a +(1 row) + +select range_end('[a,a)'::textrange); + range_end +----------- + a +(1 row) + +select range_end('(a,a]'::textrange); + range_end +----------- + a +(1 row) + +select range_end('(a,a)'::textrange); + range_end +----------- + a +(1 row) + -- -- create some test data and test the operators -- diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql index e1b8917c0c..fed62d082f 100644 --- a/src/test/regress/sql/rangetypes.sql +++ b/src/test/regress/sql/rangetypes.sql @@ -41,6 +41,18 @@ select '[a,a]'::textrange; select '[a,a)'::textrange; select '(a,a]'::textrange; select '(a,a)'::textrange; +select lower('[a,a)'::textrange); +select lower('(a,a]'::textrange); +select lower('(a,a)'::textrange); +select upper('[a,a)'::textrange); +select upper('(a,a]'::textrange); +select upper('(a,a)'::textrange); +select range_start('[a,a)'::textrange); +select range_start('(a,a]'::textrange); +select range_start('(a,a)'::textrange); +select range_end('[a,a)'::textrange); +select range_end('(a,a]'::textrange); +select range_end('(a,a)'::textrange); -- -- create some test data and test the operators