From 759c5f4647cd75932aac57b00f6db06707a2ca06 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:28:20 -0700
Subject: [PATCH v3 2/4] multirange operators and functions

---
 src/backend/utils/adt/multirangetypes.c       | 1422 ++++++++++++++-
 src/backend/utils/adt/rangetypes.c            |  230 ++-
 src/include/catalog/pg_aggregate.dat          |   11 +
 src/include/catalog/pg_amproc.dat             |    5 +-
 src/include/catalog/pg_operator.dat           |  169 ++
 src/include/catalog/pg_proc.dat               |  185 ++
 src/include/utils/multirangetypes.h           |   34 +
 src/include/utils/rangetypes.h                |   24 +-
 src/test/regress/expected/dependency.out      |    1 +
 src/test/regress/expected/multirangetypes.out | 2397 +++++++++++++++++++++++++
 src/test/regress/expected/opr_sanity.out      |    4 +-
 src/test/regress/expected/rangetypes.out      |   38 +-
 src/test/regress/expected/sanity_check.out    |    2 +
 src/test/regress/sql/multirangetypes.sql      |  607 +++++++
 src/test/regress/sql/rangetypes.sql           |   13 +
 15 files changed, 5041 insertions(+), 101 deletions(-)

diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index 5323a6a635..c509994796 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -726,45 +726,1267 @@ multirange_constructor0(PG_FUNCTION_ARGS)
 }
 
 
+/* multirange, multirange -> multirange type functions */
+
+/* multirange union */
+Datum
+range_union_range(PG_FUNCTION_ARGS)
+{
+	RangeType  *r1 = PG_GETARG_RANGE_P(0);
+	RangeType  *r2 = PG_GETARG_RANGE_P(1);
+	Oid			mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+	TypeCacheEntry *typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+	MultirangeType *mr = make_multirange(mltrngtypoid, typcache->rngtype, 1, &r2);
+
+	PG_RETURN_MULTIRANGE_P(range_union_multirange_internal(typcache, r1, mr));
+}
+
+Datum
+range_union_multirange(PG_FUNCTION_ARGS)
+{
+	RangeType  *r = PG_GETARG_RANGE_P(0);
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+	TypeCacheEntry *typcache;
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+	PG_RETURN_MULTIRANGE_P(range_union_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_union_range(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+	RangeType  *r = PG_GETARG_RANGE_P(1);
+	TypeCacheEntry *typcache;
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+	PG_RETURN_MULTIRANGE_P(range_union_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_union_multirange(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+	MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+	TypeCacheEntry *typcache;
+	int32		range_count1;
+	int32		range_count2;
+	int32		range_count3;
+	RangeType **ranges1;
+	RangeType **ranges2;
+	RangeType **ranges3;
+
+	if (MultirangeIsEmpty(mr1))
+		PG_RETURN_MULTIRANGE_P(mr2);
+	if (MultirangeIsEmpty(mr2))
+		PG_RETURN_MULTIRANGE_P(mr1);
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+	multirange_deserialize(mr1, &range_count1, &ranges1);
+	multirange_deserialize(mr2, &range_count2, &ranges2);
+
+	range_count3 = range_count1 + range_count2;
+	ranges3 = palloc0(range_count3 * sizeof(RangeType *));
+	memcpy(ranges3, ranges1, range_count1 * sizeof(RangeType *));
+	memcpy(ranges3 + range_count1, ranges2, range_count2 * sizeof(RangeType *));
+	PG_RETURN_MULTIRANGE_P(make_multirange(typcache->type_id, typcache->rngtype,
+										   range_count3, ranges3));
+}
+
+MultirangeType *
+range_union_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+								MultirangeType * mr)
+{
+	int32		range_count;
+	RangeType **ranges1;
+	RangeType **ranges2;
+
+	if (RangeIsEmpty(r))
+		return mr;
+	if (MultirangeIsEmpty(mr))
+		return make_multirange(typcache->type_id, typcache->rngtype, 1, &r);
+
+	multirange_deserialize(mr, &range_count, &ranges1);
+
+	ranges2 = palloc0((range_count + 1) * sizeof(RangeType *));
+
+	memcpy(ranges2, ranges1, range_count * sizeof(RangeType *));
+	ranges2[range_count] = r;
+	return make_multirange(typcache->type_id, typcache->rngtype, range_count + 1, ranges2);
+}
+
+/* multirange minus */
+Datum
+range_minus_range(PG_FUNCTION_ARGS)
+{
+	RangeType  *r1 = PG_GETARG_RANGE_P(0);
+	RangeType  *r2 = PG_GETARG_RANGE_P(1);
+	Oid			mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+	TypeCacheEntry *typcache;
+	TypeCacheEntry *rangetyp;
+
+	typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+	rangetyp = typcache->rngtype;
+
+	if (RangeIsEmpty(r1) || RangeIsEmpty(r2))
+		PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, rangetyp, 1, &r1));
+
+	PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+																rangetyp,
+																1,
+																&r1,
+																1,
+																&r2));
+}
+
+Datum
+range_minus_multirange(PG_FUNCTION_ARGS)
+{
+	RangeType  *r = PG_GETARG_RANGE_P(0);
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+	Oid			mltrngtypoid = MultirangeTypeGetOid(mr);
+	TypeCacheEntry *typcache;
+	TypeCacheEntry *rangetyp;
+	int32		range_count;
+	RangeType **ranges;
+
+	typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+	rangetyp = typcache->rngtype;
+
+	if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+		PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, rangetyp, 1, &r));
+
+	multirange_deserialize(mr, &range_count, &ranges);
+
+	PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+																rangetyp,
+																1,
+																&r,
+																range_count,
+																ranges));
+}
+
+Datum
+multirange_minus_range(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+	RangeType  *r = PG_GETARG_RANGE_P(1);
+	Oid			mltrngtypoid = MultirangeTypeGetOid(mr);
+	TypeCacheEntry *typcache;
+	TypeCacheEntry *rangetyp;
+	int32		range_count;
+	RangeType **ranges;
+
+	typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+	rangetyp = typcache->rngtype;
+
+	if (MultirangeIsEmpty(mr) || RangeIsEmpty(r))
+		PG_RETURN_MULTIRANGE_P(mr);
+
+	multirange_deserialize(mr, &range_count, &ranges);
+
+	PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+																rangetyp,
+																range_count,
+																ranges,
+																1,
+																&r));
+}
+
+Datum
+multirange_minus_multirange(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+	MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+	Oid			mltrngtypoid = MultirangeTypeGetOid(mr1);
+	TypeCacheEntry *typcache;
+	TypeCacheEntry *rangetyp;
+	int32		range_count1;
+	int32		range_count2;
+	RangeType **ranges1;
+	RangeType **ranges2;
+
+	typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+	rangetyp = typcache->rngtype;
+
+	if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+		PG_RETURN_MULTIRANGE_P(mr1);
+
+	multirange_deserialize(mr1, &range_count1, &ranges1);
+	multirange_deserialize(mr2, &range_count2, &ranges2);
+
+	PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+																rangetyp,
+																range_count1,
+																ranges1,
+																range_count2,
+																ranges2));
+}
+
+MultirangeType *
+multirange_minus_multirange_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+									 int32 range_count1, RangeType **ranges1, int32 range_count2, RangeType **ranges2)
+{
+	RangeType  *r1;
+	RangeType  *r2;
+	RangeType **ranges3;
+	int32		range_count3;
+	int32		i1;
+	int32		i2;
+
+	/*
+	 * Worst case: every range in ranges1 makes a different cut to some range
+	 * in ranges2.
+	 */
+	ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+	range_count3 = 0;
+
+	/*
+	 * For each range in mr1, keep subtracting until it's gone or the ranges
+	 * in mr2 have passed it. After a subtraction we assign what's left back
+	 * to r1. The parallel progress through mr1 and mr2 is similar to
+	 * multirange_overlaps_multirange_internal.
+	 */
+	r2 = ranges2[0];
+	for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+	{
+		r1 = ranges1[i1];
+
+		/* Discard r2s while r2 << r1 */
+		while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+		{
+			r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+		}
+
+		while (r2 != NULL)
+		{
+			if (range_split_internal(rangetyp, r1, r2, &ranges3[range_count3], &r1))
+			{
+				/*
+				 * If r2 takes a bite out of the middle of r1, we need two
+				 * outputs
+				 */
+				range_count3++;
+				r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+			}
+			else if (range_overlaps_internal(rangetyp, r1, r2))
+			{
+				/*
+				 * If r2 overlaps r1, replace r1 with r1 - r2.
+				 */
+				r1 = range_minus_internal(rangetyp, r1, r2);
+
+				/*
+				 * If r2 goes past r1, then we need to stay with it, in case
+				 * it hits future r1s. Otherwise we need to keep r1, in case
+				 * future r2s hit it. Since we already subtracted, there's no
+				 * point in using the overright/overleft calls.
+				 */
+				if (RangeIsEmpty(r1) || range_before_internal(rangetyp, r1, r2))
+					break;
+				else
+					r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+			}
+			else
+			{
+				/*
+				 * This and all future r2s are past r1, so keep them. Also
+				 * assign whatever is left of r1 to the result.
+				 */
+				break;
+			}
+		}
+
+		/*
+		 * Nothing else can remove anything from r1, so keep it. Even if r1 is
+		 * empty here, make_multirange will remove it.
+		 */
+		ranges3[range_count3++] = r1;
+	}
+
+	return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/* multirange intersection */
+Datum
+range_intersect_range(PG_FUNCTION_ARGS)
+{
+	RangeType  *r1 = PG_GETARG_RANGE_P(0);
+	RangeType  *r2 = PG_GETARG_RANGE_P(1);
+	Oid			mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+	TypeCacheEntry *typcache;
+	TypeCacheEntry *rangetyp;
+
+	typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+	rangetyp = typcache->rngtype;
+
+	if (RangeIsEmpty(r1) || RangeIsEmpty(r2))
+		PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+	PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+																	rangetyp,
+																	1,
+																	&r1,
+																	1,
+																	&r2));
+}
+
+Datum
+range_intersect_multirange(PG_FUNCTION_ARGS)
+{
+	RangeType  *r1 = PG_GETARG_RANGE_P(0);
+	MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+	Oid			mltrngtypoid = MultirangeTypeGetOid(mr2);
+	TypeCacheEntry *typcache;
+	TypeCacheEntry *rangetyp;
+	int32		range_count2;
+	RangeType **ranges2;
+
+	typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+	rangetyp = typcache->rngtype;
+
+	if (RangeIsEmpty(r1) || MultirangeIsEmpty(mr2))
+		PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+	multirange_deserialize(mr2, &range_count2, &ranges2);
+
+	PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+																	rangetyp,
+																	1,
+																	&r1,
+																	range_count2,
+																	ranges2));
+}
+
+Datum
+multirange_intersect_range(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+	RangeType  *r2 = PG_GETARG_RANGE_P(1);
+	Oid			mltrngtypoid = MultirangeTypeGetOid(mr1);
+	TypeCacheEntry *typcache;
+	TypeCacheEntry *rangetyp;
+	int32		range_count1;
+	RangeType **ranges1;
+
+	typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+	rangetyp = typcache->rngtype;
+
+	if (MultirangeIsEmpty(mr1) || RangeIsEmpty(r2))
+		PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+	multirange_deserialize(mr1, &range_count1, &ranges1);
+
+	PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+																	rangetyp,
+																	range_count1,
+																	ranges1,
+																	1,
+																	&r2));
+}
+
+Datum
+multirange_intersect_multirange(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+	MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+	Oid			mltrngtypoid = MultirangeTypeGetOid(mr1);
+	TypeCacheEntry *typcache;
+	TypeCacheEntry *rangetyp;
+	int32		range_count1;
+	int32		range_count2;
+	RangeType **ranges1;
+	RangeType **ranges2;
+
+	typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+	rangetyp = typcache->rngtype;
+
+	if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+		PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+	multirange_deserialize(mr1, &range_count1, &ranges1);
+	multirange_deserialize(mr2, &range_count2, &ranges2);
+
+	PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+																	rangetyp,
+																	range_count1,
+																	ranges1,
+																	range_count2,
+																	ranges2));
+}
+
+MultirangeType *
+multirange_intersect_multirange_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+										 int32 range_count1, RangeType **ranges1, int32 range_count2, RangeType **ranges2)
+{
+	RangeType  *r1;
+	RangeType  *r2;
+	RangeType **ranges3;
+	int32		range_count3;
+	int32		i1;
+	int32		i2;
+
+	/*
+	 * Worst case is a stitching pattern like this:
+	 *
+	 * mr1: --- --- --- ---
+	 * mr2:   --- --- ---
+	 * mr3:   - - - - - -
+	 *
+	 * That seems to be range_count1 + range_count2 - 1, but one extra won't
+	 * hurt.
+	 */
+	ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+	range_count3 = 0;
+
+	/*
+	 * For each range in mr1, keep intersecting until the ranges in mr2 have
+	 * passed it. The parallel progress through mr1 and mr2 is similar to
+	 * multirange_minus_multirange_internal, but we don't have to assign back
+	 * to r1.
+	 */
+	r2 = ranges2[0];
+	for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+	{
+		r1 = ranges1[i1];
+
+		/* Discard r2s while r2 << r1 */
+		while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+		{
+			r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+		}
+
+		while (r2 != NULL)
+		{
+			if (range_overlaps_internal(rangetyp, r1, r2))
+			{
+				/* Keep the overlapping part */
+				ranges3[range_count3++] = range_intersect_internal(rangetyp, r1, r2);
+
+				/* If we "used up" all of r2, go to the next one... */
+				if (range_overleft_internal(rangetyp, r2, r1))
+					r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+				/* ...otherwise go to the next r1 */
+				else
+					break;
+			}
+			else
+				/* We're past r1, so move to the next one */
+				break;
+		}
+
+		/* If we're out of r2s, there can be no more intersections */
+		if (r2 == NULL)
+			break;
+	}
+
+	return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ */
+Datum
+range_agg_transfn(PG_FUNCTION_ARGS)
+{
+	MemoryContext aggContext;
+	Oid			rngtypoid;
+	ArrayBuildState *state;
+
+	if (!AggCheckCallContext(fcinfo, &aggContext))
+		elog(ERROR, "range_agg_transfn called in non-aggregate context");
+
+	rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+	if (!type_is_range(rngtypoid))
+		ereport(ERROR, (errmsg("range_agg must be called with a range")));
+
+	if (PG_ARGISNULL(0))
+		state = initArrayResult(rngtypoid, aggContext, false);
+	else
+		state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+	/* skip NULLs */
+	if (!PG_ARGISNULL(1))
+		accumArrayResult(state, PG_GETARG_DATUM(1), false, rngtypoid, aggContext);
+
+	PG_RETURN_POINTER(state);
+}
+
+/*
+ * range_agg_finalfn: use our internal array to merge touching ranges.
+ */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+	MemoryContext aggContext;
+	Oid			mltrngtypoid;
+	TypeCacheEntry *typcache;
+	ArrayBuildState *state;
+	int32		range_count;
+	RangeType **ranges;
+	int			i;
+
+	if (!AggCheckCallContext(fcinfo, &aggContext))
+		elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+
+	state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+	if (state == NULL)
+		/* This shouldn't be possible, but just in case.... */
+		PG_RETURN_NULL();
+
+	/* Also return NULL if we had zero inputs, like other aggregates */
+	range_count = state->nelems;
+	if (range_count == 0)
+		PG_RETURN_NULL();
+
+	mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+	typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+	ranges = palloc0(range_count * sizeof(RangeType *));
+	for (i = 0; i < range_count; i++)
+		ranges[i] = DatumGetRangeTypeP(state->dvalues[i]);
+
+	PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, typcache->rngtype, range_count, ranges));
+}
+
+Datum
+multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+	MemoryContext aggContext;
+	Oid			mltrngtypoid;
+	TypeCacheEntry *typcache;
+	MultirangeType *result;
+	MultirangeType *current;
+	int32		range_count1;
+	int32		range_count2;
+	RangeType **ranges1;
+	RangeType **ranges2;
+
+	if (!AggCheckCallContext(fcinfo, &aggContext))
+		elog(ERROR, "multirange_intersect_agg_transfn called in non-aggregate context");
+
+	mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+	if (!type_is_multirange(mltrngtypoid))
+		ereport(ERROR, (errmsg("range_intersect_agg must be called with a multirange")));
+
+	typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+	/* strictness ensures these are non-null */
+	result = PG_GETARG_MULTIRANGE_P(0);
+	current = PG_GETARG_MULTIRANGE_P(1);
+
+	multirange_deserialize(result, &range_count1, &ranges1);
+	multirange_deserialize(current, &range_count2, &ranges2);
+
+	result = multirange_intersect_multirange_internal(mltrngtypoid,
+													  typcache->rngtype,
+													  range_count1,
+													  ranges1,
+													  range_count2,
+													  ranges2);
+	PG_RETURN_RANGE_P(result);
+}
+
+
+/* multirange -> element type functions */
+
+/* extract lower bound value */
+Datum
+multirange_lower(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+	TypeCacheEntry *typcache;
+	int32		range_count;
+	RangeType **ranges;
+	bool		isnull;
+	Datum		result;
+
+	if (MultirangeIsEmpty(mr))
+		PG_RETURN_NULL();
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+	multirange_deserialize(mr, &range_count, &ranges);
+
+	result = range_lower_internal(typcache->rngtype, ranges[0], &isnull);
+
+	if (isnull)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_DATUM(result);
+}
+
+/* extract upper bound value */
+Datum
+multirange_upper(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+	TypeCacheEntry *typcache;
+	int32		range_count;
+	RangeType **ranges;
+	bool		isnull;
+	Datum		result;
+
+	if (MultirangeIsEmpty(mr))
+		PG_RETURN_NULL();
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+	multirange_deserialize(mr, &range_count, &ranges);
+
+	result = range_upper_internal(typcache->rngtype, ranges[range_count - 1], &isnull);
+
+	if (isnull)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_DATUM(result);
+}
+
+
+/* multirange -> bool functions */
+
+/* is multirange empty? */
+Datum
+multirange_empty(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+
+	PG_RETURN_BOOL(mr->rangeCount == 0);
+}
+
+/* is lower bound inclusive? */
+Datum
+multirange_lower_inc(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+	int32		range_count;
+	RangeType **ranges;
+
+	if (MultirangeIsEmpty(mr))
+		PG_RETURN_BOOL(false);
+
+	multirange_deserialize(mr, &range_count, &ranges);
+
+	PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INC));
+}
+
+/* is upper bound inclusive? */
+Datum
+multirange_upper_inc(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+	int32		range_count;
+	RangeType **ranges;
+
+	if (MultirangeIsEmpty(mr))
+		PG_RETURN_BOOL(false);
+
+	multirange_deserialize(mr, &range_count, &ranges);
+
+	PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INC));
+}
+
+/* is lower bound infinite? */
+Datum
+multirange_lower_inf(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+	int32		range_count;
+	RangeType **ranges;
+
+	if (MultirangeIsEmpty(mr))
+		PG_RETURN_BOOL(false);
+
+	multirange_deserialize(mr, &range_count, &ranges);
+
+	PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INF));
+}
+
+/* is upper bound infinite? */
+Datum
+multirange_upper_inf(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+	int32		range_count;
+	RangeType **ranges;
+
+	if (MultirangeIsEmpty(mr))
+		PG_RETURN_BOOL(false);
+
+	multirange_deserialize(mr, &range_count, &ranges);
+
+	PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INF));
+}
+
+
+
+/* multirange, element -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_elem(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+	Datum		val = PG_GETARG_DATUM(1);
+	TypeCacheEntry *typcache;
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+	PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/* contained by? */
+Datum
+elem_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+	Datum		val = PG_GETARG_DATUM(0);
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+	TypeCacheEntry *typcache;
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+	PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/*
+ * Test whether multirange mr contains a specific element value.
+ */
+bool
+multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr, Datum val)
+{
+	TypeCacheEntry *rangetyp;
+	int32		range_count;
+	RangeType **ranges;
+	RangeType  *r;
+	int			i;
+
+	rangetyp = typcache->rngtype;
+
+	multirange_deserialize(mr, &range_count, &ranges);
+
+	for (i = 0; i < range_count; i++)
+	{
+		r = ranges[i];
+		if (range_contains_elem_internal(rangetyp, r, val))
+			return true;
+	}
+
+	return false;
+}
+
+/* multirange, range -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_range(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+	RangeType  *r = PG_GETARG_RANGE_P(1);
+	TypeCacheEntry *typcache;
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+	PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/* contained by? */
+Datum
+range_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+	RangeType  *r = PG_GETARG_RANGE_P(0);
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+	TypeCacheEntry *typcache;
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+	PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/*
+ * Test whether multirange mr contains a specific range r.
+ */
+bool
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr, RangeType *r)
+{
+	TypeCacheEntry *rangetyp;
+	int32		range_count;
+	RangeType **ranges;
+	RangeType  *mrr;
+	int			i;
+
+	rangetyp = typcache->rngtype;
+
+	/*
+	 * Every multirange contains an infinite number of empty ranges, even an
+	 * empty one.
+	 */
+	if (RangeIsEmpty(r))
+		return true;
+
+	multirange_deserialize(mr, &range_count, &ranges);
+
+	for (i = 0; i < range_count; i++)
+	{
+		mrr = ranges[i];
+		if (range_contains_internal(rangetyp, mrr, r))
+			return true;
+	}
+
+	return false;
+}
+
+
 /* multirange, multirange -> bool functions */
 
-/* equality (internal version) */
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+	int32		range_count_1;
+	int32		range_count_2;
+	int32		i;
+	RangeType **ranges1;
+	RangeType **ranges2;
+	RangeType  *r1;
+	RangeType  *r2;
+
+	/* Different types should be prevented by ANYMULTIRANGE matching rules */
+	if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+		elog(ERROR, "multirange types do not match");
+
+	multirange_deserialize(mr1, &range_count_1, &ranges1);
+	multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+	if (range_count_1 != range_count_2)
+		return false;
+
+	for (i = 0; i < range_count_1; i++)
+	{
+		r1 = ranges1[i];
+		r2 = ranges2[i];
+
+		if (!range_eq_internal(typcache->rngtype, r1, r2))
+			return false;
+	}
+
+	return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+	MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+	TypeCacheEntry *typcache;
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+	PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+	return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+	MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+	TypeCacheEntry *typcache;
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+	PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* overlaps? */
+Datum
+range_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+	RangeType  *r = PG_GETARG_RANGE_P(0);
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+	TypeCacheEntry *typcache;
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+	PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_range(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+	RangeType  *r = PG_GETARG_RANGE_P(1);
+	TypeCacheEntry *typcache;
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+	PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+	MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+	TypeCacheEntry *typcache;
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+	PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache, mr1, mr2));
+}
+
+bool
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType * mr)
+{
+	TypeCacheEntry *rangetyp;
+	int32		range_count;
+	int32		i;
+	RangeType **ranges;
+	RangeType  *mrr;
+
+	/*
+	 * Empties never overlap, even with empties. (This seems strange since
+	 * they *do* contain each other, but we want to follow how ranges work.)
+	 */
+	if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+		return false;
+
+	rangetyp = typcache->rngtype;
+
+	multirange_deserialize(mr, &range_count, &ranges);
+
+	/* Scan through mrr and once it gets to r they either overlap or not */
+	mrr = ranges[0];
+
+	/* Discard mrrs while mrr << r */
+	i = 0;
+	while (range_before_internal(rangetyp, mrr, r))
+	{
+		if (++i >= range_count)
+			return false;
+		mrr = ranges[i];
+	}
+
+	/* Now either we overlap or we passed r */
+	return range_overlaps_internal(rangetyp, mrr, r);
+}
+
+bool
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+										MultirangeType * mr2)
+{
+	TypeCacheEntry *rangetyp;
+	int32		range_count1;
+	int32		range_count2;
+	int32		i1;
+	int32		i2;
+	RangeType **ranges1;
+	RangeType **ranges2;
+	RangeType  *r1;
+	RangeType  *r2;
+
+	/*
+	 * Empties never overlap, even with empties. (This seems strange since
+	 * they *do* contain each other, but we want to follow how ranges work.)
+	 */
+	if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+		return false;
+
+	rangetyp = typcache->rngtype;
+
+	multirange_deserialize(mr1, &range_count1, &ranges1);
+	multirange_deserialize(mr2, &range_count2, &ranges2);
+
+	/*
+	 * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
+	 * we can use their ordering to avoid O(n^2). This is similar to
+	 * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
+	 * don't find an overlap with r we're done, and here if we don't find an
+	 * overlap with r2 we try the next r2.
+	 */
+	r1 = ranges1[0];
+	for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+	{
+		r2 = ranges2[i2];
+
+		/* Discard r1s while r1 << r2 */
+		while (range_before_internal(rangetyp, r1, r2))
+		{
+			if (++i1 >= range_count1)
+				return false;
+			r1 = ranges1[i1];
+		}
+
+		/*
+		 * If r1 && r2, we're done, otherwise we failed to find an overlap for
+		 * r2, so go to the next one.
+		 */
+		if (range_overlaps_internal(rangetyp, r1, r2))
+			return true;
+	}
+
+	/* We looked through all of mr2 without finding an overlap */
+	return false;
+}
+
+/* does not extend to right of? */
+Datum
+range_overleft_multirange(PG_FUNCTION_ARGS)
+{
+	RangeType  *r = PG_GETARG_RANGE_P(0);
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+	TypeCacheEntry *typcache;
+	int32		range_count;
+	RangeType **ranges;
+
+	if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+		PG_RETURN_BOOL(false);
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+	multirange_deserialize(mr, &range_count, &ranges);
+
+	PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, r, ranges[range_count - 1]));
+}
+
+Datum
+multirange_overleft_range(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+	RangeType  *r = PG_GETARG_RANGE_P(1);
+	TypeCacheEntry *typcache;
+	int32		range_count;
+	RangeType **ranges;
+
+	if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+		PG_RETURN_BOOL(false);
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+	multirange_deserialize(mr, &range_count, &ranges);
+
+	PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_overleft_multirange(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+	MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+	TypeCacheEntry *typcache;
+	int32		range_count1;
+	int32		range_count2;
+	RangeType **ranges1;
+	RangeType **ranges2;
+
+	if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+		PG_RETURN_BOOL(false);
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+	multirange_deserialize(mr1, &range_count1, &ranges1);
+	multirange_deserialize(mr2, &range_count2, &ranges2);
+
+	PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[range_count2 - 1]));
+}
+
+/* does not extend to left of? */
+Datum
+range_overright_multirange(PG_FUNCTION_ARGS)
+{
+	RangeType  *r = PG_GETARG_RANGE_P(0);
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+	TypeCacheEntry *typcache;
+	int32		range_count;
+	RangeType **ranges;
+
+	if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+		PG_RETURN_BOOL(false);
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+	multirange_deserialize(mr, &range_count, &ranges);
+
+	PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_overright_range(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+	RangeType  *r = PG_GETARG_RANGE_P(1);
+	TypeCacheEntry *typcache;
+	int32		range_count;
+	RangeType **ranges;
+
+	if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+		PG_RETURN_BOOL(false);
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+	multirange_deserialize(mr, &range_count, &ranges);
+
+	PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges[0], r));
+}
+
+Datum
+multirange_overright_multirange(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+	MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+	TypeCacheEntry *typcache;
+	int32		range_count1;
+	int32		range_count2;
+	RangeType **ranges1;
+	RangeType **ranges2;
+
+	if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+		PG_RETURN_BOOL(false);
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+	multirange_deserialize(mr1, &range_count1, &ranges1);
+	multirange_deserialize(mr2, &range_count2, &ranges2);
+
+	PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges1[0], ranges2[0]));
+}
+
+/* contains? */
+Datum
+multirange_contains_multirange(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+	MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+	TypeCacheEntry *typcache;
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+	PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr1, mr2));
+}
+
+/* contained by? */
+Datum
+multirange_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+	MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+	TypeCacheEntry *typcache;
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+	PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr2, mr1));
+}
+
+/*
+ * Test whether multirange mr1 contains every range from another multirange mr2.
+ */
 bool
-multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+										MultirangeType * mr1, MultirangeType * mr2)
 {
-	int32		range_count_1;
-	int32		range_count_2;
-	int32		i;
+	TypeCacheEntry *rangetyp;
+	int32		range_count1;
+	int32		range_count2;
 	RangeType **ranges1;
 	RangeType **ranges2;
 	RangeType  *r1;
 	RangeType  *r2;
+	int			i1,
+				i2;
 
-	/* Different types should be prevented by ANYMULTIRANGE matching rules */
-	if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
-		elog(ERROR, "multirange types do not match");
+	rangetyp = typcache->rngtype;
 
-	multirange_deserialize(mr1, &range_count_1, &ranges1);
-	multirange_deserialize(mr2, &range_count_2, &ranges2);
+	multirange_deserialize(mr1, &range_count1, &ranges1);
+	multirange_deserialize(mr2, &range_count2, &ranges2);
 
-	if (range_count_1 != range_count_2)
+	/*
+	 * We follow the same logic for empties as ranges: - an empty multirange
+	 * contains an empty range/multirange. - an empty multirange can't contain
+	 * any other range/multirange. - an empty multirange is contained by any
+	 * other range/multirange.
+	 */
+
+	if (range_count2 == 0)
+		return true;
+	if (range_count1 == 0)
 		return false;
 
-	for (i = 0; i < range_count_1; i++)
+	/*
+	 * Every range in mr2 must be contained by some range in mr1. To avoid
+	 * O(n^2) we walk through both ranges in tandem.
+	 */
+	r1 = ranges1[0];
+	for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
 	{
-		r1 = ranges1[i];
-		r2 = ranges2[i];
+		r2 = ranges2[i2];
 
-		if (!range_eq_internal(typcache->rngtype, r1, r2))
+		/* Discard r1s while r1 << r2 */
+		while (range_before_internal(rangetyp, r1, r2))
+		{
+			if (++i1 >= range_count1)
+				return false;
+			r1 = ranges1[i1];
+		}
+
+		/*
+		 * If r1 @> r2, go to the next r2, otherwise return false (since every
+		 * r1[n] and r1[n+1] must have a gap). Note this will give weird
+		 * answers if you don't canonicalize, e.g. with a custom
+		 * int2multirange {[1,1], [2,2]} there is a "gap". But that is
+		 * consistent with other range operators, e.g. '[1,1]'::int2range -|-
+		 * '[2,2]'::int2range is false.
+		 */
+		if (!range_contains_internal(rangetyp, r1, r2))
 			return false;
 	}
 
+	/* All ranges in mr2 are satisfied */
 	return true;
 }
 
-/* equality */
+/* strictly left of? */
 Datum
-multirange_eq(PG_FUNCTION_ARGS)
+range_before_multirange(PG_FUNCTION_ARGS)
+{
+	RangeType  *r = PG_GETARG_RANGE_P(0);
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+	TypeCacheEntry *typcache;
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+	PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_range(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+	RangeType  *r = PG_GETARG_RANGE_P(1);
+	TypeCacheEntry *typcache;
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+	PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_multirange(PG_FUNCTION_ARGS)
 {
 	MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
 	MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
@@ -772,27 +1994,156 @@ multirange_eq(PG_FUNCTION_ARGS)
 
 	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
 
-	PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+	PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr1, mr2));
 }
 
-/* inequality (internal version) */
+/* strictly right of? */
+Datum
+range_after_multirange(PG_FUNCTION_ARGS)
+{
+	RangeType  *r = PG_GETARG_RANGE_P(0);
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+	TypeCacheEntry *typcache;
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+	PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_range(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+	RangeType  *r = PG_GETARG_RANGE_P(1);
+	TypeCacheEntry *typcache;
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+	PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_multirange(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+	MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+	TypeCacheEntry *typcache;
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+	PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr2, mr1));
+}
+
+/* strictly left of? (internal version) */
 bool
-multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+								 MultirangeType * mr)
 {
-	return (!multirange_eq_internal(typcache, mr1, mr2));
+	if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+		return false;
+
+	int32		range_count;
+	RangeType **ranges;
+
+	multirange_deserialize(mr, &range_count, &ranges);
+
+	return range_before_internal(typcache->rngtype, r, ranges[0]);
 }
 
-/* inequality */
+bool
+multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+									  MultirangeType * mr2)
+{
+	if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+		return false;
+
+	int32		range_count1;
+	int32		range_count2;
+	RangeType **ranges1;
+	RangeType **ranges2;
+
+	multirange_deserialize(mr1, &range_count1, &ranges1);
+	multirange_deserialize(mr2, &range_count2, &ranges2);
+
+	return range_before_internal(typcache->rngtype, ranges1[range_count1 - 1],
+								 ranges2[0]);
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+								MultirangeType * mr)
+{
+	if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+		return false;
+
+	int32		range_count;
+	RangeType **ranges;
+
+	multirange_deserialize(mr, &range_count, &ranges);
+
+	return range_after_internal(typcache->rngtype, r, ranges[range_count - 1]);
+}
+
+/* adjacent to? */
 Datum
-multirange_ne(PG_FUNCTION_ARGS)
+range_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+	RangeType  *r = PG_GETARG_RANGE_P(0);
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+	TypeCacheEntry *typcache;
+	int32		range_count;
+	RangeType **ranges;
+
+	if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+		return false;
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+	multirange_deserialize(mr, &range_count, &ranges);
+
+	PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_adjacent_range(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+	RangeType  *r = PG_GETARG_RANGE_P(1);
+	TypeCacheEntry *typcache;
+	int32		range_count;
+	RangeType **ranges;
+
+	if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+		return false;
+
+	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+	multirange_deserialize(mr, &range_count, &ranges);
+
+	PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_adjacent_multirange(PG_FUNCTION_ARGS)
 {
 	MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
 	MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
 	TypeCacheEntry *typcache;
+	int32		range_count1;
+	int32		range_count2;
+	RangeType **ranges1;
+	RangeType **ranges2;
+
+	if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+		return false;
 
 	typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
 
-	PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+	multirange_deserialize(mr1, &range_count1, &ranges1);
+	multirange_deserialize(mr2, &range_count2, &ranges2);
+
+	PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[0]));
 }
 
 /* Btree support */
@@ -891,6 +2242,29 @@ multirange_gt(PG_FUNCTION_ARGS)
 	PG_RETURN_BOOL(cmp > 0);
 }
 
+/* multirange -> range functions */
+
+/* Find the smallest range that includes everything in the multirange */
+Datum
+range_merge_from_multirange(PG_FUNCTION_ARGS)
+{
+	MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+	Oid			mltrngtypoid = MultirangeTypeGetOid(mr);
+	TypeCacheEntry *typcache;
+	int32		range_count;
+	RangeType **ranges;
+
+	typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+	if (MultirangeIsEmpty(mr))
+		PG_RETURN_RANGE_P(make_empty_range(typcache->rngtype));
+
+	multirange_deserialize(mr, &range_count, &ranges);
+
+	PG_RETURN_RANGE_P(range_union_internal(typcache->rngtype, ranges[0],
+										   ranges[range_count - 1], false));
+}
+
 /* Hash support */
 
 /* hash a multirange value */
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 1feebc48dc..bd9ad67672 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -429,19 +429,37 @@ range_lower(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE_P(0);
 	TypeCacheEntry *typcache;
+	bool		isnull;
+	Datum		result;
+
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+	result = range_lower_internal(typcache, r1, &isnull);
+
+	if (isnull)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_DATUM(result);
+}
+
+Datum
+range_lower_internal(TypeCacheEntry *typcache, RangeType *r1, bool *isnull)
+{
 	RangeBound	lower;
 	RangeBound	upper;
 	bool		empty;
 
-	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
 	range_deserialize(typcache, r1, &lower, &upper, &empty);
 
 	/* Return NULL if there's no finite lower bound */
 	if (empty || lower.infinite)
-		PG_RETURN_NULL();
+	{
+		*isnull = true;
+		return 0;
+	}
 
-	PG_RETURN_DATUM(lower.val);
+	*isnull = false;
+	return lower.val;
 }
 
 /* extract upper bound value */
@@ -450,19 +468,37 @@ range_upper(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE_P(0);
 	TypeCacheEntry *typcache;
+	bool		isnull;
+	Datum		result;
+
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+	result = range_upper_internal(typcache, r1, &isnull);
+
+	if (isnull)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_DATUM(result);
+}
+
+Datum
+range_upper_internal(TypeCacheEntry *typcache, RangeType *r1, bool *isnull)
+{
 	RangeBound	lower;
 	RangeBound	upper;
 	bool		empty;
 
-	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
 	range_deserialize(typcache, r1, &lower, &upper, &empty);
 
 	/* Return NULL if there's no finite upper bound */
 	if (empty || upper.infinite)
-		PG_RETURN_NULL();
+	{
+		*isnull = true;
+		return 0;
+	}
 
-	PG_RETURN_DATUM(upper.val);
+	*isnull = false;
+	return upper.val;
 }
 
 
@@ -473,9 +509,8 @@ Datum
 range_empty(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE_P(0);
-	char		flags = range_get_flags(r1);
 
-	PG_RETURN_BOOL(flags & RANGE_EMPTY);
+	PG_RETURN_BOOL(range_has_flag(r1, RANGE_EMPTY));
 }
 
 /* is lower bound inclusive? */
@@ -483,9 +518,8 @@ Datum
 range_lower_inc(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE_P(0);
-	char		flags = range_get_flags(r1);
 
-	PG_RETURN_BOOL(flags & RANGE_LB_INC);
+	PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INC));
 }
 
 /* is upper bound inclusive? */
@@ -493,9 +527,8 @@ Datum
 range_upper_inc(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE_P(0);
-	char		flags = range_get_flags(r1);
 
-	PG_RETURN_BOOL(flags & RANGE_UB_INC);
+	PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INC));
 }
 
 /* is lower bound infinite? */
@@ -503,9 +536,8 @@ Datum
 range_lower_inf(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE_P(0);
-	char		flags = range_get_flags(r1);
 
-	PG_RETURN_BOOL(flags & RANGE_LB_INF);
+	PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INF));
 }
 
 /* is upper bound infinite? */
@@ -513,9 +545,8 @@ Datum
 range_upper_inf(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE_P(0);
-	char		flags = range_get_flags(r1);
 
-	PG_RETURN_BOOL(flags & RANGE_UB_INF);
+	PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INF));
 }
 
 
@@ -955,7 +986,25 @@ range_minus(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE_P(0);
 	RangeType  *r2 = PG_GETARG_RANGE_P(1);
+	RangeType  *ret;
 	TypeCacheEntry *typcache;
+
+	/* Different types should be prevented by ANYRANGE matching rules */
+	if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+		elog(ERROR, "range types do not match");
+
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+	ret = range_minus_internal(typcache, r1, r2);
+	if (ret)
+		PG_RETURN_RANGE_P(ret);
+	else
+		PG_RETURN_NULL();
+}
+
+RangeType *
+range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
 	RangeBound	lower1,
 				lower2;
 	RangeBound	upper1,
@@ -967,18 +1016,12 @@ range_minus(PG_FUNCTION_ARGS)
 				cmp_u1l2,
 				cmp_u1u2;
 
-	/* Different types should be prevented by ANYRANGE matching rules */
-	if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
-		elog(ERROR, "range types do not match");
-
-	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
 	range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
 	range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
 
 	/* if either is empty, r1 is the correct answer */
 	if (empty1 || empty2)
-		PG_RETURN_RANGE_P(r1);
+		return r1;
 
 	cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
 	cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
@@ -991,34 +1034,34 @@ range_minus(PG_FUNCTION_ARGS)
 				 errmsg("result of range difference would not be contiguous")));
 
 	if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
-		PG_RETURN_RANGE_P(r1);
+		return r1;
 
 	if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
-		PG_RETURN_RANGE_P(make_empty_range(typcache));
+		return make_empty_range(typcache);
 
 	if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
 	{
 		lower2.inclusive = !lower2.inclusive;
 		lower2.lower = false;	/* it will become the upper bound */
-		PG_RETURN_RANGE_P(make_range(typcache, &lower1, &lower2, false));
+		return make_range(typcache, &lower1, &lower2, false);
 	}
 
 	if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
 	{
 		upper2.inclusive = !upper2.inclusive;
 		upper2.lower = true;	/* it will become the lower bound */
-		PG_RETURN_RANGE_P(make_range(typcache, &upper2, &upper1, false));
+		return make_range(typcache, &upper2, &upper1, false);
 	}
 
 	elog(ERROR, "unexpected case in range_minus");
-	PG_RETURN_NULL();
+	return NULL;
 }
 
 /*
  * Set union.  If strict is true, it is an error that the two input ranges
  * are not adjacent or overlapping.
  */
-static RangeType *
+RangeType *
 range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
 					 bool strict)
 {
@@ -1099,6 +1142,19 @@ range_intersect(PG_FUNCTION_ARGS)
 	RangeType  *r1 = PG_GETARG_RANGE_P(0);
 	RangeType  *r2 = PG_GETARG_RANGE_P(1);
 	TypeCacheEntry *typcache;
+
+	/* Different types should be prevented by ANYRANGE matching rules */
+	if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+		elog(ERROR, "range types do not match");
+
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+	PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
+}
+
+RangeType *
+range_intersect_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
 	RangeBound	lower1,
 				lower2;
 	RangeBound	upper1,
@@ -1108,17 +1164,11 @@ range_intersect(PG_FUNCTION_ARGS)
 	RangeBound *result_lower;
 	RangeBound *result_upper;
 
-	/* Different types should be prevented by ANYRANGE matching rules */
-	if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
-		elog(ERROR, "range types do not match");
-
-	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
 	range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
 	range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
 
-	if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
-		PG_RETURN_RANGE_P(make_empty_range(typcache));
+	if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
+		return make_empty_range(typcache);
 
 	if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
 		result_lower = &lower1;
@@ -1130,54 +1180,83 @@ range_intersect(PG_FUNCTION_ARGS)
 	else
 		result_upper = &upper2;
 
-	PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
+	return make_range(typcache, result_lower, result_upper, false);
 }
 
-/* Btree support */
+/* range, range -> range, range functions */
 
-/* btree comparator */
-Datum
-range_cmp(PG_FUNCTION_ARGS)
+/*
+ * range_split_internal - if r2 intersects the middle of r1, leaving non-empty
+ * ranges on both sides, then return true and set output1 and output2 to the
+ * results of r1 - r2 (in order). Otherwise return false and don't set output1
+ * or output2. Neither input range should be empty.
+ */
+bool
+range_split_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
+					 RangeType **output1, RangeType **output2)
 {
-	RangeType  *r1 = PG_GETARG_RANGE_P(0);
-	RangeType  *r2 = PG_GETARG_RANGE_P(1);
-	TypeCacheEntry *typcache;
 	RangeBound	lower1,
 				lower2;
 	RangeBound	upper1,
 				upper2;
 	bool		empty1,
 				empty2;
-	int			cmp;
-
-	check_stack_depth();		/* recurses when subtype is a range type */
-
-	/* Different types should be prevented by ANYRANGE matching rules */
-	if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
-		elog(ERROR, "range types do not match");
-
-	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
 
 	range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
 	range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
 
-	/* For b-tree use, empty ranges sort before all else */
-	if (empty1 && empty2)
-		cmp = 0;
-	else if (empty1)
-		cmp = -1;
-	else if (empty2)
-		cmp = 1;
-	else
+	if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
+		range_cmp_bounds(typcache, &upper1, &upper2) > 0)
 	{
-		cmp = range_cmp_bounds(typcache, &lower1, &lower2);
-		if (cmp == 0)
-			cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+		/*
+		 * Need to invert inclusive/exclusive for the lower2 and upper2
+		 * points. They can't be infinite though. We're allowed to overwrite
+		 * these RangeBounds since they only exist locally.
+		 */
+		lower2.inclusive = !lower2.inclusive;
+		lower2.lower = false;
+		upper2.inclusive = !upper2.inclusive;
+		upper2.lower = true;
+
+		*output1 = make_range(typcache, &lower1, &lower2, false);
+		*output2 = make_range(typcache, &upper2, &upper1, false);
+		return true;
 	}
 
-	return cmp;
+	return false;
+}
+
+/* range -> range aggregate functions */
+
+Datum
+range_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+	MemoryContext aggContext;
+	Oid			rngtypoid;
+	TypeCacheEntry *typcache;
+	RangeType  *result;
+	RangeType  *current;
+
+	if (!AggCheckCallContext(fcinfo, &aggContext))
+		elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
+
+	rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+	if (!type_is_range(rngtypoid))
+		ereport(ERROR, (errmsg("range_intersect_agg must be called with a range")));
+
+	typcache = range_get_typcache(fcinfo, rngtypoid);
+
+	/* strictness ensures these are non-null */
+	result = PG_GETARG_RANGE_P(0);
+	current = PG_GETARG_RANGE_P(1);
+
+	result = range_intersect_internal(typcache, result, current);
+	PG_RETURN_RANGE_P(result);
 }
 
+
+/* Btree support */
+
 /* btree comparator */
 Datum
 range_cmp(PG_FUNCTION_ARGS)
@@ -1838,6 +1917,21 @@ range_get_flags(RangeType *range)
 }
 
 /*
+ * range_has_flag: set whether a range has a specific flag.
+ *
+ * This lets expose some of our functions that just check flags
+ * to the rest of the code base (like to multiranges)
+ * without writing full-fledged *_internal versions.
+ */
+bool
+range_has_flag(RangeType *r1, char flag)
+{
+	char		flags = range_get_flags(r1);
+
+	return flags & flag;
+}
+
+/*
  * range_set_contain_empty: set the RANGE_CONTAIN_EMPTY bit in the value.
  *
  * This is only needed in GiST operations, so we don't include a provision
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index 242d843347..07a7211c15 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -538,6 +538,17 @@
   aggtransfn => 'bytea_string_agg_transfn',
   aggfinalfn => 'bytea_string_agg_finalfn', aggtranstype => 'internal' },
 
+# range
+{ aggfnoid => 'range_intersect_agg(anyrange)',
+  aggtransfn => 'range_intersect_agg_transfn',
+  aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' },
+{ aggfnoid => 'range_intersect_agg(anymultirange)',
+  aggtransfn => 'multirange_intersect_agg_transfn',
+  aggcombinefn => 'multirange_intersect_agg_transfn', aggtranstype => 'anymultirange' },
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+  aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+  aggtranstype => 'internal' },
+
 # json
 { aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
   aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index b7cb2cb000..32965d6b3d 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -1210,12 +1210,13 @@
   amprocrighttype => 'anyrange', amprocnum => '4',
   amproc => 'brin_inclusion_union' },
 { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
-  amprocrighttype => 'anyrange', amprocnum => '11', amproc => 'range_merge' },
+  amprocrighttype => 'anyrange', amprocnum => '11',
+  amproc => 'range_merge(anyrange,anyrange)' },
 { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
   amprocrighttype => 'anyrange', amprocnum => '13',
   amproc => 'range_contains' },
 { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
-  amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty' },
+  amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty(anyrange)' },
 
 # minmax pg_lsn
 { amprocfamily => 'brin/pg_lsn_minmax_ops', amproclefttype => 'pg_lsn',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 4d319bb5b3..ade223b6f3 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3322,5 +3322,174 @@
   oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
   oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
   oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '8073', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
+  descr => 'overlaps',
+  oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
+  oprcode => 'range_overlaps_multirange', oprrest => 'areasel',
+  oprjoin => 'areajoinsel' },
+{ oid => '8074', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
+  oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
+  oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
+  oprcode => 'multirange_overlaps_range', oprrest => 'areasel',
+  oprjoin => 'areajoinsel' },
+{ oid => '8075', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
+  oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
+  oprcode => 'multirange_overlaps_multirange', oprrest => 'areasel',
+  oprjoin => 'areajoinsel' },
+{ oid => '8064', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
+  descr => 'contains',
+  oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
+  oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
+  oprcode => 'multirange_contains_elem', oprrest => 'contsel',
+  oprjoin => 'contjoinsel' },
+{ oid => '8065', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
+  oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
+  oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
+  oprcode => 'multirange_contains_range', oprrest => 'contsel',
+  oprjoin => 'contjoinsel' },
+{ oid => '8066', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
+  oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
+  oprcode => 'multirange_contains_multirange', oprrest => 'contsel',
+  oprjoin => 'contjoinsel' },
+{ oid => '8067', oid_symbol => 'OID_MULTIRANGE_ELEM_CONTAINED_OP',
+  descr => 'is contained by',
+  oprname => '<@', oprleft => 'anyelement', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '@>(anymultirange,anyelement)',
+  oprcode => 'elem_contained_by_multirange', oprrest => 'contsel',
+  oprjoin => 'contjoinsel' },
+{ oid => '8068', oid_symbol => 'OID_MULTIRANGE_RANGE_CONTAINED_OP',
+  descr => 'is contained by',
+  oprname => '<@', oprleft => 'anyrange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '@>(anymultirange,anyrange)',
+  oprcode => 'range_contained_by_multirange', oprrest => 'contsel',
+  oprjoin => 'contjoinsel' },
+{ oid => '8069', oid_symbol => 'OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP',
+  descr => 'is contained by',
+  oprname => '<@', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)',
+  oprcode => 'multirange_contained_by_multirange', oprrest => 'contsel',
+  oprjoin => 'contjoinsel' },
+{ oid => '8106', oid_symbol => 'OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+  descr => 'overlaps or is left of',
+  oprname => '&<', oprleft => 'anyrange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcode => 'range_overleft_multirange', oprrest => 'scalarltsel',
+  oprjoin => 'scalarltjoinsel' },
+{ oid => '8107', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP',
+  descr => 'overlaps or is left of',
+  oprname => '&<', oprleft => 'anymultirange', oprright => 'anyrange',
+  oprresult => 'bool', oprcode => 'multirange_overleft_range', oprrest => 'scalarltsel',
+  oprjoin => 'scalarltjoinsel' },
+{ oid => '8108', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+  descr => 'overlaps or is left of',
+  oprname => '&<', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcode => 'multirange_overleft_multirange', oprrest => 'scalarltsel',
+  oprjoin => 'scalarltjoinsel' },
+{ oid => '8109', oid_symbol => 'OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+  descr => 'overlaps or is right of',
+  oprname => '&>', oprleft => 'anyrange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcode => 'range_overright_multirange', oprrest => 'scalargtsel',
+  oprjoin => 'scalargtjoinsel' },
+{ oid => '8110', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP',
+  descr => 'overlaps or is right of',
+  oprname => '&>', oprleft => 'anymultirange', oprright => 'anyrange',
+  oprresult => 'bool', oprcode => 'multirange_overright_range', oprrest => 'scalargtsel',
+  oprjoin => 'scalargtjoinsel' },
+{ oid => '8111', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+  descr => 'overlaps or is right of',
+  oprname => '&>', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcode => 'multirange_overright_multirange', oprrest => 'scalargtsel',
+  oprjoin => 'scalargtjoinsel' },
+{ oid => '8091', oid_symbol => 'OID_RANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+  oprname => '-|-', oprleft => 'anyrange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '-|-(anymultirange,anyrange)',
+  oprcode => 'range_adjacent_multirange', oprrest => 'scalarltsel',
+  oprjoin => 'scalarltjoinsel' },
+{ oid => '8092', oid_symbol => 'OID_MULTIRANGE_ADJACENT_RANGE_OP', descr => 'is adjacent to',
+  oprname => '-|-', oprleft => 'anymultirange', oprright => 'anyrange',
+  oprresult => 'bool', oprcom => '-|-(anyrange,anymultirange)',
+  oprcode => 'multirange_adjacent_range', oprrest => 'scalarltsel',
+  oprjoin => 'scalarltjoinsel' },
+{ oid => '8093', oid_symbol => 'OID_MULTIRANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+  oprname => '-|-', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '-|-(anymultirange,anymultirange)',
+  oprcode => 'multirange_adjacent_multirange', oprrest => 'scalarltsel',
+  oprjoin => 'scalarltjoinsel' },
+{ oid => '8138', descr => 'multirange union',
+  oprname => '@+', oprleft => 'anyrange', oprright => 'anyrange',
+  oprresult => 'anymultirange', oprcom => '@+(anyrange,anyrange)',
+  oprcode => 'range_union_range' },
+{ oid => '8115', descr => 'multirange union',
+  oprname => '@+', oprleft => 'anyrange', oprright => 'anymultirange',
+  oprresult => 'anymultirange', oprcom => '@+(anymultirange,anyrange)',
+  oprcode => 'range_union_multirange' },
+{ oid => '8116', descr => 'multirange union',
+  oprname => '@+', oprleft => 'anymultirange', oprright => 'anyrange',
+  oprresult => 'anymultirange', oprcom => '@+(anyrange,anymultirange)',
+  oprcode => 'multirange_union_range' },
+{ oid => '8117', descr => 'multirange union',
+  oprname => '@+', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'anymultirange', oprcom => '@+(anymultirange,anymultirange)',
+  oprcode => 'multirange_union_multirange' },
+{ oid => '8140', descr => 'multirange minus',
+  oprname => '@-', oprleft => 'anyrange', oprright => 'anyrange',
+  oprresult => 'anymultirange', oprcode => 'range_minus_range' },
+{ oid => '8121', descr => 'multirange minus',
+  oprname => '@-', oprleft => 'anyrange', oprright => 'anymultirange',
+  oprresult => 'anymultirange', oprcode => 'range_minus_multirange' },
+{ oid => '8122', descr => 'multirange minus',
+  oprname => '@-', oprleft => 'anymultirange', oprright => 'anyrange',
+  oprresult => 'anymultirange', oprcode => 'multirange_minus_range' },
+{ oid => '8123', descr => 'multirange minus',
+  oprname => '@-', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'anymultirange', oprcode => 'multirange_minus_multirange' },
+{ oid => '8142', descr => 'multirange intersect',
+  oprname => '@*', oprleft => 'anyrange', oprright => 'anyrange',
+  oprresult => 'anymultirange', oprcom => '@*(anyrange,anyrange)',
+  oprcode => 'range_intersect_range' },
+{ oid => '8127', descr => 'multirange intersect',
+  oprname => '@*', oprleft => 'anyrange', oprright => 'anymultirange',
+  oprresult => 'anymultirange', oprcom => '@*(anymultirange,anyrange)',
+  oprcode => 'range_intersect_multirange' },
+{ oid => '8128', descr => 'multirange intersect',
+  oprname => '@*', oprleft => 'anymultirange', oprright => 'anyrange',
+  oprresult => 'anymultirange', oprcom => '@*(anyrange,anymultirange)',
+  oprcode => 'multirange_intersect_range' },
+{ oid => '8129', descr => 'multirange intersect',
+  oprname => '@*', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'anymultirange', oprcom => '@*(anymultirange,anymultirange)',
+  oprcode => 'multirange_intersect_multirange' },
+{ oid => '8082', oid_symbol => 'OID_RANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+  oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
+  oprcode => 'range_before_multirange', oprrest => 'scalarltsel',
+  oprjoin => 'scalarltjoinsel' },
+{ oid => '8083', oid_symbol => 'OID_MULTIRANGE_BEFORE_RANGE_OP', descr => 'is left of',
+  oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
+  oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
+  oprcode => 'multirange_before_range', oprrest => 'scalarltsel',
+  oprjoin => 'scalarltjoinsel' },
+{ oid => '8084', oid_symbol => 'OID_MULTIRANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+  oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
+  oprcode => 'multirange_before_multirange', oprrest => 'scalarltsel',
+  oprjoin => 'scalarltjoinsel' },
+{ oid => '8085', oid_symbol => 'OID_RANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+  oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
+  oprcode => 'range_after_multirange', oprrest => 'scalargtsel',
+  oprjoin => 'scalargtjoinsel' },
+{ oid => '8086', oid_symbol => 'OID_MULTIRANGE_AFTER_RANGE_OP', descr => 'is right of',
+  oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
+  oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
+  oprcode => 'multirange_after_range', oprrest => 'scalargtsel',
+  oprjoin => 'scalargtjoinsel' },
+{ oid => '8087', oid_symbol => 'OID_MULTIRANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+  oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
+  oprcode => 'multirange_after_multirange', oprrest => 'scalargtsel',
+  oprjoin => 'scalargtjoinsel' },
 
 ]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index db1bef821f..274a25027f 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9585,6 +9585,10 @@
   descr => 'the smallest range which includes both of the given ranges',
   proname => 'range_merge', prorettype => 'anyrange',
   proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
+{ oid => '8143',
+  descr => 'the smallest range which includes the whole multirange',
+  proname => 'range_merge', prorettype => 'anyrange',
+  proargtypes => 'anymultirange', prosrc => 'range_merge_from_multirange' },
 { oid => '3868',
   proname => 'range_intersect', prorettype => 'anyrange',
   proargtypes => 'anyrange anyrange', prosrc => 'range_intersect' },
@@ -9634,6 +9638,13 @@
 { oid => '3169', descr => 'restriction selectivity for range operators',
   proname => 'rangesel', provolatile => 's', prorettype => 'float8',
   proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
+{ oid => '8133', descr => 'range aggregate by intersecting',
+  proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
+  proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
+{ oid => '8134', descr => 'range aggregate by intersecting',
+  proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+  prorettype => 'anyrange', proargtypes => 'anyrange',
+  prosrc => 'aggregate_dummy'},
 
 { oid => '3914', descr => 'convert an int4 range to canonical form',
   proname => 'int4range_canonical', prorettype => 'int4range',
@@ -9721,9 +9732,165 @@
 { oid => '8007', descr => 'I/O',
   proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
   proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8094', descr => 'lower bound of multirange',
+  proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
+  prosrc => 'multirange_lower' },
+{ oid => '8095', descr => 'upper bound of multirange',
+  proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
+  prosrc => 'multirange_upper' },
+{ oid => '8057', descr => 'is the multirange empty?',
+  proname => 'isempty', prorettype => 'bool',
+  proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
+{ oid => '8096', descr => 'is the multirange\'s lower bound inclusive?',
+  proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+  prosrc => 'multirange_lower_inc' },
+{ oid => '8097', descr => 'is the multirange\'s upper bound inclusive?',
+  proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+  prosrc => 'multirange_upper_inc' },
+{ oid => '8098', descr => 'is the multirange\'s lower bound infinite?',
+  proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+  prosrc => 'multirange_lower_inf' },
+{ oid => '8099', descr => 'is the multirange\'s upper bound infinite?',
+  proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+  prosrc => 'multirange_upper_inf' },
 { oid => '8008', descr => 'multirange typanalyze',
   proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
   proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+{ oid => '8033',
+  proname => 'multirange_eq', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
+{ oid => '8034',
+  proname => 'multirange_ne', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
+{ oid => '8070',
+  proname => 'range_overlaps_multirange', prorettype => 'bool',
+  proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
+{ oid => '8071',
+  proname => 'multirange_overlaps_range', prorettype => 'bool',
+  proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
+{ oid => '8072',
+  proname => 'multirange_overlaps_multirange', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
+{ oid => '8058',
+  proname => 'multirange_contains_elem', prorettype => 'bool',
+  proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
+{ oid => '8059',
+  proname => 'multirange_contains_range', prorettype => 'bool',
+  proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
+{ oid => '8060',
+  proname => 'multirange_contains_multirange', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
+{ oid => '8061',
+  proname => 'elem_contained_by_multirange', prorettype => 'bool',
+  proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
+{ oid => '8062',
+  proname => 'range_contained_by_multirange', prorettype => 'bool',
+  proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
+{ oid => '8063',
+  proname => 'multirange_contained_by_multirange', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
+{ oid => '8088',
+  proname => 'range_adjacent_multirange', prorettype => 'bool',
+  proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
+{ oid => '8089',
+  proname => 'multirange_adjacent_multirange', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
+{ oid => '8090',
+  proname => 'multirange_adjacent_range', prorettype => 'bool',
+  proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
+{ oid => '8076',
+  proname => 'range_before_multirange', prorettype => 'bool',
+  proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
+{ oid => '8077',
+  proname => 'multirange_before_range', prorettype => 'bool',
+  proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
+{ oid => '8078',
+  proname => 'multirange_before_multirange', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
+{ oid => '8079',
+  proname => 'range_after_multirange', prorettype => 'bool',
+  proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
+{ oid => '8080',
+  proname => 'multirange_after_range', prorettype => 'bool',
+  proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
+{ oid => '8081',
+  proname => 'multirange_after_multirange', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
+{ oid => '8100',
+  proname => 'range_overleft_multirange', prorettype => 'bool',
+  proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
+{ oid => '8101',
+  proname => 'multirange_overleft_range', prorettype => 'bool',
+  proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
+{ oid => '8102',
+  proname => 'multirange_overleft_multirange', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
+{ oid => '8103',
+  proname => 'range_overright_multirange', prorettype => 'bool',
+  proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
+{ oid => '8104',
+  proname => 'multirange_overright_range', prorettype => 'bool',
+  proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
+{ oid => '8105',
+  proname => 'multirange_overright_multirange', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
+{ oid => '8137',
+  proname => 'range_union_range', prorettype => 'anymultirange',
+  proargtypes => 'anyrange anyrange', prosrc => 'range_union_range' },
+{ oid => '8112',
+  proname => 'range_union_multirange', prorettype => 'anymultirange',
+  proargtypes => 'anyrange anymultirange', prosrc => 'range_union_multirange' },
+{ oid => '8113',
+  proname => 'multirange_union_range', prorettype => 'anymultirange',
+  proargtypes => 'anymultirange anyrange', prosrc => 'multirange_union_range' },
+{ oid => '8114',
+  proname => 'multirange_union_multirange', prorettype => 'anymultirange',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union_multirange' },
+{ oid => '8139',
+  proname => 'range_minus_range', prorettype => 'anymultirange',
+  proargtypes => 'anyrange anyrange', prosrc => 'range_minus_range' },
+{ oid => '8118',
+  proname => 'range_minus_multirange', prorettype => 'anymultirange',
+  proargtypes => 'anyrange anymultirange', prosrc => 'range_minus_multirange' },
+{ oid => '8119',
+  proname => 'multirange_minus_range', prorettype => 'anymultirange',
+  proargtypes => 'anymultirange anyrange', prosrc => 'multirange_minus_range' },
+{ oid => '8120',
+  proname => 'multirange_minus_multirange', prorettype => 'anymultirange',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus_multirange' },
+{ oid => '8141',
+  proname => 'range_intersect_range', prorettype => 'anymultirange',
+  proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_range' },
+{ oid => '8124',
+  proname => 'range_intersect_multirange', prorettype => 'anymultirange',
+  proargtypes => 'anyrange anymultirange', prosrc => 'range_intersect_multirange' },
+{ oid => '8125',
+  proname => 'multirange_intersect_range', prorettype => 'anymultirange',
+  proargtypes => 'anymultirange anyrange', prosrc => 'multirange_intersect_range' },
+{ oid => '8126',
+  proname => 'multirange_intersect_multirange', prorettype => 'anymultirange',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_multirange' },
+{ oid => '8022', descr => 'less-equal-greater',
+  proname => 'multirange_cmp', prorettype => 'int4',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
+{ oid => '8023',
+  proname => 'multirange_lt', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
+{ oid => '8024',
+  proname => 'multirange_le', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
+{ oid => '8025',
+  proname => 'multirange_ge', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
+{ oid => '8026',
+  proname => 'multirange_gt', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
+{ oid => '8038', descr => 'hash a multirange',
+  proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
+  prosrc => 'hash_multirange' },
+{ oid => '8039', descr => 'hash a multirange',
+  proname => 'hash_multirange_extended', prorettype => 'int8',
+  proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
 
 { oid => '8045', descr => 'int4multirange constructor',
   proname => 'int4multirange', proisstrict => 'f',
@@ -9779,6 +9946,24 @@
   prorettype => 'int8multirange', proargtypes => '_int8range',
   proallargtypes => '{_int8range}', proargmodes => '{v}',
   prosrc => 'multirange_constructor1' },
+{ oid => '8130', descr => 'aggregate transition function',
+  proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+  proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8131', descr => 'aggregate final function',
+  proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
+  proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8132', descr => 'combine aggregate input into a multirange',
+  proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+  prorettype => 'anymultirange', proargtypes => 'anyrange',
+  prosrc => 'aggregate_dummy' },
+{ oid => '8135', descr => 'range aggregate by intersecting',
+  proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
+{ oid => '8136', descr => 'range aggregate by intersecting',
+  proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+  prorettype => 'anymultirange', proargtypes => 'anymultirange',
+  prosrc => 'aggregate_dummy'},
+
 # date, time, timestamp constructors
 { oid => '3846', descr => 'construct date',
   proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
index ed2e19aafa..cc4f82b0ba 100644
--- a/src/include/utils/multirangetypes.h
+++ b/src/include/utils/multirangetypes.h
@@ -59,6 +59,40 @@ extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr
 								   MultirangeType * mr2);
 extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
 								   MultirangeType * mr2);
+extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+											  Datum elem);
+extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+											   RangeType *r);
+extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+													MultirangeType * mr1,
+													MultirangeType * mr2);
+extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+											   MultirangeType * mr);
+extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
+													MultirangeType * mr1,
+													MultirangeType * mr2);
+extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+											 MultirangeType * mr);
+extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+											MultirangeType * mr);
+extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
+												  MultirangeType * mr1,
+												  MultirangeType * mr2);
+extern MultirangeType * range_union_multirange_internal(TypeCacheEntry *typcache,
+														RangeType *r,
+														MultirangeType * mr);
+extern MultirangeType * multirange_minus_multirange_internal(Oid mltrngtypoid,
+															 TypeCacheEntry *rangetyp,
+															 int32 range_count1,
+															 RangeType **ranges1,
+															 int32 range_count2,
+															 RangeType **ranges2);
+extern MultirangeType * multirange_intersect_multirange_internal(Oid mltrngtypoid,
+																 TypeCacheEntry *rangetyp,
+																 int32 range_count1,
+																 RangeType **ranges1,
+																 int32 range_count2,
+																 RangeType **ranges2);
 
 /* assorted support functions */
 extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 6323034548..33775a9d42 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -94,13 +94,22 @@ typedef struct
  * prototypes for functions defined in rangetypes.c
  */
 
-extern bool range_contains_elem_internal(TypeCacheEntry *typcache, RangeType *r, Datum val);
-
 /* internal versions of the above */
+extern int	range_cmp_internal(TypeCacheEntry *typcache, RangeType *r1,
+							   RangeType *r2);
+extern uint32 hash_range_internal(TypeCacheEntry *typcache, RangeType *r);
+extern uint64 hash_range_extended_internal(TypeCacheEntry *typcache, RangeType *r,
+										   Datum seed);
+extern Datum range_lower_internal(TypeCacheEntry *typcache, RangeType *r1,
+								  bool *isnull);
+extern Datum range_upper_internal(TypeCacheEntry *typcache, RangeType *r1,
+								  bool *isnull);
 extern bool range_eq_internal(TypeCacheEntry *typcache, RangeType *r1,
 							  RangeType *r2);
 extern bool range_ne_internal(TypeCacheEntry *typcache, RangeType *r1,
 							  RangeType *r2);
+extern bool range_contains_elem_internal(TypeCacheEntry *typcache, RangeType *r,
+										 Datum val);
 extern bool range_contains_internal(TypeCacheEntry *typcache, RangeType *r1,
 									RangeType *r2);
 extern bool range_contained_by_internal(TypeCacheEntry *typcache, RangeType *r1,
@@ -117,6 +126,12 @@ extern bool range_overleft_internal(TypeCacheEntry *typcache, RangeType *r1,
 									RangeType *r2);
 extern bool range_overright_internal(TypeCacheEntry *typcache, RangeType *r1,
 									 RangeType *r2);
+extern RangeType *range_union_internal(TypeCacheEntry *typcache, RangeType *r1,
+									   RangeType *r2, bool strict);
+extern RangeType *range_minus_internal(TypeCacheEntry *typcache, RangeType *r1,
+									   RangeType *r2);
+extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, RangeType *r1,
+										   RangeType *r2);
 
 /* assorted support functions */
 extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
@@ -127,6 +142,7 @@ extern void range_deserialize(TypeCacheEntry *typcache, RangeType *range,
 							  RangeBound *lower, RangeBound *upper,
 							  bool *empty);
 extern char range_get_flags(RangeType *range);
+extern bool range_has_flag(RangeType *range, char flag);
 extern void range_set_contain_empty(RangeType *range);
 extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
 							 RangeBound *upper, bool empty);
@@ -134,8 +150,12 @@ extern int	range_cmp_bounds(TypeCacheEntry *typcache, RangeBound *b1,
 							 RangeBound *b2);
 extern int	range_cmp_bound_values(TypeCacheEntry *typcache, RangeBound *b1,
 								   RangeBound *b2);
+extern int	range_compare(const void *key1, const void *key2, void *arg);
 extern bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
 							RangeBound bound2);
 extern RangeType *make_empty_range(TypeCacheEntry *typcache);
+extern bool range_split_internal(TypeCacheEntry *typcache, RangeType *r1,
+								 RangeType *r2, RangeType **output1,
+								 RangeType **output2);
 
 #endif							/* RANGETYPES_H */
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index b4a11b8aa9..778699a961 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -141,6 +141,7 @@ owner of table deptest
 owner of function deptest_func()
 owner of type deptest_enum
 owner of type deptest_range
+owner of type deptest_multirange
 owner of table deptest2
 owner of sequence ss1
 owner of type deptest_t
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
index 5749537635..2276d6d002 100644
--- a/src/test/regress/expected/multirangetypes.out
+++ b/src/test/regress/expected/multirangetypes.out
@@ -290,3 +290,2400 @@ select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
  {[a,d)}
 (1 row)
 
+--
+-- create some test data and test the operators
+--
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+          nmr          | isempty | lower | upper 
+-----------------------+---------+-------+-------
+ {}                    | t       |       |      
+ {}                    | t       |       |      
+ {}                    | t       |       |      
+ {}                    | t       |       |      
+ {(,5)}                | f       |       |     5
+ {(,)}                 | f       |       |      
+ {(,)}                 | f       |       |      
+ {[1.1,2.2)}           | f       |   1.1 |   2.2
+ {[1.7,1.7],[1.9,2.1)} | f       |   1.7 |   2.1
+ {[1.7,1.9)}           | f       |   1.7 |   1.9
+ {[3,)}                | f       |     3 |      
+(11 rows)
+
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+          nmr          | lower_inc | lower_inf | upper_inc | upper_inf 
+-----------------------+-----------+-----------+-----------+-----------
+ {}                    | f         | f         | f         | f
+ {}                    | f         | f         | f         | f
+ {}                    | f         | f         | f         | f
+ {}                    | f         | f         | f         | f
+ {(,5)}                | f         | t         | f         | f
+ {(,)}                 | f         | t         | f         | t
+ {(,)}                 | f         | t         | f         | t
+ {[1.1,2.2)}           | t         | f         | f         | f
+ {[1.7,1.7],[1.9,2.1)} | t         | f         | f         | f
+ {[1.7,1.9)}           | t         | f         | f         | f
+ {[3,)}                | t         | f         | f         | t
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+ nmr 
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+  nmr   
+--------
+ {(,5)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+  nmr   
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+ nmr 
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+          nmr          
+-----------------------
+ {[1.7,1.7],[1.9,2.1)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+ nmr 
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+  nmr   
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+  nmr   
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+          nmr          
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+ nmr 
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+          nmr          
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+          nmr          
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+  nmr   
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+          nmr          
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+          nmr          
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+          nmr          
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+ nmr 
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+          nmr          
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+          nmr          
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(10 rows)
+
+select nummultirange(numrange(2.0, 1.0));
+ERROR:  range lower bound must be less than or equal to range upper bound
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+     nummultirange     
+-----------------------
+ {[1.0,2.0),[5.0,6.0)}
+(1 row)
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && 'empty'::numrange;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange() @> 'empty'::numrange;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+ ?column? 
+----------
+ t
+(1 row)
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'empty'::numrange <@ nummultirange();
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+ ?column? 
+----------
+ t
+(1 row)
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< 'empty'::numrange;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+ ?column? 
+----------
+ f
+(1 row)
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- 'empty'::numrange;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+ ?column? 
+----------
+ t
+(1 row)
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(3,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange() << 'empty'::numrange;
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange() << numrange(1,2);
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) << numrange(3,6);
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,2)) << numrange(3,6);
+ ?column? 
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+ ?column? 
+----------
+ t
+(1 row)
+
+select nummultirange() << nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange() << nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column? 
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column? 
+----------
+ f
+(1 row)
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange() >> numrange(1,2);
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+ ?column? 
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+ ?column? 
+----------
+ f
+(1 row)
+
+select 'empty'::numrange >> nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+select numrange(1,2) >> nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2));
+ ?column? 
+----------
+ t
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+ ?column? 
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+ ?column? 
+----------
+ t
+(1 row)
+
+select nummultirange() >> nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange() >> nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+ ?column? 
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+ ?column? 
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+ ?column? 
+----------
+ f
+(1 row)
+
+-- union
+SELECT 'empty'::numrange @+ 'empty'::numrange;
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @+ nummultirange();
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @+ 'empty'::numrange;
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @+ nummultirange();
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @+ numrange(1,2);
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT 'empty'::numrange @+ nummultirange(numrange(1,2));
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ nummultirange();
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ 'empty'::numrange;
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ 'empty'::numrange;
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange() @+ numrange(1,2);
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange() @+ nummultirange(numrange(1,2));
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange();
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ numrange(1,2);
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ numrange(2,4);
+ ?column? 
+----------
+ {[1,4)}
+(1 row)
+
+SELECT numrange(1,2) @+ numrange(3,4);
+   ?column?    
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(1,2));
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(2,4));
+ ?column? 
+----------
+ {[1,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(3,4));
+   ?column?    
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(2,4));
+ ?column? 
+----------
+ {[1,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(3,4));
+   ?column?    
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(0,9));
+ ?column? 
+----------
+ {[0,9)}
+(1 row)
+
+-- merge
+SELECT range_merge(nummultirange());
+ range_merge 
+-------------
+ empty
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2)));
+ range_merge 
+-------------
+ [1,2)
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+ range_merge 
+-------------
+ [1,8)
+(1 row)
+
+-- minus
+SELECT 'empty'::numrange @- 'empty'::numrange;
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @- nummultirange();
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @- 'empty'::numrange;
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @- nummultirange();
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @- numrange(1,2);
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @- nummultirange(numrange(1,2));
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @- nummultirange();
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- 'empty'::numrange;
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT 'empty'::numrange @- numrange(1,2);
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @- 'empty'::numrange;
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange() @- numrange(1,2);
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @- nummultirange(numrange(1,2));
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange();
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(1,3);
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(1,2);
+ ?column? 
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(2,4);
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(3,4);
+ ?column? 
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(1,3));
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(1,2));
+ ?column? 
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(2,4));
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(3,4));
+ ?column? 
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,10) @- nummultirange(numrange(1,2), numrange(4,5));
+    ?column?    
+----------------
+ {[2,4),[5,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(0,2);
+ ?column? 
+----------
+ {[2,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(1,2);
+ ?column? 
+----------
+ {[2,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(3,4);
+    ?column?    
+----------------
+ {[1,3),[4,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(13,18);
+ ?column? 
+----------
+ {[1,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3,4)) @- nummultirange();
+   ?column?    
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(1,2));
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(2,4));
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(3,4));
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(1,2));
+ ?column? 
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(2,3));
+   ?column?    
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,8));
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,2));
+ ?column? 
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(0,2), numrange(3,4));
+   ?column?    
+---------------
+ {[2,3),[4,8)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(2,3), numrange(5,null));
+   ?column?    
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0));
+   ?column?    
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(2,4));
+   ?column?    
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(3,5));
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(0,9));
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,3), numrange(4,5)) @- nummultirange(numrange(2,9));
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(8,9));
+   ?column?    
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0), numrange(8,9));
+   ?column?    
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+-- intersection
+SELECT 'empty'::numrange @* 'empty'::numrange;
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @* nummultirange();
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* 'empty'::numrange;
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* nummultirange();
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @* numrange(1,2);
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @* nummultirange(numrange(1,2));
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @* 'empty'::numrange;
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @* nummultirange();
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @* 'empty'::numrange;
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* numrange(1,2);
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* nummultirange(numrange(1,2));
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @* nummultirange();
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(1,3);
+ ?column? 
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(1,2);
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(1,5);
+ ?column? 
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(2,5);
+ ?column? 
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,5) @* numrange(2,3);
+ ?column? 
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(1,3);
+ ?column? 
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(1,2);
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(1,5);
+ ?column? 
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(2,5);
+ ?column? 
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @* numrange(2,3);
+ ?column? 
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,3) @* '{[1,3)}'::nummultirange;
+ ?column? 
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,2) @* '{[1,3)}'::nummultirange;
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,5) @* '{[1,3)}'::nummultirange;
+ ?column? 
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(2,5) @* '{[1,3)}'::nummultirange;
+ ?column? 
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(2,3) @* '{[1,5)}'::nummultirange;
+ ?column? 
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[1,5)}'::nummultirange;
+ ?column? 
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[0,5)}'::nummultirange;
+ ?column? 
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[0,2)}'::nummultirange;
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[2,5)}'::nummultirange;
+ ?column? 
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange @* '{[2,3)}'::nummultirange;
+ ?column? 
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange @* '{[0,2), [3,5)}'::nummultirange;
+   ?column?    
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,8), [9,12)}'::nummultirange;
+       ?column?       
+----------------------
+ {[1,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[9,12)}'::nummultirange;
+ ?column? 
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+ ?column? 
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+          ?column?          
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+          ?column?          
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT   room_id, range_agg(booked_during)
+FROM     reservations
+GROUP BY room_id
+ORDER BY room_id;
+ room_id |                     range_agg                     
+---------+---------------------------------------------------
+       1 | {[07-01-2018,07-14-2018),[07-20-2018,07-22-2018)}
+       2 | {[07-01-2018,07-03-2018)}
+       3 | 
+       4 | 
+       5 | {[07-01-2018,07-03-2018)}
+       6 | {[07-01-2018,07-10-2018)}
+       7 | {[07-01-2018,07-14-2018)}
+       8 | {}
+(8 rows)
+
+-- range_agg on a custom range type too
+SELECT  range_agg(r)
+FROM    (VALUES
+          ('[a,c]'::textrange),
+          ('[b,b]'::textrange),
+          ('[c,f]'::textrange),
+          ('[g,h)'::textrange),
+          ('[h,j)'::textrange)
+        ) t(r);
+   range_agg   
+---------------
+ {[a,f],[g,j)}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test;
+ range_intersect_agg 
+---------------------
+ {}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where false;
+ range_intersect_agg 
+---------------------
+ 
+(1 row)
+
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+ range_intersect_agg 
+---------------------
+ {[1,2]}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+ range_intersect_agg 
+---------------------
+ {[3,5)}
+(1 row)
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+select * from nummultirange_test2 where nmr = '{}';
+ nmr 
+-----
+ {}
+(1 row)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+     nmr     
+-------------
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(2 rows)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+ nmr 
+-----
+(0 rows)
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+     nmr     
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+     nmr     
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+     nmr     
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+DROP TABLE nummultirange_test2;
+--
+-- Test user-defined multirange of floats
+--
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+ ?column? 
+----------
+ t
+(1 row)
+
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+           f8mr            | i  
+---------------------------+----
+ {[-100.00007,1111113000)} | 42
+(1 row)
+
+drop table float8multirange_test;
+--
+-- Test multirange types over domains
+--
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+ ?column? 
+----------
+ t
+(1 row)
+
+drop domain mydomain cascade;
+NOTICE:  drop cascades to type mydomainrange
+--
+-- Test domains over multirange types
+--
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+ ?column? 
+----------
+ f
+(1 row)
+
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+ERROR:  value for domain restrictedmultirange violates check constraint "restrictedmultirange_check"
+drop domain restrictedmultirange;
+--
+-- Test multiple multirange types over the same subtype
+--
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+select textmultirange1(textrange2('a','Z'));  -- should fail
+ERROR:  function textmultirange1(textrange2) does not exist
+LINE 1: select textmultirange1(textrange2('a','Z'));
+               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+ERROR:  range lower bound must be less than or equal to range upper bound
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+ ?column? 
+----------
+ t
+(1 row)
+
+drop type textrange1;
+drop type textrange2;
+--
+-- Test polymorphic type system
+--
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+  returns anyelement as 'select $1[1] + lower($2);' language sql;
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+ anyarray_anymultirange_func 
+-----------------------------
+                          11
+(1 row)
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+ERROR:  function anyarray_anymultirange_func(integer[], nummultirange) does not exist
+LINE 1: select anyarray_anymultirange_func(ARRAY[1,2], nummultirange...
+               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+-- should fail
+create function bogus_func(anyelement)
+  returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+-- should fail
+create function bogus_func(int)
+  returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A function returning a polymorphic type must have at least one polymorphic argument.
+create function range_add_bounds(anymultirange)
+  returns anyelement as 'select lower($1) + upper($1)' language sql;
+select range_add_bounds(int4multirange(int4range(1, 17)));
+ range_add_bounds 
+------------------
+               18
+(1 row)
+
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+ range_add_bounds 
+------------------
+         124.1231
+(1 row)
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+  as $$ select upper($1) + $2[1] $$
+  language sql;
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+ multirangetypes_sql 
+---------------------
+                  12
+(1 row)
+
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]);  -- match failure
+ERROR:  function multirangetypes_sql(nummultirange, integer[]) does not exist
+LINE 1: select multirangetypes_sql(nummultirange(numrange(1,10)), AR...
+               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+--
+-- Arrays of multiranges
+--
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+              array               
+----------------------------------
+ {"{[1.1,1.2)}","{[12.3,155.5)}"}
+(1 row)
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+ f1 |           f2            
+----+-------------------------
+ 42 | {"{[1,10)}","{[2,20)}"}
+(1 row)
+
+drop table i8mr_array;
+--
+-- Multiranges of arrays
+--
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+   arraymultirange   
+---------------------
+ {["{1,2}","{2,1}")}
+(1 row)
+
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2]));  -- fail
+ERROR:  range lower bound must be less than or equal to range upper bound
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column? 
+----------
+ f
+(1 row)
+
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column? 
+----------
+ t
+(1 row)
+
+--
+-- Ranges of composites
+--
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+  (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+          (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+          t          |       u       
+---------------------+---------------
+ {["(1,2)","(3,4)")} | {"a":3,"b":4}
+ {["(5,6)","(7,8)")} | {"a":7,"b":8}
+(2 rows)
+
+drop type two_ints cascade;
+NOTICE:  drop cascades to type two_ints_range
+--
+-- Check behavior when subtype lacks a hash function
+--
+set enable_sort = off;  -- try to make it pick a hash setop implementation
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+ cashmultirange  
+-----------------
+ {($2.00,$5.00)}
+(1 row)
+
+reset enable_sort;
+--
+-- OUT/INOUT/TABLE functions
+--
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+  as $$ select $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+    r    |  t  
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+  as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+  r  |  t  
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+  as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+   r   |  t  
+-------+-----
+ [1,2) | foo
+(1 row)
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+  as $$ select $1 @+ $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed4(int4range(1,2));
+    r    |  t  
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+  as $$ select upper($1), $1 $$ language sql;
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+ i |    r    
+---+---------
+ 2 | {[1,2)}
+(1 row)
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+  as $$ select $1, $2 $$ language sql;
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+  i  |    r     
+-----+----------
+ 123 | {[1,11)}
+(1 row)
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+  as $$ select '[1,10]', 'foo' $$ language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+  as $$ select $1, '[1,10]' $$ language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+  as $$ select $1, '[1,10]' $$ language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 916e1016b5..e818eaea79 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1101,13 +1101,15 @@ ORDER BY 1, 2;
  ?|   | ?|
  ?||  | ?||
  @    | ~
+ @*   | @*
+ @+   | @+
  @@   | @@
  @@@  | @@@
  |    | |
  ~<=~ | ~>=~
  ~<~  | ~>~
  ~=   | ~=
-(30 rows)
+(32 rows)
 
 -- Likewise for negator pairs.
 SELECT DISTINCT o1.oprname AS op1, o2.oprname AS op2
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index 60d875e898..d825c0db1a 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -518,6 +518,24 @@ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
  empty
 (1 row)
 
+select range_intersect_agg(nr) from numrange_test;
+ range_intersect_agg 
+---------------------
+ empty
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where false;
+ range_intersect_agg 
+---------------------
+ 
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+ range_intersect_agg 
+---------------------
+ [3,5)
+(1 row)
+
 create table numrange_test2(nr numrange);
 create index numrange_test2_hash_idx on numrange_test2 (nr);
 INSERT INTO numrange_test2 VALUES('[, 5)');
@@ -1284,7 +1302,7 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
 create function bogus_func(anyelement)
   returns anyrange as 'select int4range(1,10)' language sql;
 ERROR:  cannot determine result data type
-DETAIL:  A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL:  A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
 -- should fail
 create function bogus_func(int)
   returns anyrange as 'select int4range(1,10)' language sql;
@@ -1392,6 +1410,7 @@ reset enable_sort;
 --
 -- OUT/INOUT/TABLE functions
 --
+-- infer anyrange from anyrange
 create function outparam_succeed(i anyrange, out r anyrange, out t text)
   as $$ select $1, 'foo'::text $$ language sql;
 select * from outparam_succeed(int4range(1,2));
@@ -1400,6 +1419,16 @@ select * from outparam_succeed(int4range(1,2));
  [1,2) | foo
 (1 row)
 
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+  as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from outparam_succeed2(int4range(int4range(1,2)));
+  r  |  t  
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyelement from anyrange
 create function inoutparam_succeed(out i anyelement, inout r anyrange)
   as $$ select upper($1), $1 $$ language sql;
 select * from inoutparam_succeed(int4range(1,2));
@@ -1408,6 +1437,7 @@ select * from inoutparam_succeed(int4range(1,2));
  2 | [1,2)
 (1 row)
 
+-- infer anyelement+anyrange from anyelement+anyrange
 create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
   as $$ select $1, $2 $$ language sql;
 select * from table_succeed(123, int4range(1,11));
@@ -1420,14 +1450,14 @@ select * from table_succeed(123, int4range(1,11));
 create function outparam_fail(i anyelement, out r anyrange, out t text)
   as $$ select '[1,10]', 'foo' $$ language sql;
 ERROR:  cannot determine result data type
-DETAIL:  A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL:  A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
 --should fail
 create function inoutparam_fail(inout i anyelement, out r anyrange)
   as $$ select $1, '[1,10]' $$ language sql;
 ERROR:  cannot determine result data type
-DETAIL:  A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL:  A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
 --should fail
 create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
   as $$ select $1, '[1,10]' $$ language sql;
 ERROR:  cannot determine result data type
-DETAIL:  A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL:  A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index d6e75ffce6..3d92541ebc 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -92,6 +92,7 @@ num_exp_sqrt|t
 num_exp_sub|t
 num_input_test|f
 num_result|f
+nummultirange_test|t
 onek|t
 onek2|t
 path_tbl|f
@@ -172,6 +173,7 @@ quad_poly_tbl|t
 radix_text_tbl|t
 ramp|f
 real_city|f
+reservations|f
 road|t
 shighway|t
 slow_emp4000|f
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
index 8651ba3f3d..b3ede18f96 100644
--- a/src/test/regress/sql/multirangetypes.sql
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -63,3 +63,610 @@ select textmultirange();
 select textmultirange(textrange('a', 'c'));
 select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
 select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+
+--
+-- create some test data and test the operators
+--
+
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+
+select nummultirange(numrange(2.0, 1.0));
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+SELECT nummultirange() && 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+SELECT nummultirange() && nummultirange();
+SELECT nummultirange() && nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+SELECT nummultirange() @> 'empty'::numrange;
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+SELECT 'empty'::numrange <@ nummultirange();
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+SELECT nummultirange() &< 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+SELECT nummultirange() &< nummultirange();
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+SELECT 'empty'::numrange &> nummultirange();
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+SELECT nummultirange() &> nummultirange();
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+SELECT nummultirange() -|- 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+SELECT nummultirange() -|- nummultirange();
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+select numrange(1,2) << nummultirange();
+select numrange(1,2) << nummultirange(numrange(3,4));
+select numrange(1,2) << nummultirange(numrange(0,4));
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+select nummultirange() << 'empty'::numrange;
+select nummultirange() << numrange(1,2);
+select nummultirange(numrange(3,4)) << numrange(3,6);
+select nummultirange(numrange(0,2)) << numrange(3,6);
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+select nummultirange() << nummultirange();
+select nummultirange() << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange();
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+select nummultirange() >> numrange(1,2);
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+select 'empty'::numrange >> nummultirange();
+select numrange(1,2) >> nummultirange();
+select numrange(3,6) >> nummultirange(numrange(3,4));
+select numrange(3,6) >> nummultirange(numrange(0,2));
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+select nummultirange() >> nummultirange();
+select nummultirange(numrange(1,2)) >> nummultirange();
+select nummultirange() >> nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+
+-- union
+SELECT 'empty'::numrange @+ 'empty'::numrange;
+SELECT 'empty'::numrange @+ nummultirange();
+SELECT nummultirange() @+ 'empty'::numrange;
+SELECT nummultirange() @+ nummultirange();
+SELECT 'empty'::numrange @+ numrange(1,2);
+SELECT 'empty'::numrange @+ nummultirange(numrange(1,2));
+SELECT numrange(1,2) @+ nummultirange();
+SELECT nummultirange(numrange(1,2)) @+ 'empty'::numrange;
+SELECT numrange(1,2) @+ 'empty'::numrange;
+SELECT nummultirange() @+ numrange(1,2);
+SELECT nummultirange() @+ nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @+ nummultirange();
+SELECT numrange(1,2) @+ numrange(1,2);
+SELECT numrange(1,2) @+ numrange(2,4);
+SELECT numrange(1,2) @+ numrange(3,4);
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(0,9));
+
+-- merge
+SELECT range_merge(nummultirange());
+SELECT range_merge(nummultirange(numrange(1,2)));
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+
+-- minus
+SELECT 'empty'::numrange @- 'empty'::numrange;
+SELECT 'empty'::numrange @- nummultirange();
+SELECT nummultirange() @- 'empty'::numrange;
+SELECT nummultirange() @- nummultirange();
+SELECT 'empty'::numrange @- numrange(1,2);
+SELECT 'empty'::numrange @- nummultirange(numrange(1,2));
+SELECT numrange(1,2) @- nummultirange();
+SELECT nummultirange(numrange(1,2)) @- 'empty'::numrange;
+SELECT 'empty'::numrange @- numrange(1,2);
+SELECT numrange(1,2) @- 'empty'::numrange;
+SELECT nummultirange() @- numrange(1,2);
+SELECT nummultirange() @- nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @- nummultirange();
+SELECT numrange(1,3) @- numrange(1,3);
+SELECT numrange(1,3) @- numrange(1,2);
+SELECT numrange(1,3) @- numrange(2,4);
+SELECT numrange(1,3) @- numrange(3,4);
+SELECT numrange(1,3) @- nummultirange(numrange(1,3));
+SELECT numrange(1,3) @- nummultirange(numrange(1,2));
+SELECT numrange(1,3) @- nummultirange(numrange(2,4));
+SELECT numrange(1,3) @- nummultirange(numrange(3,4));
+SELECT numrange(1,10) @- nummultirange(numrange(1,2), numrange(4,5));
+SELECT nummultirange(numrange(1,10)) @- numrange(0,2);
+SELECT nummultirange(numrange(1,10)) @- numrange(1,2);
+SELECT nummultirange(numrange(1,10)) @- numrange(3,4);
+SELECT nummultirange(numrange(1,10)) @- numrange(13,18);
+SELECT nummultirange(numrange(1,2), numrange(3,4)) @- nummultirange();
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(2,3));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,8));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,2));
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(0,2), numrange(3,4));
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(2,3), numrange(5,null));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(3,5));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(0,9));
+SELECT nummultirange(numrange(1,3), numrange(4,5)) @- nummultirange(numrange(2,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0), numrange(8,9));
+
+-- intersection
+SELECT 'empty'::numrange @* 'empty'::numrange;
+SELECT 'empty'::numrange @* nummultirange();
+SELECT nummultirange() @* 'empty'::numrange;
+SELECT nummultirange() @* nummultirange();
+SELECT 'empty'::numrange @* numrange(1,2);
+SELECT 'empty'::numrange @* nummultirange(numrange(1,2));
+SELECT numrange(1,2) @* 'empty'::numrange;
+SELECT numrange(1,2) @* nummultirange();
+SELECT nummultirange(numrange(1,2)) @* 'empty'::numrange;
+SELECT nummultirange() @* numrange(1,2);
+SELECT nummultirange() @* nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @* nummultirange();
+SELECT numrange(1,3) @* numrange(1,3);
+SELECT numrange(1,3) @* numrange(1,2);
+SELECT numrange(1,3) @* numrange(1,5);
+SELECT numrange(1,3) @* numrange(2,5);
+SELECT numrange(1,5) @* numrange(2,3);
+SELECT '{[1,3)}'::nummultirange @* numrange(1,3);
+SELECT '{[1,3)}'::nummultirange @* numrange(1,2);
+SELECT '{[1,3)}'::nummultirange @* numrange(1,5);
+SELECT '{[1,3)}'::nummultirange @* numrange(2,5);
+SELECT '{[1,5)}'::nummultirange @* numrange(2,3);
+SELECT numrange(1,3) @* '{[1,3)}'::nummultirange;
+SELECT numrange(1,2) @* '{[1,3)}'::nummultirange;
+SELECT numrange(1,5) @* '{[1,3)}'::nummultirange;
+SELECT numrange(2,5) @* '{[1,3)}'::nummultirange;
+SELECT numrange(2,3) @* '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[0,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[0,2)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[2,5)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange @* '{[2,3)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange @* '{[0,2), [3,5)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT   room_id, range_agg(booked_during)
+FROM     reservations
+GROUP BY room_id
+ORDER BY room_id;
+
+-- range_agg on a custom range type too
+SELECT  range_agg(r)
+FROM    (VALUES
+          ('[a,c]'::textrange),
+          ('[b,b]'::textrange),
+          ('[c,f]'::textrange),
+          ('[g,h)'::textrange),
+          ('[h,j)'::textrange)
+        ) t(r);
+
+select range_intersect_agg(nmr) from nummultirange_test;
+select range_intersect_agg(nmr) from nummultirange_test where false;
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+
+select * from nummultirange_test2 where nmr = '{}';
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+
+DROP TABLE nummultirange_test2;
+
+--
+-- Test user-defined multirange of floats
+--
+
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+drop table float8multirange_test;
+
+--
+-- Test multirange types over domains
+--
+
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+drop domain mydomain cascade;
+
+--
+-- Test domains over multirange types
+--
+
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+drop domain restrictedmultirange;
+
+--
+-- Test multiple multirange types over the same subtype
+--
+
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+
+select textmultirange1(textrange2('a','Z'));  -- should fail
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+
+drop type textrange1;
+drop type textrange2;
+
+--
+-- Test polymorphic type system
+--
+
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+  returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+
+-- should fail
+create function bogus_func(anyelement)
+  returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+-- should fail
+create function bogus_func(int)
+  returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+create function range_add_bounds(anymultirange)
+  returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+select range_add_bounds(int4multirange(int4range(1, 17)));
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+  as $$ select upper($1) + $2[1] $$
+  language sql;
+
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]);  -- match failure
+
+--
+-- Arrays of multiranges
+--
+
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+drop table i8mr_array;
+
+--
+-- Multiranges of arrays
+--
+
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2]));  -- fail
+
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+
+--
+-- Ranges of composites
+--
+
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+  (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+          (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+
+drop type two_ints cascade;
+
+--
+-- Check behavior when subtype lacks a hash function
+--
+
+set enable_sort = off;  -- try to make it pick a hash setop implementation
+
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+
+reset enable_sort;
+
+--
+-- OUT/INOUT/TABLE functions
+--
+
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+  as $$ select $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+  as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+  as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+  as $$ select $1 @+ $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed4(int4range(1,2));
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+  as $$ select upper($1), $1 $$ language sql;
+
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+  as $$ select $1, $2 $$ language sql;
+
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+  as $$ select '[1,10]', 'foo' $$ language sql;
+
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+  as $$ select $1, '[1,10]' $$ language sql;
+
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+  as $$ select $1, '[1,10]' $$ language sql;
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 9fdb1953df..d23855d08e 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -118,6 +118,10 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
 select numrange(1.0, 2.0) * numrange(1.5, 3.0);
 select numrange(1.0, 2.0) * numrange(2.5, 3.0);
 
+select range_intersect_agg(nr) from numrange_test;
+select range_intersect_agg(nr) from numrange_test where false;
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+
 create table numrange_test2(nr numrange);
 create index numrange_test2_hash_idx on numrange_test2 (nr);
 
@@ -481,16 +485,25 @@ reset enable_sort;
 -- OUT/INOUT/TABLE functions
 --
 
+-- infer anyrange from anyrange
 create function outparam_succeed(i anyrange, out r anyrange, out t text)
   as $$ select $1, 'foo'::text $$ language sql;
 
 select * from outparam_succeed(int4range(1,2));
 
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+  as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from outparam_succeed2(int4range(int4range(1,2)));
+
+-- infer anyelement from anyrange
 create function inoutparam_succeed(out i anyelement, inout r anyrange)
   as $$ select upper($1), $1 $$ language sql;
 
 select * from inoutparam_succeed(int4range(1,2));
 
+-- infer anyelement+anyrange from anyelement+anyrange
 create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
   as $$ select $1, $2 $$ language sql;
 
-- 
2.11.0

