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
