/*-------------------------------------------------------------------------
 *
 * rangetypes.c
 *	  I/O functions, operators, and support functions for range types
 *
 * Copyright (c) 2006-2011, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/rangetypes.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/hash.h"
#include "access/nbtree.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_range.h"
#include "catalog/pg_type.h"
#include "fmgr.h"
#include "lib/stringinfo.h"
#include "libpq/pqformat.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/fmgroids.h"
#include "utils/int8.h"
#include "utils/lsyscache.h"
#include "utils/rangetypes.h"
#include "utils/syscache.h"
#include "utils/timestamp.h"
#include "utils/typcache.h"

#define TYPE_IS_PACKABLE(typlen, typstorage) \
	(typlen == -1 && typstorage != 'p')

/* flags */
#define RANGE_EMPTY		0x01
#define RANGE_LB_INC	0x02
#define RANGE_LB_NULL	0x04 /* NOT USED */
#define RANGE_LB_INF	0x08
#define RANGE_UB_INC	0x10
#define RANGE_UB_NULL	0x20 /* NOT USED */
#define RANGE_UB_INF	0x40

#define RANGE_HAS_LBOUND(flags) (!(flags & (RANGE_EMPTY |   \
											RANGE_LB_NULL |	\
											RANGE_LB_INF)))

#define RANGE_HAS_UBOUND(flags) (!(flags & (RANGE_EMPTY |	\
											RANGE_UB_NULL |	\
											RANGE_UB_INF)))

#define RANGE_EMPTY_LITERAL "empty"

static void range_parse(char *input_str,  char *flags, char **lbound_str,
						char **ubound_str);
static char *range_parse_bound(char *string, char *ptr, char **bound_str,
							   bool *infinite);
static char *range_deparse(char flags, char *lbound_str, char *ubound_str);
static char *range_bound_escape(char *in_str);
static bool range_contains_internal(FunctionCallInfo fcinfo, RangeType *r1,
									RangeType *r2);
static Size datum_compute_size(Size sz, Datum datum, bool typbyval,
							   char typalign, int16 typlen, char typstorage);
static Pointer datum_write(Pointer ptr, Datum datum, bool typbyval,
						   char typalign, int16 typlen, char typstorage);

/*
 *----------------------------------------------------------
 * I/O FUNCTIONS
 *----------------------------------------------------------
 */

Datum
range_in(PG_FUNCTION_ARGS)
{
	char		*input_str = PG_GETARG_CSTRING(0);
	Oid			 rngtypoid = PG_GETARG_OID(1);
	Oid			 typmod	   = PG_GETARG_INT32(2);

	char		 flags;
	Datum		 range;
	char		*lbound_str;
	char		*ubound_str;

	regproc		subInput;
	FmgrInfo	subInputFn;
	Oid			ioParam;

	RangeTypeInfo		rngtypinfo;
	RangeBound			lower;
	RangeBound			upper;

	if (rngtypoid == ANYRANGEOID)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("cannot accept a value of type anyrange")));

	range_gettypinfo(fcinfo, rngtypoid, &rngtypinfo);

	/* parse */
	range_parse(input_str, &flags, &lbound_str, &ubound_str);

	/* input */
	getTypeInputInfo(rngtypinfo.subtype, &subInput, &ioParam);
	fmgr_info(subInput, &subInputFn);

	lower.rngtypid	= rngtypoid;
	lower.infinite	= (flags & RANGE_LB_INF) != 0;
	lower.inclusive = (flags & RANGE_LB_INC) != 0;
	lower.lower		= true;
	upper.rngtypid	= rngtypoid;
	upper.infinite	= (flags & RANGE_UB_INF) != 0;
	upper.inclusive = (flags & RANGE_UB_INC) != 0;
	upper.lower		= false;

	if (RANGE_HAS_LBOUND(flags))
		lower.val = InputFunctionCall(&subInputFn, lbound_str,
									   ioParam, typmod);
	if (RANGE_HAS_UBOUND(flags))
		upper.val = InputFunctionCall(&subInputFn, ubound_str,
									   ioParam, typmod);

	/* serialize and canonicalize */
	range = make_range(fcinfo, &lower, &upper, flags & RANGE_EMPTY);

	PG_RETURN_RANGE(range);
}

Datum
range_out(PG_FUNCTION_ARGS)
{
	RangeType  *range = PG_GETARG_RANGE(0);

	regproc		subOutput;
	FmgrInfo	subOutputFn;
	bool		isVarlena;

	char		flags = 0;
	char	   *lbound_str = NULL;
	char	   *ubound_str = NULL;
	char	   *output_str;

	bool		empty;

	RangeTypeInfo		rngtypinfo;
	RangeBound			lower;
	RangeBound			upper;

	/* deserialize */
	range_deserialize(fcinfo, range, &lower, &upper, &empty);

	if (lower.rngtypid != upper.rngtypid)
		elog(ERROR, "range types do not match");

	range_gettypinfo(fcinfo, lower.rngtypid, &rngtypinfo);

	if (empty)
		flags |= RANGE_EMPTY;

	flags |= (lower.inclusive)	? RANGE_LB_INC  : 0;
	flags |= (lower.infinite)	? RANGE_LB_INF  : 0;
	flags |= (upper.inclusive)	? RANGE_UB_INC  : 0;
	flags |= (upper.infinite)	? RANGE_UB_INF  : 0;

	/* output */
	getTypeOutputInfo(rngtypinfo.subtype, &subOutput, &isVarlena);
	fmgr_info(subOutput, &subOutputFn);

	if (RANGE_HAS_LBOUND(flags))
		lbound_str = OutputFunctionCall(&subOutputFn, lower.val);
	if (RANGE_HAS_UBOUND(flags))
		ubound_str = OutputFunctionCall(&subOutputFn, upper.val);

	/* deparse */
	output_str = range_deparse(flags, lbound_str, ubound_str);

	PG_RETURN_CSTRING(output_str);
}

/*
 * Binary representation: The first byte is the flags, then 4 bytes are the
 * range type Oid, then the lower bound (if present) then the upper bound (if
 * present). Each bound is represented by a 4-byte length header and the binary
 * representation of that bound (as returned by a call to the send function for
 * the subtype).
 */

Datum
range_recv(PG_FUNCTION_ARGS)
{
	StringInfo	buf		 = (StringInfo) PG_GETARG_POINTER(0);
	Oid			rngtypid = PG_GETARG_OID(1);
	int32		typmod	 = PG_GETARG_INT32(2);
	Oid			subrecv;
	Oid			ioparam;
	Datum		range;
	char		flags;
	RangeBound	lower;
	RangeBound	upper;

	RangeTypeInfo rngtypinfo;

	flags = (unsigned char) pq_getmsgbyte(buf);

	range_gettypinfo(fcinfo, rngtypid, &rngtypinfo);

	getTypeBinaryInputInfo(rngtypinfo.subtype, &subrecv, &ioparam);

	if (RANGE_HAS_LBOUND(flags))
	{
		uint32			 bound_len	= pq_getmsgint(buf, 4);
		const char		*bound_data = pq_getmsgbytes(buf, bound_len);
		StringInfoData	 bound_buf;

		initStringInfo(&bound_buf);
		appendBinaryStringInfo(&bound_buf, bound_data, bound_len);

		lower.val = OidReceiveFunctionCall(subrecv,
										   &bound_buf,
										   ioparam,
										   typmod);
		pfree(bound_buf.data);
	}
	else
		lower.val = (Datum) 0;

	if (RANGE_HAS_UBOUND(flags))
	{
		uint32			 bound_len	= pq_getmsgint(buf, 4);
		const char		*bound_data = pq_getmsgbytes(buf, bound_len);
		StringInfoData	 bound_buf;

		initStringInfo(&bound_buf);
		appendBinaryStringInfo(&bound_buf, bound_data, bound_len);

		upper.val = OidReceiveFunctionCall(subrecv,
										   &bound_buf,
										   ioparam,
										   typmod);
		pfree(bound_buf.data);
	}
	else
		upper.val = (Datum) 0;

	pq_getmsgend(buf);

	lower.rngtypid	= rngtypid;
	lower.infinite	= (flags & RANGE_LB_INF) != 0;
	lower.inclusive = (flags & RANGE_LB_INC) != 0;
	lower.lower		= true;
	upper.rngtypid	= rngtypid;
	upper.infinite	= (flags & RANGE_UB_INF) != 0;
	upper.inclusive = (flags & RANGE_UB_INC) != 0;
	upper.lower		= false;

	/* serialize and canonicalize */
	range = make_range(fcinfo, &lower, &upper, flags & RANGE_EMPTY);

	/*
	 * XXX if the subtype is pass-by-val, we should pfree the upper and
	 * lower bounds here.
	 */

	PG_RETURN_RANGE(range);
}

Datum
range_send(PG_FUNCTION_ARGS)
{
	RangeType	*range = PG_GETARG_RANGE(0);
	StringInfo	 buf   = makeStringInfo();
	char		 flags = 0;
	RangeBound	 lower;
	RangeBound	 upper;
	bool		 empty;
	Oid			 subsend;
	bool		 typIsVarlena;

	RangeTypeInfo rngtypinfo;

	pq_begintypsend(buf);

	range_deserialize(fcinfo, range, &lower, &upper, &empty);

	if (empty)
		flags |= RANGE_EMPTY;

	flags |= (lower.inclusive)	? RANGE_LB_INC  : 0;
	flags |= (lower.infinite)	? RANGE_LB_INF  : 0;
	flags |= (upper.inclusive)	? RANGE_UB_INC  : 0;
	flags |= (upper.infinite)	? RANGE_UB_INF  : 0;

	range_gettypinfo(fcinfo, lower.rngtypid, &rngtypinfo);

	getTypeBinaryOutputInfo(rngtypinfo.subtype,
							&subsend, &typIsVarlena);

	pq_sendbyte(buf, flags);

	if (RANGE_HAS_LBOUND(flags))
	{
		Datum	 bound		= PointerGetDatum(
			OidSendFunctionCall(subsend, lower.val));
		uint32	 bound_len	= VARSIZE(bound) - VARHDRSZ;
		char	*bound_data = VARDATA(bound);

		pq_sendint(buf, bound_len, 4);
		pq_sendbytes(buf, bound_data, bound_len);
	}

	if (RANGE_HAS_UBOUND(flags))
	{
		Datum	 bound		= PointerGetDatum(
			OidSendFunctionCall(subsend, upper.val));
		uint32	 bound_len	= VARSIZE(bound) - VARHDRSZ;
		char	*bound_data = VARDATA(bound);

		pq_sendint(buf, bound_len, 4);
		pq_sendbytes(buf, bound_data, bound_len);
	}

	PG_RETURN_BYTEA_P(pq_endtypsend(buf));
}


/*
 *----------------------------------------------------------
 * GENERIC FUNCTIONS
 *----------------------------------------------------------
 */

Datum
range_constructor0(PG_FUNCTION_ARGS)
{
	Oid			 rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
	RangeType	*range;
	RangeBound	 lower;
	RangeBound	 upper;

	lower.rngtypid	= rngtypid;
	lower.val		= (Datum) 0;
	lower.inclusive = false;
	lower.infinite	= false;
	lower.lower		= true;

	upper.rngtypid	= rngtypid;
	upper.val		= (Datum) 0;
	upper.inclusive = false;
	upper.infinite	= false;
	upper.lower		= false;

	range = DatumGetRangeType(make_range(fcinfo, &lower, &upper, true));

	PG_RETURN_RANGE(range);
}

Datum
range_constructor1(PG_FUNCTION_ARGS)
{
	Datum		 arg1	  = PG_GETARG_DATUM(0);
	Oid			 rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
	RangeType	*range;
	RangeBound	 lower;
	RangeBound	 upper;

	if (PG_ARGISNULL(0))
		ereport(ERROR,
				(errcode(ERRCODE_DATA_EXCEPTION),
				 errmsg("argument must not be NULL")));

	lower.rngtypid	= rngtypid;
	lower.val		= arg1;
	lower.inclusive = true;
	lower.infinite	= false;
	lower.lower		= true;

	upper.rngtypid	= rngtypid;
	upper.val		= arg1;
	upper.inclusive = true;
	upper.infinite	= false;
	upper.lower		= false;

	range = DatumGetRangeType(make_range(fcinfo, &lower, &upper, false));

	PG_RETURN_RANGE(range);
}

Datum
range_constructor2(PG_FUNCTION_ARGS)
{
	Datum		 arg1	  = PG_GETARG_DATUM(0);
	Datum		 arg2	  = PG_GETARG_DATUM(1);
	Oid			 rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
	RangeType	*range;
	RangeBound	 lower;
	RangeBound	 upper;
	char		 flags;

	flags = range_parse_flags(RANGE_DEFAULT_FLAGS);

	lower.rngtypid	= rngtypid;
	lower.val		= PG_ARGISNULL(0) ? (Datum)0 : arg1;
	lower.inclusive = flags & RANGE_LB_INC;
	lower.infinite	= PG_ARGISNULL(0);
	lower.lower		= true;

	upper.rngtypid	= rngtypid;
	upper.val		= PG_ARGISNULL(1) ? (Datum)0 : arg2;
	upper.inclusive = flags & RANGE_UB_INC;
	upper.infinite	= PG_ARGISNULL(1);
	upper.lower		= false;

	range = DatumGetRangeType(make_range(fcinfo, &lower, &upper, false));

	PG_RETURN_RANGE(range);
}

Datum
range_constructor3(PG_FUNCTION_ARGS)
{
	Datum		 arg1	  = PG_GETARG_DATUM(0);
	Datum		 arg2	  = PG_GETARG_DATUM(1);
	Oid			 rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
	RangeType	*range;
	RangeBound	 lower;
	RangeBound	 upper;
	char		 flags;

	if (PG_ARGISNULL(2))
		ereport(ERROR,
				(errcode(ERRCODE_DATA_EXCEPTION),
				 errmsg("flags argument must not be NULL")));
	flags = range_parse_flags(text_to_cstring(PG_GETARG_TEXT_P(2)));

	lower.rngtypid	= rngtypid;
	lower.val		= PG_ARGISNULL(0) ? (Datum)0 : arg1;
	lower.inclusive = flags & RANGE_LB_INC;
	lower.infinite	= PG_ARGISNULL(0);
	lower.lower		= true;

	upper.rngtypid	= rngtypid;
	upper.val		= PG_ARGISNULL(1) ? (Datum)0 : arg2;
	upper.inclusive = flags & RANGE_UB_INC;
	upper.infinite	= PG_ARGISNULL(1);
	upper.lower		= false;

	range = DatumGetRangeType(make_range(fcinfo, &lower, &upper, false));

	PG_RETURN_RANGE(range);
}

/* range -> subtype */
Datum
range_lower(PG_FUNCTION_ARGS)
{
	RangeType	*r1 = PG_GETARG_RANGE(0);
	RangeBound	 lower;
	RangeBound	 upper;
	bool		 empty;

	range_deserialize(fcinfo, r1, &lower, &upper, &empty);

	if (empty)
		ereport(ERROR,
				(errcode(ERRCODE_DATA_EXCEPTION),
				 errmsg("empty range has no lower bound")));
	if (lower.infinite)
		ereport(ERROR,
				(errcode(ERRCODE_DATA_EXCEPTION),
				 errmsg("range lower bound is infinite")));

	PG_RETURN_DATUM(lower.val);
}

Datum
range_upper(PG_FUNCTION_ARGS)
{
	RangeType	*r1 = PG_GETARG_RANGE(0);
	RangeBound	 lower;
	RangeBound	 upper;
	bool		 empty;

	range_deserialize(fcinfo, r1, &lower, &upper, &empty);

	if (empty)
		ereport(ERROR,
				(errcode(ERRCODE_DATA_EXCEPTION),
				 errmsg("empty range has no upper bound")));
	if (upper.infinite)
		ereport(ERROR,
				(errcode(ERRCODE_DATA_EXCEPTION),
				 errmsg("range upper bound is infinite")));

	PG_RETURN_DATUM(upper.val);
}


/* range -> bool */
Datum
range_empty(PG_FUNCTION_ARGS)
{
	RangeType	*r1 = PG_GETARG_RANGE(0);
	RangeBound	 lower;
	RangeBound	 upper;
	bool		 empty;

	range_deserialize(fcinfo, r1, &lower, &upper, &empty);

	PG_RETURN_BOOL(empty);
}

Datum
range_lower_inc(PG_FUNCTION_ARGS)
{
	RangeType	*r1 = PG_GETARG_RANGE(0);
	RangeBound	 lower;
	RangeBound	 upper;
	bool		 empty;

	range_deserialize(fcinfo, r1, &lower, &upper, &empty);

	PG_RETURN_BOOL(lower.inclusive);
}

Datum
range_upper_inc(PG_FUNCTION_ARGS)
{
	RangeType	*r1 = PG_GETARG_RANGE(0);
	RangeBound	 lower;
	RangeBound	 upper;
	bool		 empty;

	range_deserialize(fcinfo, r1, &lower, &upper, &empty);

	PG_RETURN_BOOL(upper.inclusive);
}

Datum
range_lower_inf(PG_FUNCTION_ARGS)
{
	RangeType	*r1 = PG_GETARG_RANGE(0);
	RangeBound	 lower;
	RangeBound	 upper;
	bool		 empty;

	range_deserialize(fcinfo, r1, &lower, &upper, &empty);

	PG_RETURN_BOOL(lower.infinite);
}

Datum
range_upper_inf(PG_FUNCTION_ARGS)
{
	RangeType	*r1 = PG_GETARG_RANGE(0);
	RangeBound	 lower;
	RangeBound	 upper;
	bool		 empty;

	range_deserialize(fcinfo, r1, &lower, &upper, &empty);

	PG_RETURN_BOOL(upper.infinite);
}


/* range, range -> bool */
Datum
range_eq(PG_FUNCTION_ARGS)
{
	RangeType *r1 = PG_GETARG_RANGE(0);
	RangeType *r2 = PG_GETARG_RANGE(1);

	RangeBound	lower1, lower2;
	RangeBound	upper1, upper2;
	bool		empty1, empty2;

	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);

	if (lower1.rngtypid != upper1.rngtypid ||
		lower1.rngtypid != lower2.rngtypid ||
		lower1.rngtypid != upper2.rngtypid)
		elog(ERROR, "range types do not match");

	if (empty1 && empty2)
		PG_RETURN_BOOL(true);
	if (empty1 != empty2)
		PG_RETURN_BOOL(false);

	if (range_cmp_bounds(fcinfo, &lower1, &lower2) != 0)
		PG_RETURN_BOOL(false);

	if (range_cmp_bounds(fcinfo, &upper1, &upper2) != 0)
		PG_RETURN_BOOL(false);

	PG_RETURN_BOOL(true);
}

Datum
range_ne(PG_FUNCTION_ARGS)
{
	bool eq = DatumGetBool(range_eq(fcinfo));

	PG_RETURN_BOOL(!eq);
}

Datum
range_contains_elem(PG_FUNCTION_ARGS)
{
	RangeType	*r1		  = PG_GETARG_RANGE(0);
	RangeType	*r2;
	Datum		 val	  = PG_GETARG_DATUM(1);

	RangeBound	lower1, lower2;
	RangeBound	upper1, upper2;
	bool		empty1;

	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);

	lower2.rngtypid	 = lower1.rngtypid;
	lower2.inclusive = true;
	lower2.infinite	 = false;
	lower2.lower	 = true;
	lower2.val		 = val;

	upper2.rngtypid	 = lower1.rngtypid;
	upper2.inclusive = true;
	upper2.infinite	 = false;
	upper2.lower	 = false;
	upper2.val		 = val;

	r2 = DatumGetRangeType(make_range(fcinfo, &lower2, &upper2, false));

	PG_RETURN_BOOL(range_contains_internal(fcinfo, r1, r2));
}

Datum
range_contains(PG_FUNCTION_ARGS)
{
	RangeType	*r1 = PG_GETARG_RANGE(0);
	RangeType	*r2 = PG_GETARG_RANGE(1);

	PG_RETURN_BOOL(range_contains_internal(fcinfo, r1, r2));
}

Datum
elem_contained_by_range(PG_FUNCTION_ARGS)
{
	RangeType	*r1		  = PG_GETARG_RANGE(1);
	RangeType	*r2;
	Datum		 val	  = PG_GETARG_DATUM(0);

	RangeBound	lower1, lower2;
	RangeBound	upper1, upper2;
	bool		empty1;

	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);

	lower2.rngtypid	 = lower1.rngtypid;
	lower2.inclusive = true;
	lower2.infinite	 = false;
	lower2.lower	 = true;
	lower2.val		 = val;

	upper2.rngtypid	 = lower1.rngtypid;
	upper2.inclusive = true;
	upper2.infinite	 = false;
	upper2.lower	 = false;
	upper2.val		 = val;

	r2 = DatumGetRangeType(make_range(fcinfo, &lower2, &upper2, false));

	PG_RETURN_BOOL(range_contains_internal(fcinfo, r1, r2));
}

Datum
range_contained_by(PG_FUNCTION_ARGS)
{
	RangeType	*r1 = PG_GETARG_RANGE(0);
	RangeType	*r2 = PG_GETARG_RANGE(1);

	PG_RETURN_BOOL(range_contains_internal(fcinfo, r2, r1));
}

Datum
range_before(PG_FUNCTION_ARGS)
{
	RangeType *r1 = PG_GETARG_RANGE(0);
	RangeType *r2 = PG_GETARG_RANGE(1);

	RangeBound	lower1, lower2;
	RangeBound	upper1, upper2;
	bool		empty1, empty2;

	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);

	if (lower1.rngtypid != upper1.rngtypid ||
		lower1.rngtypid != lower2.rngtypid ||
		lower1.rngtypid != upper2.rngtypid)
		elog(ERROR, "range types do not match");

	if (empty1 || empty2)
		ereport(ERROR,
				(errcode(ERRCODE_DATA_EXCEPTION),
				 errmsg("input range is empty")));

	if (range_cmp_bounds(fcinfo, &upper1, &lower2) < 0)
		PG_RETURN_BOOL(true);
	else
		PG_RETURN_BOOL(false);
}

Datum
range_after(PG_FUNCTION_ARGS)
{
	RangeType *r1 = PG_GETARG_RANGE(0);
	RangeType *r2 = PG_GETARG_RANGE(1);

	RangeBound	lower1, lower2;
	RangeBound	upper1, upper2;
	bool		empty1, empty2;

	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);

	if (lower1.rngtypid != upper1.rngtypid ||
		lower1.rngtypid != lower2.rngtypid ||
		lower1.rngtypid != upper2.rngtypid)
		elog(ERROR, "range types do not match");

	if (empty1 || empty2)
		ereport(ERROR,
				(errcode(ERRCODE_DATA_EXCEPTION),
				 errmsg("input range is empty")));

	if (range_cmp_bounds(fcinfo, &lower1, &upper2) > 0)
		PG_RETURN_BOOL(true);
	else
		PG_RETURN_BOOL(false);
}

Datum range_adjacent(PG_FUNCTION_ARGS)
{
	RangeType *r1 = PG_GETARG_RANGE(0);
	RangeType *r2 = PG_GETARG_RANGE(1);

	RangeTypeInfo rngtypinfo;

	RangeBound	lower1, lower2;
	RangeBound	upper1, upper2;
	bool		empty1, empty2;

	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);

	if (lower1.rngtypid != upper1.rngtypid ||
		lower1.rngtypid != lower2.rngtypid ||
		lower1.rngtypid != upper2.rngtypid)
		elog(ERROR, "range types do not match");

	if (empty1 || empty2)
		ereport(ERROR,
				(errcode(ERRCODE_DATA_EXCEPTION),
				 errmsg("undefined for empty ranges")));

	/*
	 * For two ranges to be adjacent, the lower boundary of one range
	 * has to match the upper boundary of the other. However, the
	 * inclusivity of those two boundaries must also be different.
	 *
	 * The semantics for range_cmp_bounds aren't quite what we need
	 * here, so we do the comparison more directly.
	 */

	range_gettypinfo(fcinfo, lower1.rngtypid, &rngtypinfo);

	if (lower1.inclusive != upper2.inclusive)
	{
		if (DatumGetInt32(FunctionCall2Coll(&rngtypinfo.cmpFn,
											rngtypinfo.collation,
											lower1.val, upper2.val)) == 0)
			PG_RETURN_BOOL(true);
	}

	if (upper1.inclusive != lower2.inclusive)
	{
		if (DatumGetInt32(FunctionCall2Coll(&rngtypinfo.cmpFn,
											rngtypinfo.collation,
											upper1.val, lower2.val)) == 0)
			PG_RETURN_BOOL(true);
	}

	PG_RETURN_BOOL(false);
}

Datum
range_overlaps(PG_FUNCTION_ARGS)
{
	RangeType *r1 = PG_GETARG_RANGE(0);
	RangeType *r2 = PG_GETARG_RANGE(1);

	RangeBound	lower1, lower2;
	RangeBound	upper1, upper2;
	bool		empty1, empty2;

	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);

	if (lower1.rngtypid != upper1.rngtypid ||
		lower1.rngtypid != lower2.rngtypid ||
		lower1.rngtypid != upper2.rngtypid)
		elog(ERROR, "range types do not match");

	if (empty1 || empty2)
		PG_RETURN_BOOL(false);

	if (range_cmp_bounds(fcinfo, &lower1, &lower2) >= 0 &&
		range_cmp_bounds(fcinfo, &lower1, &upper2) <= 0)
		PG_RETURN_BOOL(true);

	if (range_cmp_bounds(fcinfo, &lower2, &lower1) >= 0 &&
		range_cmp_bounds(fcinfo, &lower2, &upper1) <= 0)
		PG_RETURN_BOOL(true);

	PG_RETURN_BOOL(false);
}

Datum
range_overleft(PG_FUNCTION_ARGS)
{
	RangeType *r1 = PG_GETARG_RANGE(0);
	RangeType *r2 = PG_GETARG_RANGE(1);

	RangeBound	lower1, lower2;
	RangeBound	upper1, upper2;
	bool		empty1, empty2;

	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);

	if (lower1.rngtypid != upper1.rngtypid ||
		lower1.rngtypid != lower2.rngtypid ||
		lower1.rngtypid != upper2.rngtypid)
		elog(ERROR, "range types do not match");

	if (empty1 || empty2)
		PG_RETURN_BOOL(false);

	if (range_cmp_bounds(fcinfo, &upper1, &upper2) <= 0)
		PG_RETURN_BOOL(true);

	PG_RETURN_BOOL(false);
}

Datum
range_overright(PG_FUNCTION_ARGS)
{
	RangeType *r1 = PG_GETARG_RANGE(0);
	RangeType *r2 = PG_GETARG_RANGE(1);

	RangeBound	lower1, lower2;
	RangeBound	upper1, upper2;
	bool		empty1, empty2;

	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);

	if (lower1.rngtypid != upper1.rngtypid ||
		lower1.rngtypid != lower2.rngtypid ||
		lower1.rngtypid != upper2.rngtypid)
		elog(ERROR, "range types do not match");

	if (empty1 || empty2)
		PG_RETURN_BOOL(false);

	if (range_cmp_bounds(fcinfo, &lower1, &lower2) >= 0)
		PG_RETURN_BOOL(true);

	PG_RETURN_BOOL(false);
}


/* range, range -> range */
Datum
range_minus(PG_FUNCTION_ARGS)
{
	RangeType *r1 = PG_GETARG_RANGE(0);
	RangeType *r2 = PG_GETARG_RANGE(1);

	RangeBound	lower1, lower2;
	RangeBound	upper1, upper2;
	bool		empty1, empty2;

	int cmp_l1l2, cmp_l1u2, cmp_u1l2, cmp_u1u2;

	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);

	if (lower1.rngtypid != upper1.rngtypid ||
		lower1.rngtypid != lower2.rngtypid ||
		lower1.rngtypid != upper2.rngtypid)
		elog(ERROR, "range types do not match");

	if (empty1 || empty2)
		PG_RETURN_RANGE(r1);

	cmp_l1l2 = range_cmp_bounds(fcinfo, &lower1, &lower2);
	cmp_l1u2 = range_cmp_bounds(fcinfo, &lower1, &upper2);
	cmp_u1l2 = range_cmp_bounds(fcinfo, &upper1, &lower2);
	cmp_u1u2 = range_cmp_bounds(fcinfo, &upper1, &upper2);

	if (cmp_l1l2 < 0 && cmp_u1u2 > 0)
		ereport(ERROR,
				(errcode(ERRCODE_DATA_EXCEPTION),
				 errmsg("result range is not contiguous")));

	if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
		PG_RETURN_RANGE(r1);

	if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
		PG_RETURN_RANGE(make_empty_range(fcinfo, lower1.rngtypid));

	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(make_range(fcinfo, &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(make_range(fcinfo, &upper2, &upper1, false));
	}

	elog(ERROR, "unexpected error in range_minus");
	PG_RETURN_VOID();
}

Datum
range_union(PG_FUNCTION_ARGS)
{
	RangeType *r1 = PG_GETARG_RANGE(0);
	RangeType *r2 = PG_GETARG_RANGE(1);

	RangeBound	 lower1, lower2;
	RangeBound	 upper1, upper2;
	bool		 empty1, empty2;
	RangeBound	*result_lower;
	RangeBound	*result_upper;

	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);

	if (empty1)
		PG_RETURN_RANGE(r2);
	if (empty2)
		PG_RETURN_RANGE(r1);

	if (!DatumGetBool(range_overlaps(fcinfo)) &&
		!DatumGetBool(range_adjacent(fcinfo)))
		ereport(ERROR,
				(errcode(ERRCODE_DATA_EXCEPTION),
				 errmsg("result range is not contiguous")));

	if (range_cmp_bounds(fcinfo, &lower1, &lower2) < 0)
		result_lower = &lower1;
	else
		result_lower = &lower2;

	if (range_cmp_bounds(fcinfo, &upper1, &upper2) > 0)
		result_upper = &upper1;
	else
		result_upper = &upper2;

	PG_RETURN_RANGE(make_range(fcinfo, result_lower, result_upper, false));
}

Datum
range_intersect(PG_FUNCTION_ARGS)
{
	RangeType *r1 = PG_GETARG_RANGE(0);
	RangeType *r2 = PG_GETARG_RANGE(1);

	RangeBound	 lower1, lower2;
	RangeBound	 upper1, upper2;
	bool		 empty1, empty2;
	RangeBound	*result_lower;
	RangeBound	*result_upper;

	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);

	if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
		PG_RETURN_RANGE(make_empty_range(fcinfo, lower1.rngtypid));

	if (range_cmp_bounds(fcinfo, &lower1, &lower2) >= 0)
		result_lower = &lower1;
	else
		result_lower = &lower2;

	if (range_cmp_bounds(fcinfo, &upper1, &upper2) <= 0)
		result_upper = &upper1;
	else
		result_upper = &upper2;

	PG_RETURN_RANGE(make_range(fcinfo, result_lower, result_upper, false));
}

/* Btree support */

Datum
range_cmp(PG_FUNCTION_ARGS)
{
	RangeType *r1 = PG_GETARG_RANGE(0);
	RangeType *r2 = PG_GETARG_RANGE(1);

	RangeBound	lower1, lower2;
	RangeBound	upper1, upper2;
	bool		empty1, empty2;

	int cmp;

	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);

	if (lower1.rngtypid != upper1.rngtypid ||
		lower1.rngtypid != lower2.rngtypid ||
		lower1.rngtypid != upper2.rngtypid)
		elog(ERROR, "range types do not match");

	if (empty1 && empty2)
		PG_RETURN_INT32(0);
	else if (empty1)
		PG_RETURN_INT32(-1);
	else if (empty2)
		PG_RETURN_INT32(1);

	if ((cmp = range_cmp_bounds(fcinfo, &lower1, &lower2)) != 0)
		PG_RETURN_INT32(cmp);

	PG_RETURN_INT32(range_cmp_bounds(fcinfo, &upper1, &upper2));
}

Datum
range_lt(PG_FUNCTION_ARGS)
{
	int cmp = range_cmp(fcinfo);
	PG_RETURN_BOOL(cmp < 0);
}

Datum
range_le(PG_FUNCTION_ARGS)
{
	int cmp = range_cmp(fcinfo);
	PG_RETURN_BOOL(cmp <= 0);
}

Datum
range_ge(PG_FUNCTION_ARGS)
{
	int cmp = range_cmp(fcinfo);
	PG_RETURN_BOOL(cmp >= 0);
}

Datum
range_gt(PG_FUNCTION_ARGS)
{
	int cmp = range_cmp(fcinfo);
	PG_RETURN_BOOL(cmp > 0);
}

/* Hash support */
Datum
hash_range(PG_FUNCTION_ARGS)
{
	RangeType	*r			= PG_GETARG_RANGE(0);
	RangeBound	 lower;
	RangeBound	 upper;
	bool		 empty;
	char		 flags		= 0;
	uint32		 lower_hash = 0;
	uint32		 upper_hash = 0;
	uint32		 result		= 0;

	RangeTypeInfo rngtypinfo;

	TypeCacheEntry *typentry;
	Oid subtype;
	FunctionCallInfoData locfcinfo;


	range_deserialize(fcinfo, r, &lower, &upper, &empty);

	if (lower.rngtypid != upper.rngtypid)
		elog(ERROR, "range types do not match");

	if (empty)
		flags |= RANGE_EMPTY;

	flags |= (lower.inclusive)	? RANGE_LB_INC  : 0;
	flags |= (lower.infinite)	? RANGE_LB_INF  : 0;
	flags |= (upper.inclusive)	? RANGE_UB_INC  : 0;
	flags |= (upper.infinite)	? RANGE_UB_INF  : 0;

	range_gettypinfo(fcinfo, lower.rngtypid, &rngtypinfo);
	subtype = rngtypinfo.subtype;

	/*
	 * We arrange to look up the hash function only once per series of
	 * calls, assuming the subtype doesn't change underneath us.  The
	 * typcache is used so that we have no memory leakage when being
	 * used as an index support function.
	 */
	typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
	if (typentry == NULL || typentry->type_id != subtype)
	{
		typentry = lookup_type_cache(subtype, TYPECACHE_HASH_PROC_FINFO);
		if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_FUNCTION),
					 errmsg("could not identify a hash function for type %s",
							format_type_be(subtype))));
		fcinfo->flinfo->fn_extra = (void *) typentry;
	}

	/*
	 * Apply the hash function to each bound (the hash function shouldn't
	 * care about the collation).
	 */
	InitFunctionCallInfoData(locfcinfo, &typentry->hash_proc_finfo, 1,
							 InvalidOid, NULL, NULL);

	if (RANGE_HAS_LBOUND(flags))
	{
		locfcinfo.arg[0]	 = lower.val;
		locfcinfo.argnull[0] = false;
		locfcinfo.isnull	 = false;
		lower_hash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
	}
	if (RANGE_HAS_UBOUND(flags))
	{
		locfcinfo.arg[0]	 = upper.val;
		locfcinfo.argnull[0] = false;
		locfcinfo.isnull	 = false;
		upper_hash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
	}

	result	= hash_uint32((uint32) flags);
	result ^= lower_hash;
	result	= (result << 1) | (result >> 31);
	result ^= upper_hash;

	PG_RETURN_INT32(result);
}

/*
 *----------------------------------------------------------
 * CANONICAL FUNCTIONS
 *
 *   Functions for specific built-in range types.
 *----------------------------------------------------------
 */

Datum
int4range_canonical(PG_FUNCTION_ARGS)
{
	RangeType	*r = PG_GETARG_RANGE(0);

	RangeBound	lower;
	RangeBound	upper;
	bool		empty;

	range_deserialize(fcinfo, r, &lower, &upper, &empty);

	if (empty)
		PG_RETURN_RANGE(r);

	if (!lower.infinite && !lower.inclusive)
	{
		lower.val = DirectFunctionCall2(int4pl, lower.val, Int32GetDatum(1));
		lower.inclusive = true;
	}

	if (!upper.infinite && upper.inclusive)
	{
		upper.val = DirectFunctionCall2(int4pl, upper.val, Int32GetDatum(1));
		upper.inclusive = false;
	}

	PG_RETURN_RANGE(range_serialize(fcinfo, &lower, &upper, false));
}

Datum
int8range_canonical(PG_FUNCTION_ARGS)
{
	RangeType	*r = PG_GETARG_RANGE(0);

	RangeBound	lower;
	RangeBound	upper;
	bool		empty;

	range_deserialize(fcinfo, r, &lower, &upper, &empty);

	if (empty)
		PG_RETURN_RANGE(r);

	if (!lower.infinite && !lower.inclusive)
	{
		lower.val = DirectFunctionCall2(int8pl, lower.val, Int64GetDatum(1));
		lower.inclusive = true;
	}

	if (!upper.infinite && upper.inclusive)
	{
		upper.val = DirectFunctionCall2(int8pl, upper.val, Int64GetDatum(1));
		upper.inclusive = false;
	}

	PG_RETURN_RANGE(range_serialize(fcinfo, &lower, &upper, false));
}

Datum
daterange_canonical(PG_FUNCTION_ARGS)
{
	RangeType	*r = PG_GETARG_RANGE(0);

	RangeBound	lower;
	RangeBound	upper;
	bool		empty;

	range_deserialize(fcinfo, r, &lower, &upper, &empty);

	if (empty)
		PG_RETURN_RANGE(r);

	if (!lower.infinite && !lower.inclusive)
	{
		lower.val = DirectFunctionCall2(date_pli, lower.val, Int32GetDatum(1));
		lower.inclusive = true;
	}

	if (!upper.infinite && upper.inclusive)
	{
		upper.val = DirectFunctionCall2(date_pli, upper.val, Int32GetDatum(1));
		upper.inclusive = false;
	}

	PG_RETURN_RANGE(range_serialize(fcinfo, &lower, &upper, false));
}

/*
 *----------------------------------------------------------
 * SUBTYPE_DIFF FUNCTIONS
 *
 *   Functions for specific built-in range types.
 *----------------------------------------------------------
 */

Datum
int4range_subdiff(PG_FUNCTION_ARGS)
{
	int32 v1 = PG_GETARG_INT32(0);
	int32 v2 = PG_GETARG_INT32(1);

	PG_RETURN_FLOAT8((float8)(v1-v2));
}

Datum
int8range_subdiff(PG_FUNCTION_ARGS)
{
	int64 v1 = PG_GETARG_INT64(0);
	int64 v2 = PG_GETARG_INT64(1);

	PG_RETURN_FLOAT8((float8)(v1-v2));
}

Datum
daterange_subdiff(PG_FUNCTION_ARGS)
{
	int32 v1 = PG_GETARG_INT32(0);
	int32 v2 = PG_GETARG_INT32(1);

	PG_RETURN_FLOAT8((float8)(v1-v2));
}

Datum
numrange_subdiff(PG_FUNCTION_ARGS)
{
	Datum		v1 = PG_GETARG_DATUM(0);
	Datum		v2 = PG_GETARG_DATUM(1);
	Datum		numresult;
	float8		floatresult;

	numresult = DirectFunctionCall2(numeric_sub, v1, v2);

	floatresult = DatumGetFloat8(
		DirectFunctionCall1(numeric_float8, numresult));

	PG_RETURN_FLOAT8(floatresult);
}

Datum
tsrange_subdiff(PG_FUNCTION_ARGS)
{
	Timestamp	v1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	v2 = PG_GETARG_TIMESTAMP(1);
	float8		result;

#ifdef HAVE_INT64_TIMESTAMP
	result = ((float8)(v1-v2)) / USECS_PER_SEC;
#else
	result = timestamp;
#endif

	PG_RETURN_FLOAT8(result);
}

Datum
tstzrange_subdiff(PG_FUNCTION_ARGS)
{
	Timestamp	v1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	v2 = PG_GETARG_TIMESTAMP(1);
	float8		result;

#ifdef HAVE_INT64_TIMESTAMP
	result = ((float8)(v1-v2)) / USECS_PER_SEC;
#else
	result = timestamp;
#endif

	PG_RETURN_FLOAT8(result);
}

/*
 *----------------------------------------------------------
 * SUPPORT FUNCTIONS
 *
 *   These functions aren't in pg_proc, but are useful if
 *   defining new generic range functions in C.
 *----------------------------------------------------------
 */

/*
 * Serialized format is:
 *
 *  4 bytes: Range type Oid
 *  Lower boundary, if any, aligned according to subtype's typalign
 *  Upper boundary, if any, aligned according to subtype's typalign
 *  1 byte for flags
 *
 * This representation is chosen to be compact when the boundary
 * values need to be MAXALIGNed. A palloc chunk always starts out
 * MAXALIGNed, and the first 4 bytes will be the length header (range
 * types are always variable-length), then the next 4 bytes will be
 * the range type Oid. That leaves the first boundary item MAXALIGNed
 * without the need for padding.
 *
 * However, it requires a slightly odd deserialization strategy,
 * because we have to read the flags byte before we know whether to
 * read a boundary value.
 */

/*
 * This serializes a range, but does not canonicalize it. This should
 * only be called by a canonicalization function.
 */
Datum
range_serialize(FunctionCallInfo fcinfo, RangeBound *lower, RangeBound *upper,
				bool empty)
{
	Datum		range;
	size_t		msize;
	Pointer		ptr;
	int16		typlen;
	char		typalign;
	bool		typbyval;
	char		typstorage;
	char		flags	   = 0;

	RangeTypeInfo rngtypinfo;

	if (lower->rngtypid != upper->rngtypid)
		elog(ERROR, "range types do not match");

	range_gettypinfo(fcinfo, lower->rngtypid, &rngtypinfo);

	typlen	   = rngtypinfo.subtyplen;
	typalign   = rngtypinfo.subtypalign;
	typbyval   = rngtypinfo.subtypbyval;
	typstorage = rngtypinfo.subtypstorage;

	if (empty)
		flags |= RANGE_EMPTY;
	else if (range_cmp_bounds(fcinfo, lower, upper) > 0)
		ereport(ERROR,
				(errcode(ERRCODE_DATA_EXCEPTION),
				 errmsg("range lower bound must be less than or equal to range upper bound")));

	flags |= (lower->inclusive) ? RANGE_LB_INC  : 0;
	flags |= (lower->infinite)  ? RANGE_LB_INF  : 0;
	flags |= (upper->inclusive) ? RANGE_UB_INC  : 0;
	flags |= (upper->infinite)  ? RANGE_UB_INF  : 0;

	msize  = VARHDRSZ;
	msize += sizeof(Oid);

	if (RANGE_HAS_LBOUND(flags))
	{
		msize = datum_compute_size(msize, lower->val, typbyval, typalign,
								   typlen, typstorage);
	}

	if (RANGE_HAS_UBOUND(flags))
	{
		msize = datum_compute_size(msize, upper->val, typbyval, typalign,
								   typlen, typstorage);
	}

	msize += sizeof(char);

	ptr = palloc0(msize);
	range = (Datum) ptr;

	ptr += VARHDRSZ;

	memcpy(ptr, &lower->rngtypid, sizeof(Oid));
	ptr += sizeof(Oid);

	if (RANGE_HAS_LBOUND(flags))
	{
		Assert(lower->lower);
		ptr = datum_write(ptr, lower->val, typbyval, typalign, typlen,
						  typstorage);
	}

	if (RANGE_HAS_UBOUND(flags))
	{
		Assert(!upper->lower);
		ptr = datum_write(ptr, upper->val, typbyval, typalign, typlen,
						  typstorage);
	}

	memcpy(ptr, &flags, sizeof(char));
	ptr += sizeof(char);

	SET_VARSIZE(range, msize);
	PG_RETURN_RANGE(range);
}

void
range_deserialize(FunctionCallInfo fcinfo, RangeType *range, RangeBound *lower,
				  RangeBound *upper, bool *empty)
{
	Pointer		ptr	= VARDATA(range);
	char		typalign;
	int16		typlen;
	int16		typbyval;
	char		flags;
	Oid			rngtypid;
	Datum		lbound;
	Datum		ubound;
	Pointer		flags_ptr;

	RangeTypeInfo rngtypinfo;

	memset(lower, 0, sizeof(RangeBound));
	memset(upper, 0, sizeof(RangeBound));

	/* peek at last byte to read the flag byte */
	flags_ptr = ptr + VARSIZE(range) - VARHDRSZ - 1;
	memcpy(&flags, flags_ptr, sizeof(char));

	memcpy(&rngtypid, ptr, sizeof(Oid));
	ptr += sizeof(Oid);

	if (rngtypid == ANYRANGEOID)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("cannot output a value of type anyrange")));

	range_gettypinfo(fcinfo, rngtypid, &rngtypinfo);

	typalign = rngtypinfo.subtypalign;
	typlen	 = rngtypinfo.subtyplen;
	typbyval = rngtypinfo.subtypbyval;

	if (RANGE_HAS_LBOUND(flags))
	{
		ptr = (Pointer) att_align_pointer(ptr, typalign, typlen, ptr);
		lbound = fetch_att(ptr, typbyval, typlen);
		ptr = (Pointer) att_addlength_datum(ptr, typlen, PointerGetDatum(ptr));
		if (typlen == -1)
			lbound = PointerGetDatum(PG_DETOAST_DATUM(lbound));
	}
	else
		lbound = (Datum) 0;

	if (RANGE_HAS_UBOUND(flags))
	{
		ptr = (Pointer) att_align_pointer(ptr, typalign, typlen, ptr);
		ubound = fetch_att(ptr, typbyval, typlen);
		ptr = (Pointer) att_addlength_datum(ptr, typlen, PointerGetDatum(ptr));
		if (typlen == -1)
			ubound = PointerGetDatum(PG_DETOAST_DATUM(ubound));
	}
	else
		ubound = (Datum) 0;

	*empty = flags & RANGE_EMPTY;

	lower->rngtypid	 = rngtypid;
	lower->val		 = lbound;
	lower->inclusive = flags &	RANGE_LB_INC;
	lower->infinite	 = flags &	RANGE_LB_INF;
	lower->lower	 = true;

	upper->rngtypid	 = rngtypid;
	upper->val		 = ubound;
	upper->inclusive = flags &	RANGE_UB_INC;
	upper->infinite	 = flags &	RANGE_UB_INF;
	upper->lower	 = false;
}

/*
 * This both serializes and caonicalizes (if applicable) the
 * range. This should be used by most callers.
 */
Datum
make_range(FunctionCallInfo fcinfo, RangeBound *lower, RangeBound *upper,
		   bool empty)
{
	Datum range;

	RangeTypeInfo rngtypinfo;

	range_gettypinfo(fcinfo, lower->rngtypid, &rngtypinfo);

	if (lower->rngtypid != upper->rngtypid)
		elog(ERROR, "range types do not match");

	range = range_serialize(fcinfo, lower, upper, empty);

	if (rngtypinfo.canonicalFn.fn_addr != NULL)
		range = FunctionCall1(&rngtypinfo.canonicalFn, range);

	PG_RETURN_RANGE(range);
}

int
range_cmp_bounds(FunctionCallInfo fcinfo, RangeBound *b1, RangeBound *b2)
{
	int			result;

	RangeTypeInfo rngtypinfo;

	if (b1->infinite && b2->infinite)
	{
		if (b1->lower == b2->lower)
			return 0;
		else
			return (b1->lower) ? -1 : 1;
	}
	else if (b1->infinite && !b2->infinite)
		return (b1->lower) ? -1 : 1;
	else if (!b1->infinite && b2->infinite)
		return (b2->lower) ? 1 : -1;

	range_gettypinfo(fcinfo, b1->rngtypid, &rngtypinfo);
	result = DatumGetInt32(FunctionCall2Coll(&rngtypinfo.cmpFn,
											 rngtypinfo.collation,
											 b1->val, b2->val));

	if (result == 0)
	{
		if (b1->inclusive && !b2->inclusive)
			return (b2->lower) ? -1 : 1;
		else if (!b1->inclusive && b2->inclusive)
			return (b1->lower) ? 1 : -1;
	}

	return result;
}

RangeType *
make_empty_range(FunctionCallInfo fcinfo, Oid rngtypid)
{
	RangeBound lower;
	RangeBound upper;

	memset(&lower, 0, sizeof(RangeBound));
	memset(&upper, 0, sizeof(RangeBound));

	lower.rngtypid = rngtypid;
	lower.lower = true;
	upper.rngtypid = rngtypid;
	upper.lower = false;

	return DatumGetRangeType(make_range(fcinfo, &lower, &upper, true));
}

/*
 * Fills in rngtypinfo, from a cached copy if available.
 */
void
range_gettypinfo(FunctionCallInfo fcinfo, Oid rngtypid,
				 RangeTypeInfo *rngtypinfo)
{
	RangeTypeInfo *cached = (RangeTypeInfo *) fcinfo->flinfo->fn_extra;

	if (cached == NULL)
	{
		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
													  sizeof(RangeTypeInfo));
		cached = (RangeTypeInfo *) fcinfo->flinfo->fn_extra;
		cached->rngtypid = ~rngtypid;
	}

	if (cached->rngtypid != rngtypid)
	{
		Form_pg_range		pg_range;
		Form_pg_opclass		pg_opclass;
		Form_pg_type		pg_type;
		HeapTuple			tup;

		Oid		subtypeOid;
		Oid		collationOid;
		Oid		canonicalOid;
		Oid		subdiffOid;
		Oid		opclassOid;
		Oid		cmpFnOid;
		Oid		opfamilyOid;
		Oid		opcintype;
		int16	subtyplen;
		char	subtypalign;
		char	subtypstorage;
		bool	subtypbyval;

		/* get information from pg_range */
		tup = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rngtypid));
		if (!HeapTupleIsValid(tup))
			elog(ERROR, "cache lookup failed for range type %u", rngtypid);

		pg_range = (Form_pg_range) GETSTRUCT(tup);

		subtypeOid	 = pg_range->rngsubtype;
		collationOid = pg_range->rngcollation;
		canonicalOid = pg_range->rngcanonical;
		opclassOid	 = pg_range->rngsubopc;
		subdiffOid	 = pg_range->rngsubdiff;

		ReleaseSysCache(tup);

		/* get information from pg_opclass */
		tup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassOid));
		if (!HeapTupleIsValid(tup))
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_OBJECT),
					 errmsg("operator class with OID %u does not exist",
							opclassOid)));

		pg_opclass = (Form_pg_opclass) GETSTRUCT(tup);

		opfamilyOid = pg_opclass->opcfamily;
		opcintype  = pg_opclass->opcintype;

		ReleaseSysCache(tup);

		cmpFnOid = get_opfamily_proc(opfamilyOid, opcintype, opcintype,
									 BTORDER_PROC);

		if (!OidIsValid(cmpFnOid))
			elog(ERROR, "unable to find compare function for operator class %d",
				 opclassOid);

		/* get information from pg_type */
		tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(subtypeOid));
		if (!HeapTupleIsValid(tup))
			elog(ERROR, "cache lookup failed for type %u", subtypeOid);

		pg_type = (Form_pg_type) GETSTRUCT(tup);

		subtyplen	  = pg_type->typlen;
		subtypalign	  = pg_type->typalign;
		subtypstorage = pg_type->typstorage;
		subtypbyval	  = pg_type->typbyval;

		ReleaseSysCache(tup);

		/* set up the cache */

		if (OidIsValid(canonicalOid))
			fmgr_info(canonicalOid, &cached->canonicalFn);
		else
			cached->canonicalFn.fn_addr = NULL;

		if (OidIsValid(subdiffOid))
			fmgr_info(subdiffOid, &cached->subdiffFn);
		else
			cached->subdiffFn.fn_addr = NULL;

		fmgr_info(cmpFnOid, &cached->cmpFn);
		cached->subtype		  = subtypeOid;
		cached->collation	  = collationOid;
		cached->subtyplen	  = subtyplen;
		cached->subtypalign	  = subtypalign;
		cached->subtypstorage = subtypstorage;
		cached->subtypbyval	  = subtypbyval;
		cached->rngtypid	  = rngtypid;
	}

	memcpy(rngtypinfo, cached, sizeof(RangeTypeInfo));
}

/*
 * Given a string representing the flags for the range type, return the flags
 * represented as a char.
 *
 * Exported so that it can be called by DefineRange to check the default flags.
 */
char
range_parse_flags(char *flags_str)
{
	char		 flags = 0;

	if (flags_str[0] == '\0' ||
		flags_str[1] == '\0' ||
		flags_str[2] != '\0')
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("invalid range bound flags"),
				 errhint("Valid values are '[]', '[)', '(]', and '()'.")));

	switch (flags_str[0])
	{
		case '[':
			flags |= RANGE_LB_INC;
			break;
		case '(':
			break;
		default:
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("invalid range bound flags"),
					 errhint("Valid values are '[]', '[)', '(]', and '()'.")));
	}

	switch (flags_str[1])
	{
		case ']':
			flags |= RANGE_UB_INC;
			break;
		case ')':
			break;
		default:
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("invalid range bound flags"),
					 errhint("Valid values are '[]', '[)', '(]', and '()'.")));
	}

	return flags;
}

/*
 *----------------------------------------------------------
 * STATIC FUNCTIONS
 *----------------------------------------------------------
 */

/*
 * Parse range input, modeled after record_in in rowtypes.c.
 *
 *  <range>   := EMPTY
 *	           | <lb-inc> <string>, <string> <ub-inc>
 *  <lb-inc>  := '[' | '('
 *  <ub-inc>  := ']' | ')'
 *
 * Whitespace before or after <range> is ignored. Whitespace within a <string>
 * is taken literally and becomes the input string for that bound.
 *
 * A <string> of length zero is taken as "infinite" (i.e. no bound); unless it
 * is surrounded by double-quotes, in which case it is the literal empty
 * string.
 *
 * Within a <string>, special characters (such as comma, parenthesis, or
 * brackets) can be enclosed in double-quotes or escaped with backslash. Within
 * double-quotes, a double-quote can be escaped with double-quote or backslash.
 */
static void
range_parse(char *string,  char *flags, char **lbound_str,
			char **ubound_str)
{
	char		*ptr = string;
	bool		 infinite;

	*flags = 0;

	/* consume whitespace */
	while (*ptr != '\0' && isspace(*ptr))
		ptr++;

	/* check for empty range */
	if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
					   strlen(RANGE_EMPTY_LITERAL)) == 0)
	{
		*flags = RANGE_EMPTY;

		ptr += strlen(RANGE_EMPTY_LITERAL);

		/* the rest should be whitespace */
		while (*ptr != '\0' && isspace(*ptr))
			ptr++;

		/* should have consumed everything */
		if (*ptr != '\0')
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("malformed range literal: \"%s\"",
							string),
					 errdetail("Unexpected end of input.")));

		return;
	}

	if (*ptr == '[' || *ptr == '(')
	{
		if (*ptr == '[')
			*flags |= RANGE_LB_INC;
		ptr++;
	}
	else
	{
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("malformed range literal: \"%s\"",
						string),
				 errdetail("Missing left parenthesis or bracket.")));
	}

	ptr = range_parse_bound(string, ptr, lbound_str, &infinite);
	if (infinite)
	{
		*flags |= RANGE_LB_INF;
		*flags &= ~RANGE_LB_INC;
	}

	if (*ptr != ',')
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("malformed range literal: \"%s\"",
						string),
				 errdetail("Missing upper bound.")));
	ptr++;

	ptr = range_parse_bound(string, ptr, ubound_str, &infinite);

	if (*ptr == ')' || *ptr == ']')
	{
		if (*ptr == ']')
			*flags |= RANGE_UB_INC;
	}
	else
	{
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("malformed range literal: \"%s\"",
						string),
				 errdetail("Too many boundaries.")));
	}

	ptr++;

	if (infinite)
	{
		*flags |= RANGE_UB_INF;
		*flags &= ~RANGE_UB_INC;
	}

	/* consume whitespace */
	while (*ptr != '\0' && isspace(*ptr))
		ptr++;

	if (*ptr != '\0')
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("malformed range literal: \"%s\"",
						string),
				 errdetail("Junk after right parenthesis or bracket.")));

	return;
}

static char *
range_parse_bound(char *string, char *ptr, char **bound_str, bool *infinite)
{
	StringInfoData		 buf;

	/* Check for null: completely empty input means null */
	if (*ptr == ',' || *ptr == ')' || *ptr == ']')
	{
		*bound_str = NULL;
		*infinite  = true;
	}
	else
	{
		/* Extract string for this column */
		bool		inquote = false;

		initStringInfo(&buf);
		while (inquote || !(*ptr == ',' || *ptr == ')' || *ptr == ']'))
		{
			char		ch = *ptr++;

			if (ch == '\0')
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
						 errmsg("malformed range literal: \"%s\"",
								string),
						 errdetail("Unexpected end of input.")));
			if (ch == '\\')
			{
				if (*ptr == '\0')
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
							 errmsg("malformed range literal: \"%s\"",
									string),
							 errdetail("Unexpected end of input.")));
				appendStringInfoChar(&buf, *ptr++);
			}
			else if (ch == '\"')
			{
				if (!inquote)
					inquote = true;
				else if (*ptr == '\"')
				{
					/* doubled quote within quote sequence */
					appendStringInfoChar(&buf, *ptr++);
				}
				else
					inquote = false;
			}
			else
				appendStringInfoChar(&buf, ch);
		}

		*bound_str = buf.data;
		*infinite  = false;
	}

	return ptr;
}

static char *
range_deparse(char flags, char *lbound_str, char *ubound_str)
{
	StringInfoData	 buf;

	initStringInfo(&buf);

	if (flags & RANGE_EMPTY)
		return pstrdup(RANGE_EMPTY_LITERAL);

	appendStringInfoString(&buf, (flags & RANGE_LB_INC) ? "[" : "(");

	if (RANGE_HAS_LBOUND(flags))
		appendStringInfoString(&buf, range_bound_escape(lbound_str));

	appendStringInfoString(&buf, ",");

	if (RANGE_HAS_UBOUND(flags))
		appendStringInfoString(&buf, range_bound_escape(ubound_str));

	appendStringInfoString(&buf, (flags & RANGE_UB_INC) ? "]" : ")");

	return buf.data;
}

static char *
range_bound_escape(char *value)
{
	bool				 nq;
	char				*tmp;
	StringInfoData		 buf;

	initStringInfo(&buf);

	/* Detect whether we need double quotes for this value */
	nq = (value[0] == '\0');	/* force quotes for empty string */
	for (tmp = value; *tmp; tmp++)
	{
		char		ch = *tmp;

		if (ch == '"' || ch == '\\' ||
			ch == '(' || ch == ')' ||
			ch == '[' || ch == ']' ||
			ch == ',' ||
			isspace((unsigned char) ch))
		{
			nq = true;
			break;
		}
	}

	/* And emit the string */
	if (nq)
		appendStringInfoChar(&buf, '"');
	for (tmp = value; *tmp; tmp++)
	{
		char		ch = *tmp;

		if (ch == '"' || ch == '\\')
			appendStringInfoChar(&buf, ch);
		appendStringInfoChar(&buf, ch);
	}
	if (nq)
		appendStringInfoChar(&buf, '"');

	return buf.data;
}

static bool
range_contains_internal(FunctionCallInfo fcinfo, RangeType *r1, RangeType *r2)
{
	RangeBound	lower1;
	RangeBound	upper1;
	bool		empty1;
	RangeBound	lower2;
	RangeBound	upper2;
	bool		empty2;

	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);

	if (lower1.rngtypid != upper1.rngtypid ||
		lower1.rngtypid != lower2.rngtypid ||
		lower1.rngtypid != upper2.rngtypid)
		elog(ERROR, "range types do not match");

	if (empty2)
		return true;
	else if (empty1)
		return false;

	if (range_cmp_bounds(fcinfo, &lower1, &lower2) > 0)
		return false;
	if (range_cmp_bounds(fcinfo, &upper1, &upper2) < 0)
		return false;

	return true;
}

/*
 * datum_compute_size() and datum_write() are modeled after
 * heap_compute_data_size() and heap_fill_tuple().
 */

static Size
datum_compute_size(Size data_length, Datum val, bool typbyval, char typalign,
				   int16 typlen, char typstorage)
{
	if (TYPE_IS_PACKABLE(typlen, typstorage) &&
		VARATT_CAN_MAKE_SHORT(DatumGetPointer(val)))
	{
		/*
		 * we're anticipating converting to a short varlena header, so
		 * adjust length and don't count any alignment
		 */
		data_length += VARATT_CONVERTED_SHORT_SIZE(DatumGetPointer(val));
	}
	else
	{
		data_length = att_align_datum(data_length, typalign, typlen, val);
		data_length = att_addlength_datum(data_length, typlen, val);
	}

	return data_length;
}

/*
 * Modified version of the code in heap_fill_tuple(). Writes the datum to ptr
 * using the correct alignment, and also uses short varlena header if
 * applicable.
 */
static Pointer
datum_write(Pointer ptr, Datum datum, bool typbyval, char typalign,
			int16 typlen, char typstorage)
{
	Size		data_length;

	if (typbyval)
	{
		/* pass-by-value */
		ptr = (char *) att_align_nominal(ptr, typalign);
		store_att_byval(ptr, datum, typlen);
		data_length = typlen;
	}
	else if (typlen == -1)
	{
		/* varlena */
		Pointer		val = DatumGetPointer(datum);

		if (VARATT_IS_EXTERNAL(val))
		{
			/* no alignment, since it's short by definition */
			data_length = VARSIZE_EXTERNAL(val);
			memcpy(ptr, val, data_length);
		}
		else if (VARATT_IS_SHORT(val))
		{
			/* no alignment for short varlenas */
			data_length = VARSIZE_SHORT(val);
			memcpy(ptr, val, data_length);
		}
		else if (TYPE_IS_PACKABLE(typlen, typstorage) &&
				 VARATT_CAN_MAKE_SHORT(val))
		{
			/* convert to short varlena -- no alignment */
			data_length = VARATT_CONVERTED_SHORT_SIZE(val);
			SET_VARSIZE_SHORT(ptr, data_length);
			memcpy(ptr + 1, VARDATA(val), data_length - 1);
		}
		else
		{
			/* full 4-byte header varlena */
			ptr = (char *) att_align_nominal(ptr, typalign);
			data_length = VARSIZE(val);
			memcpy(ptr, val, data_length);
		}
	}
	else if (typlen == -2)
	{
		/* cstring ... never needs alignment */
		Assert(typalign == 'c');
		data_length = strlen(DatumGetCString(datum)) + 1;
		memcpy(ptr, DatumGetPointer(datum), data_length);
	}
	else
	{
		/* fixed-length pass-by-reference */
		ptr = (char *) att_align_nominal(ptr, typalign);
		Assert(typlen > 0);
		data_length = typlen;
		memcpy(ptr, DatumGetPointer(datum), data_length);
	}

	ptr += data_length;

	return ptr;
}
