From 2792374cff361a7a4ec0e750b5fa935d85afc9ac Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 3 May 2001 19:00:37 +0000
Subject: [PATCH] Ensure that btree sort ordering functions and boolean
 comparison operators give consistent results for all datatypes.  Types
 float4, float8, and numeric were broken for NaN values; abstime, timestamp,
 and interval were broken for INVALID values; timetz was just plain broken
 (some possible pairs of values were neither < nor = nor >).  Also clean up
 text, bpchar, varchar, and bit/varbit to eliminate duplicate code and thereby
 reduce the probability of similar inconsistencies arising in the future.

---
 src/backend/access/nbtree/nbtcompare.c        | 138 ++----------
 src/backend/utils/adt/date.c                  |  46 ++--
 src/backend/utils/adt/float.c                 | 149 +++++++++---
 src/backend/utils/adt/nabstime.c              | 137 +++++------
 src/backend/utils/adt/numeric.c               | 156 ++++---------
 src/backend/utils/adt/timestamp.c             | 212 +++++-------------
 src/backend/utils/adt/varbit.c                |  76 +++----
 src/backend/utils/adt/varchar.c               |  15 +-
 src/backend/utils/adt/varlena.c               | 132 +++++------
 src/include/utils/nabstime.h                  |   3 +-
 .../regress/expected/abstime-solaris-1947.out |   6 +-
 src/test/regress/expected/abstime.out         |   6 +-
 src/test/regress/expected/timestamp.out       |  15 +-
 13 files changed, 440 insertions(+), 651 deletions(-)

diff --git a/src/backend/access/nbtree/nbtcompare.c b/src/backend/access/nbtree/nbtcompare.c
index fc85906d9b2..b1affe20188 100644
--- a/src/backend/access/nbtree/nbtcompare.c
+++ b/src/backend/access/nbtree/nbtcompare.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtcompare.c,v 1.41 2001/03/22 03:59:14 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtcompare.c,v 1.42 2001/05/03 19:00:36 tgl Exp $
  *
  * NOTES
  *
@@ -25,7 +25,20 @@
  *	NOTE: although any negative int32 is acceptable for reporting "<",
  *	and any positive int32 is acceptable for reporting ">", routines
  *	that work on 32-bit or wider datatypes can't just return "a - b".
- *	That could overflow and give the wrong answer.
+ *	That could overflow and give the wrong answer.  Also, one should not
+ *	return INT_MIN to report "<", since some callers will negate the result.
+ *
+ *	NOTE: it is critical that the comparison function impose a total order
+ *	on all non-NULL values of the data type, and that the datatype's
+ *	boolean comparison operators (= < >= etc) yield results consistent
+ *	with the comparison routine.  Otherwise bad behavior may ensue.
+ *	(For example, the comparison operators must NOT punt when faced with
+ *	NAN or other funny values; you must devise some collation sequence for
+ *	all such values.)  If the datatype is not trivial, this is most
+ *	reliably done by having the boolean operators invoke the same
+ *	three-way comparison code that the btree function does.  Therefore,
+ *	this file contains only btree support for "trivial" datatypes ---
+ *	all others are in the /utils/adt/ files that implement their datatypes.
  *
  *	NOTE: these routines must not leak memory, since memory allocated
  *	during an index access won't be recovered till end of query.  This
@@ -33,12 +46,11 @@
  *	they have to be careful to free any detoasted copy of an input datum.
  *-------------------------------------------------------------------------
  */
-
 #include "postgres.h"
 
-#include "utils/nabstime.h"
 #include "utils/builtins.h"
 
+
 Datum
 btboolcmp(PG_FUNCTION_ARGS)
 {
@@ -85,34 +97,6 @@ btint8cmp(PG_FUNCTION_ARGS)
 		PG_RETURN_INT32(-1);
 }
 
-Datum
-btfloat4cmp(PG_FUNCTION_ARGS)
-{
-	float4		a = PG_GETARG_FLOAT4(0);
-	float4		b = PG_GETARG_FLOAT4(1);
-
-	if (a > b)
-		PG_RETURN_INT32(1);
-	else if (a == b)
-		PG_RETURN_INT32(0);
-	else
-		PG_RETURN_INT32(-1);
-}
-
-Datum
-btfloat8cmp(PG_FUNCTION_ARGS)
-{
-	float8		a = PG_GETARG_FLOAT8(0);
-	float8		b = PG_GETARG_FLOAT8(1);
-
-	if (a > b)
-		PG_RETURN_INT32(1);
-	else if (a == b)
-		PG_RETURN_INT32(0);
-	else
-		PG_RETURN_INT32(-1);
-}
-
 Datum
 btoidcmp(PG_FUNCTION_ARGS)
 {
@@ -147,20 +131,6 @@ btoidvectorcmp(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(0);
 }
 
-Datum
-btabstimecmp(PG_FUNCTION_ARGS)
-{
-	AbsoluteTime a = PG_GETARG_ABSOLUTETIME(0);
-	AbsoluteTime b = PG_GETARG_ABSOLUTETIME(1);
-
-	if (AbsoluteTimeIsBefore(a, b))
-		PG_RETURN_INT32(-1);
-	else if (AbsoluteTimeIsBefore(b, a))
-		PG_RETURN_INT32(1);
-	else
-		PG_RETURN_INT32(0);
-}
-
 Datum
 btcharcmp(PG_FUNCTION_ARGS)
 {
@@ -179,79 +149,3 @@ btnamecmp(PG_FUNCTION_ARGS)
 
 	PG_RETURN_INT32(strncmp(NameStr(*a), NameStr(*b), NAMEDATALEN));
 }
-
-Datum
-bttextcmp(PG_FUNCTION_ARGS)
-{
-	text	   *a = PG_GETARG_TEXT_P(0);
-	text	   *b = PG_GETARG_TEXT_P(1);
-	int			res;
-	unsigned char *ap,
-			   *bp;
-
-#ifdef USE_LOCALE
-	int			la = VARSIZE(a) - VARHDRSZ;
-	int			lb = VARSIZE(b) - VARHDRSZ;
-
-	ap = (unsigned char *) palloc(la + 1);
-	bp = (unsigned char *) palloc(lb + 1);
-
-	memcpy(ap, VARDATA(a), la);
-	*(ap + la) = '\0';
-	memcpy(bp, VARDATA(b), lb);
-	*(bp + lb) = '\0';
-
-	res = strcoll(ap, bp);
-
-	pfree(ap);
-	pfree(bp);
-
-#else
-	int			len = VARSIZE(a);
-
-	/* len is the length of the shorter of the two strings */
-	if (len > VARSIZE(b))
-		len = VARSIZE(b);
-
-	len -= VARHDRSZ;
-
-	ap = (unsigned char *) VARDATA(a);
-	bp = (unsigned char *) VARDATA(b);
-
-	/*
-	 * If the two strings differ in the first len bytes, or if they're the
-	 * same in the first len bytes and they're both len bytes long, we're
-	 * done.
-	 */
-
-	res = 0;
-	if (len > 0)
-	{
-		do
-		{
-			res = (int) *ap++ - (int) *bp++;
-			len--;
-		} while (res == 0 && len != 0);
-	}
-
-	if (res == 0 && VARSIZE(a) != VARSIZE(b))
-	{
-
-		/*
-		 * The two strings are the same in the first len bytes, and they
-		 * are of different lengths.
-		 */
-		if (VARSIZE(a) < VARSIZE(b))
-			res = -1;
-		else
-			res = 1;
-	}
-
-#endif
-
-	/* Avoid leaking memory when handed toasted input. */
-	PG_FREE_IF_COPY(a, 0);
-	PG_FREE_IF_COPY(b, 1);
-
-	PG_RETURN_INT32(res);
-}
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 5e7d3c92f2e..4504f5f043c 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.56 2001/03/22 03:59:49 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.57 2001/05/03 19:00:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -899,13 +899,35 @@ timetz_out(PG_FUNCTION_ARGS)
 }
 
 
+static int
+timetz_cmp_internal(TimeTzADT *time1, TimeTzADT *time2)
+{
+	double		t1,
+				t2;
+
+	/* Primary sort is by true (GMT-equivalent) time */
+	t1 = time1->time + time1->zone;
+	t2 = time2->time + time2->zone;
+
+	if (t1 > t2)
+		return 1;
+	if (t1 < t2)
+		return -1;
+
+	/*
+	 * If same GMT time, sort by timezone; we only want to say that two
+	 * timetz's are equal if both the time and zone parts are equal.
+	 */
+	return time1->zone - time2->zone;
+}
+
 Datum
 timetz_eq(PG_FUNCTION_ARGS)
 {
 	TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
 	TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
 
-	PG_RETURN_BOOL(((time1->time + time1->zone) == (time2->time + time2->zone)));
+	PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) == 0);
 }
 
 Datum
@@ -914,7 +936,7 @@ timetz_ne(PG_FUNCTION_ARGS)
 	TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
 	TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
 
-	PG_RETURN_BOOL(((time1->time + time1->zone) != (time2->time + time2->zone)));
+	PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) != 0);
 }
 
 Datum
@@ -923,7 +945,7 @@ timetz_lt(PG_FUNCTION_ARGS)
 	TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
 	TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
 
-	PG_RETURN_BOOL(((time1->time + time1->zone) < (time2->time + time2->zone)));
+	PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) < 0);
 }
 
 Datum
@@ -932,7 +954,7 @@ timetz_le(PG_FUNCTION_ARGS)
 	TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
 	TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
 
-	PG_RETURN_BOOL(((time1->time + time1->zone) <= (time2->time + time2->zone)));
+	PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) <= 0);
 }
 
 Datum
@@ -941,7 +963,7 @@ timetz_gt(PG_FUNCTION_ARGS)
 	TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
 	TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
 
-	PG_RETURN_BOOL(((time1->time + time1->zone) > (time2->time + time2->zone)));
+	PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) > 0);
 }
 
 Datum
@@ -950,7 +972,7 @@ timetz_ge(PG_FUNCTION_ARGS)
 	TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
 	TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
 
-	PG_RETURN_BOOL(((time1->time + time1->zone) >= (time2->time + time2->zone)));
+	PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) >= 0);
 }
 
 Datum
@@ -959,15 +981,7 @@ timetz_cmp(PG_FUNCTION_ARGS)
 	TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
 	TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
 
-	if (DatumGetBool(DirectFunctionCall2(timetz_lt,
-										 TimeTzADTPGetDatum(time1),
-										 TimeTzADTPGetDatum(time2))))
-		PG_RETURN_INT32(-1);
-	if (DatumGetBool(DirectFunctionCall2(timetz_gt,
-										 TimeTzADTPGetDatum(time1),
-										 TimeTzADTPGetDatum(time2))))
-		PG_RETURN_INT32(1);
-	PG_RETURN_INT32(0);
+	PG_RETURN_INT32(timetz_cmp_internal(time1, time2));
 }
 
 /*
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 7a83ee6577e..06405d0cee1 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -8,11 +8,11 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/float.c,v 1.70 2001/03/22 03:59:50 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/float.c,v 1.71 2001/05/03 19:00:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-/*
+/*----------
  * OLD COMMENTS
  *		Basic float4 ops:
  *		 float4in, float4out, float4abs, float4um
@@ -22,8 +22,8 @@
  *		 float4pl, float4mi, float4mul, float4div
  *		 float8pl, float8mi, float8mul, float8div
  *		Comparison operators:
- *		 float4eq, float4ne, float4lt, float4le, float4gt, float4ge
- *		 float8eq, float8ne, float8lt, float8le, float8gt, float8ge
+ *		 float4eq, float4ne, float4lt, float4le, float4gt, float4ge, float4cmp
+ *		 float8eq, float8ne, float8lt, float8le, float8gt, float8ge, float8cmp
  *		Conversion routines:
  *		 ftod, dtof, i4tod, dtoi4, i2tod, dtoi2, itof, ftoi, i2tof, ftoi2
  *
@@ -37,7 +37,8 @@
  *		 float84eq, float84ne, float84lt, float84le, float84gt, float84ge
  *
  *		(You can do the arithmetic and comparison stuff using conversion
- *		 routines, but then you pay the overhead of converting...)
+ *		 routines, but then you pay the overhead of invoking a separate
+ *		 conversion function...)
  *
  * XXX GLUESOME STUFF. FIX IT! -AY '94
  *
@@ -45,14 +46,15 @@
  *		 a bit of the existing code. Need to change the error checking
  *		 for calls to pow(), exp() since on some machines (my Linux box
  *		 included) these routines do not set errno. - tgl 97/05/10
+ *----------
  */
+#include "postgres.h"
+
 #include <ctype.h>
 #include <errno.h>
 #include <float.h>				/* faked on sunos4 */
 #include <math.h>
 
-#include "postgres.h"
-
 #include <limits.h>
 /* for finite() on Solaris */
 #ifdef HAVE_IEEEFP_H
@@ -197,7 +199,7 @@ float4in(PG_FUNCTION_ARGS)
 	val = strtod(num, &endptr);
 	if (*endptr != '\0')
 	{
-		/* Should we accept "NaN" or "Infinity" for float4? */
+		/* Shouldn't we accept "NaN" or "Infinity" for float4? */
 		elog(ERROR, "Bad float4 input format '%s'", num);
 	}
 	else
@@ -225,6 +227,11 @@ float4out(PG_FUNCTION_ARGS)
 	float4		num = PG_GETARG_FLOAT4(0);
 	char	   *ascii = (char *) palloc(MAXFLOATWIDTH + 1);
 
+	if (isnan(num))
+		PG_RETURN_CSTRING(strcpy(ascii, "NaN"));
+	if (isinf(num))
+		PG_RETURN_CSTRING(strcpy(ascii, "Infinity"));
+
 	sprintf(ascii, "%.*g", FLT_DIG, num);
 	PG_RETURN_CSTRING(ascii);
 }
@@ -536,13 +543,43 @@ float8div(PG_FUNCTION_ARGS)
 /*
  *		float4{eq,ne,lt,le,gt,ge}		- float4/float4 comparison operations
  */
+static int
+float4_cmp_internal(float4 a, float4 b)
+{
+	/*
+	 * We consider all NANs to be equal and larger than any non-NAN.
+	 * This is somewhat arbitrary; the important thing is to have a
+	 * consistent sort order.
+	 */
+	if (isnan(a))
+	{
+		if (isnan(b))
+			return 0;			/* NAN = NAN */
+		else
+			return 1;			/* NAN > non-NAN */
+	}
+	else if (isnan(b))
+	{
+		return -1;				/* non-NAN < NAN */
+	}
+	else
+	{
+		if (a > b)
+			return 1;
+		else if (a == b)
+			return 0;
+		else
+			return -1;
+	}
+}
+
 Datum
 float4eq(PG_FUNCTION_ARGS)
 {
 	float4		arg1 = PG_GETARG_FLOAT4(0);
 	float4		arg2 = PG_GETARG_FLOAT4(1);
 
-	PG_RETURN_BOOL(arg1 == arg2);
+	PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) == 0);
 }
 
 Datum
@@ -551,7 +588,7 @@ float4ne(PG_FUNCTION_ARGS)
 	float4		arg1 = PG_GETARG_FLOAT4(0);
 	float4		arg2 = PG_GETARG_FLOAT4(1);
 
-	PG_RETURN_BOOL(arg1 != arg2);
+	PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) != 0);
 }
 
 Datum
@@ -560,7 +597,7 @@ float4lt(PG_FUNCTION_ARGS)
 	float4		arg1 = PG_GETARG_FLOAT4(0);
 	float4		arg2 = PG_GETARG_FLOAT4(1);
 
-	PG_RETURN_BOOL(arg1 < arg2);
+	PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) < 0);
 }
 
 Datum
@@ -569,7 +606,7 @@ float4le(PG_FUNCTION_ARGS)
 	float4		arg1 = PG_GETARG_FLOAT4(0);
 	float4		arg2 = PG_GETARG_FLOAT4(1);
 
-	PG_RETURN_BOOL(arg1 <= arg2);
+	PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) <= 0);
 }
 
 Datum
@@ -578,7 +615,7 @@ float4gt(PG_FUNCTION_ARGS)
 	float4		arg1 = PG_GETARG_FLOAT4(0);
 	float4		arg2 = PG_GETARG_FLOAT4(1);
 
-	PG_RETURN_BOOL(arg1 > arg2);
+	PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) > 0);
 }
 
 Datum
@@ -587,19 +624,58 @@ float4ge(PG_FUNCTION_ARGS)
 	float4		arg1 = PG_GETARG_FLOAT4(0);
 	float4		arg2 = PG_GETARG_FLOAT4(1);
 
-	PG_RETURN_BOOL(arg1 >= arg2);
+	PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) >= 0);
+}
+
+Datum
+btfloat4cmp(PG_FUNCTION_ARGS)
+{
+	float4		arg1 = PG_GETARG_FLOAT4(0);
+	float4		arg2 = PG_GETARG_FLOAT4(1);
+
+	PG_RETURN_INT32(float4_cmp_internal(arg1, arg2));
 }
 
 /*
  *		float8{eq,ne,lt,le,gt,ge}		- float8/float8 comparison operations
  */
+static int
+float8_cmp_internal(float8 a, float8 b)
+{
+	/*
+	 * We consider all NANs to be equal and larger than any non-NAN.
+	 * This is somewhat arbitrary; the important thing is to have a
+	 * consistent sort order.
+	 */
+	if (isnan(a))
+	{
+		if (isnan(b))
+			return 0;			/* NAN = NAN */
+		else
+			return 1;			/* NAN > non-NAN */
+	}
+	else if (isnan(b))
+	{
+		return -1;				/* non-NAN < NAN */
+	}
+	else
+	{
+		if (a > b)
+			return 1;
+		else if (a == b)
+			return 0;
+		else
+			return -1;
+	}
+}
+
 Datum
 float8eq(PG_FUNCTION_ARGS)
 {
 	float8		arg1 = PG_GETARG_FLOAT8(0);
 	float8		arg2 = PG_GETARG_FLOAT8(1);
 
-	PG_RETURN_BOOL(arg1 == arg2);
+	PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) == 0);
 }
 
 Datum
@@ -608,7 +684,7 @@ float8ne(PG_FUNCTION_ARGS)
 	float8		arg1 = PG_GETARG_FLOAT8(0);
 	float8		arg2 = PG_GETARG_FLOAT8(1);
 
-	PG_RETURN_BOOL(arg1 != arg2);
+	PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) != 0);
 }
 
 Datum
@@ -617,7 +693,7 @@ float8lt(PG_FUNCTION_ARGS)
 	float8		arg1 = PG_GETARG_FLOAT8(0);
 	float8		arg2 = PG_GETARG_FLOAT8(1);
 
-	PG_RETURN_BOOL(arg1 < arg2);
+	PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) < 0);
 }
 
 Datum
@@ -626,7 +702,7 @@ float8le(PG_FUNCTION_ARGS)
 	float8		arg1 = PG_GETARG_FLOAT8(0);
 	float8		arg2 = PG_GETARG_FLOAT8(1);
 
-	PG_RETURN_BOOL(arg1 <= arg2);
+	PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) <= 0);
 }
 
 Datum
@@ -635,7 +711,7 @@ float8gt(PG_FUNCTION_ARGS)
 	float8		arg1 = PG_GETARG_FLOAT8(0);
 	float8		arg2 = PG_GETARG_FLOAT8(1);
 
-	PG_RETURN_BOOL(arg1 > arg2);
+	PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) > 0);
 }
 
 Datum
@@ -644,7 +720,16 @@ float8ge(PG_FUNCTION_ARGS)
 	float8		arg1 = PG_GETARG_FLOAT8(0);
 	float8		arg2 = PG_GETARG_FLOAT8(1);
 
-	PG_RETURN_BOOL(arg1 >= arg2);
+	PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) >= 0);
+}
+
+Datum
+btfloat8cmp(PG_FUNCTION_ARGS)
+{
+	float8		arg1 = PG_GETARG_FLOAT8(0);
+	float8		arg2 = PG_GETARG_FLOAT8(1);
+
+	PG_RETURN_INT32(float8_cmp_internal(arg1, arg2));
 }
 
 
@@ -1650,7 +1735,7 @@ float48eq(PG_FUNCTION_ARGS)
 	float4		arg1 = PG_GETARG_FLOAT4(0);
 	float8		arg2 = PG_GETARG_FLOAT8(1);
 
-	PG_RETURN_BOOL(arg1 == arg2);
+	PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) == 0);
 }
 
 Datum
@@ -1659,7 +1744,7 @@ float48ne(PG_FUNCTION_ARGS)
 	float4		arg1 = PG_GETARG_FLOAT4(0);
 	float8		arg2 = PG_GETARG_FLOAT8(1);
 
-	PG_RETURN_BOOL(arg1 != arg2);
+	PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) != 0);
 }
 
 Datum
@@ -1668,7 +1753,7 @@ float48lt(PG_FUNCTION_ARGS)
 	float4		arg1 = PG_GETARG_FLOAT4(0);
 	float8		arg2 = PG_GETARG_FLOAT8(1);
 
-	PG_RETURN_BOOL(arg1 < arg2);
+	PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) < 0);
 }
 
 Datum
@@ -1677,7 +1762,7 @@ float48le(PG_FUNCTION_ARGS)
 	float4		arg1 = PG_GETARG_FLOAT4(0);
 	float8		arg2 = PG_GETARG_FLOAT8(1);
 
-	PG_RETURN_BOOL(arg1 <= arg2);
+	PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) <= 0);
 }
 
 Datum
@@ -1686,7 +1771,7 @@ float48gt(PG_FUNCTION_ARGS)
 	float4		arg1 = PG_GETARG_FLOAT4(0);
 	float8		arg2 = PG_GETARG_FLOAT8(1);
 
-	PG_RETURN_BOOL(arg1 > arg2);
+	PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) > 0);
 }
 
 Datum
@@ -1695,7 +1780,7 @@ float48ge(PG_FUNCTION_ARGS)
 	float4		arg1 = PG_GETARG_FLOAT4(0);
 	float8		arg2 = PG_GETARG_FLOAT8(1);
 
-	PG_RETURN_BOOL(arg1 >= arg2);
+	PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) >= 0);
 }
 
 /*
@@ -1707,7 +1792,7 @@ float84eq(PG_FUNCTION_ARGS)
 	float8		arg1 = PG_GETARG_FLOAT8(0);
 	float4		arg2 = PG_GETARG_FLOAT4(1);
 
-	PG_RETURN_BOOL(arg1 == arg2);
+	PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) == 0);
 }
 
 Datum
@@ -1716,7 +1801,7 @@ float84ne(PG_FUNCTION_ARGS)
 	float8		arg1 = PG_GETARG_FLOAT8(0);
 	float4		arg2 = PG_GETARG_FLOAT4(1);
 
-	PG_RETURN_BOOL(arg1 != arg2);
+	PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) != 0);
 }
 
 Datum
@@ -1725,7 +1810,7 @@ float84lt(PG_FUNCTION_ARGS)
 	float8		arg1 = PG_GETARG_FLOAT8(0);
 	float4		arg2 = PG_GETARG_FLOAT4(1);
 
-	PG_RETURN_BOOL(arg1 < arg2);
+	PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) < 0);
 }
 
 Datum
@@ -1734,7 +1819,7 @@ float84le(PG_FUNCTION_ARGS)
 	float8		arg1 = PG_GETARG_FLOAT8(0);
 	float4		arg2 = PG_GETARG_FLOAT4(1);
 
-	PG_RETURN_BOOL(arg1 <= arg2);
+	PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) <= 0);
 }
 
 Datum
@@ -1743,7 +1828,7 @@ float84gt(PG_FUNCTION_ARGS)
 	float8		arg1 = PG_GETARG_FLOAT8(0);
 	float4		arg2 = PG_GETARG_FLOAT4(1);
 
-	PG_RETURN_BOOL(arg1 > arg2);
+	PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) > 0);
 }
 
 Datum
@@ -1752,7 +1837,7 @@ float84ge(PG_FUNCTION_ARGS)
 	float8		arg1 = PG_GETARG_FLOAT8(0);
 	float4		arg2 = PG_GETARG_FLOAT4(1);
 
-	PG_RETURN_BOOL(arg1 >= arg2);
+	PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) >= 0);
 }
 
 /* ========== PRIVATE ROUTINES ========== */
diff --git a/src/backend/utils/adt/nabstime.c b/src/backend/utils/adt/nabstime.c
index 69ae1f17027..0f8ed87e5ba 100644
--- a/src/backend/utils/adt/nabstime.c
+++ b/src/backend/utils/adt/nabstime.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.84 2001/04/26 21:52:17 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.85 2001/05/03 19:00:36 tgl Exp $
  *
  * NOTES
  *
@@ -430,37 +430,6 @@ nabstimeout(PG_FUNCTION_ARGS)
 }
 
 
-/*
- *	AbsoluteTimeIsBefore -- true iff time1 is before time2.
- *	AbsoluteTimeIsAfter -- true iff time1 is after time2.
- */
-bool
-AbsoluteTimeIsBefore(AbsoluteTime time1, AbsoluteTime time2)
-{
-	if (time1 == CURRENT_ABSTIME)
-		time1 = GetCurrentTransactionStartTime();
-
-	if (time2 == CURRENT_ABSTIME)
-		time2 = GetCurrentTransactionStartTime();
-
-	return time1 < time2;
-}
-
-#ifdef NOT_USED
-bool
-AbsoluteTimeIsAfter(AbsoluteTime time1, AbsoluteTime time2)
-{
-	if (time1 == CURRENT_ABSTIME)
-		time1 = GetCurrentTransactionStartTime();
-
-	if (time2 == CURRENT_ABSTIME)
-		time2 = GetCurrentTransactionStartTime();
-
-	return time1 > time2;
-}
-
-#endif
-
 /* abstime_finite()
  */
 Datum
@@ -475,27 +444,51 @@ abstime_finite(PG_FUNCTION_ARGS)
 
 
 /*
- *		abstimeeq		- returns true iff arguments are equal
- *		abstimene		- returns true iff arguments are not equal
- *		abstimelt		- returns true iff t1 less than t2
- *		abstimegt		- returns true iff t1 greater than t2
- *		abstimele		- returns true iff t1 less than or equal to t2
- *		abstimege		- returns true iff t1 greater than or equal to t2
+ * abstime comparison routines
  */
+static int
+abstime_cmp_internal(AbsoluteTime a, AbsoluteTime b)
+{
+	/*
+	 * We consider all INVALIDs to be equal and larger than any non-INVALID.
+	 * This is somewhat arbitrary; the important thing is to have a
+	 * consistent sort order.
+	 */
+	if (a == INVALID_ABSTIME)
+	{
+		if (b == INVALID_ABSTIME)
+			return 0;			/* INVALID = INVALID */
+		else
+			return 1;			/* INVALID > non-INVALID */
+	}
+	else if (b == INVALID_ABSTIME)
+	{
+		return -1;				/* non-INVALID < INVALID */
+	}
+	else
+	{
+		/* XXX this is broken, should go away: */
+		if (a == CURRENT_ABSTIME)
+			a = GetCurrentTransactionStartTime();
+		if (b == CURRENT_ABSTIME)
+			b = GetCurrentTransactionStartTime();
+
+		if (a > b)
+			return 1;
+		else if (a == b)
+			return 0;
+		else
+			return -1;
+	}
+}
+
 Datum
 abstimeeq(PG_FUNCTION_ARGS)
 {
 	AbsoluteTime t1 = PG_GETARG_ABSOLUTETIME(0);
 	AbsoluteTime t2 = PG_GETARG_ABSOLUTETIME(1);
 
-	if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
-		PG_RETURN_BOOL(false);
-	if (t1 == CURRENT_ABSTIME)
-		t1 = GetCurrentTransactionStartTime();
-	if (t2 == CURRENT_ABSTIME)
-		t2 = GetCurrentTransactionStartTime();
-
-	PG_RETURN_BOOL(t1 == t2);
+	PG_RETURN_BOOL(abstime_cmp_internal(t1, t2) == 0);
 }
 
 Datum
@@ -504,14 +497,7 @@ abstimene(PG_FUNCTION_ARGS)
 	AbsoluteTime t1 = PG_GETARG_ABSOLUTETIME(0);
 	AbsoluteTime t2 = PG_GETARG_ABSOLUTETIME(1);
 
-	if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
-		PG_RETURN_BOOL(false);
-	if (t1 == CURRENT_ABSTIME)
-		t1 = GetCurrentTransactionStartTime();
-	if (t2 == CURRENT_ABSTIME)
-		t2 = GetCurrentTransactionStartTime();
-
-	PG_RETURN_BOOL(t1 != t2);
+	PG_RETURN_BOOL(abstime_cmp_internal(t1, t2) != 0);
 }
 
 Datum
@@ -520,14 +506,7 @@ abstimelt(PG_FUNCTION_ARGS)
 	AbsoluteTime t1 = PG_GETARG_ABSOLUTETIME(0);
 	AbsoluteTime t2 = PG_GETARG_ABSOLUTETIME(1);
 
-	if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
-		PG_RETURN_BOOL(false);
-	if (t1 == CURRENT_ABSTIME)
-		t1 = GetCurrentTransactionStartTime();
-	if (t2 == CURRENT_ABSTIME)
-		t2 = GetCurrentTransactionStartTime();
-
-	PG_RETURN_BOOL(t1 < t2);
+	PG_RETURN_BOOL(abstime_cmp_internal(t1, t2) < 0);
 }
 
 Datum
@@ -536,14 +515,7 @@ abstimegt(PG_FUNCTION_ARGS)
 	AbsoluteTime t1 = PG_GETARG_ABSOLUTETIME(0);
 	AbsoluteTime t2 = PG_GETARG_ABSOLUTETIME(1);
 
-	if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
-		PG_RETURN_BOOL(false);
-	if (t1 == CURRENT_ABSTIME)
-		t1 = GetCurrentTransactionStartTime();
-	if (t2 == CURRENT_ABSTIME)
-		t2 = GetCurrentTransactionStartTime();
-
-	PG_RETURN_BOOL(t1 > t2);
+	PG_RETURN_BOOL(abstime_cmp_internal(t1, t2) > 0);
 }
 
 Datum
@@ -552,14 +524,7 @@ abstimele(PG_FUNCTION_ARGS)
 	AbsoluteTime t1 = PG_GETARG_ABSOLUTETIME(0);
 	AbsoluteTime t2 = PG_GETARG_ABSOLUTETIME(1);
 
-	if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
-		PG_RETURN_BOOL(false);
-	if (t1 == CURRENT_ABSTIME)
-		t1 = GetCurrentTransactionStartTime();
-	if (t2 == CURRENT_ABSTIME)
-		t2 = GetCurrentTransactionStartTime();
-
-	PG_RETURN_BOOL(t1 <= t2);
+	PG_RETURN_BOOL(abstime_cmp_internal(t1, t2) <= 0);
 }
 
 Datum
@@ -568,14 +533,16 @@ abstimege(PG_FUNCTION_ARGS)
 	AbsoluteTime t1 = PG_GETARG_ABSOLUTETIME(0);
 	AbsoluteTime t2 = PG_GETARG_ABSOLUTETIME(1);
 
-	if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
-		PG_RETURN_BOOL(false);
-	if (t1 == CURRENT_ABSTIME)
-		t1 = GetCurrentTransactionStartTime();
-	if (t2 == CURRENT_ABSTIME)
-		t2 = GetCurrentTransactionStartTime();
+	PG_RETURN_BOOL(abstime_cmp_internal(t1, t2) >= 0);
+}
 
-	PG_RETURN_BOOL(t1 >= t2);
+Datum
+btabstimecmp(PG_FUNCTION_ARGS)
+{
+	AbsoluteTime t1 = PG_GETARG_ABSOLUTETIME(0);
+	AbsoluteTime t2 = PG_GETARG_ABSOLUTETIME(1);
+
+	PG_RETURN_INT32(abstime_cmp_internal(t1, t2));
 }
 
 
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index d4e93cf8756..99df5331bf6 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -5,7 +5,7 @@
  *
  *	1998 Jan Wieck
  *
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.40 2001/04/14 02:10:57 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.41 2001/05/03 19:00:36 tgl Exp $
  *
  * ----------
  */
@@ -153,6 +153,7 @@ static Numeric make_result(NumericVar *var);
 
 static void apply_typmod(NumericVar *var, int32 typmod);
 
+static int	cmp_numerics(Numeric num1, Numeric num2);
 static int	cmp_var(NumericVar *var1, NumericVar *var2);
 static void add_var(NumericVar *var1, NumericVar *var2, NumericVar *result);
 static void sub_var(NumericVar *var1, NumericVar *var2, NumericVar *result);
@@ -664,24 +665,7 @@ numeric_cmp(PG_FUNCTION_ARGS)
 	Numeric		num2 = PG_GETARG_NUMERIC(1);
 	int			result;
 
-	if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
-		result = 0;
-	else
-	{
-		NumericVar	arg1;
-		NumericVar	arg2;
-
-		init_var(&arg1);
-		init_var(&arg2);
-
-		set_var_from_num(num1, &arg1);
-		set_var_from_num(num2, &arg2);
-
-		result = cmp_var(&arg1, &arg2);
-
-		free_var(&arg1);
-		free_var(&arg2);
-	}
+	result = cmp_numerics(num1, num2);
 
 	PG_FREE_IF_COPY(num1, 0);
 	PG_FREE_IF_COPY(num2, 1);
@@ -697,24 +681,7 @@ numeric_eq(PG_FUNCTION_ARGS)
 	Numeric		num2 = PG_GETARG_NUMERIC(1);
 	bool		result;
 
-	if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
-		result = false;
-	else
-	{
-		NumericVar	arg1;
-		NumericVar	arg2;
-
-		init_var(&arg1);
-		init_var(&arg2);
-
-		set_var_from_num(num1, &arg1);
-		set_var_from_num(num2, &arg2);
-
-		result = cmp_var(&arg1, &arg2) == 0;
-
-		free_var(&arg1);
-		free_var(&arg2);
-	}
+	result = cmp_numerics(num1, num2) == 0;
 
 	PG_FREE_IF_COPY(num1, 0);
 	PG_FREE_IF_COPY(num2, 1);
@@ -729,24 +696,7 @@ numeric_ne(PG_FUNCTION_ARGS)
 	Numeric		num2 = PG_GETARG_NUMERIC(1);
 	bool		result;
 
-	if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
-		result = false;
-	else
-	{
-		NumericVar	arg1;
-		NumericVar	arg2;
-
-		init_var(&arg1);
-		init_var(&arg2);
-
-		set_var_from_num(num1, &arg1);
-		set_var_from_num(num2, &arg2);
-
-		result = cmp_var(&arg1, &arg2) != 0;
-
-		free_var(&arg1);
-		free_var(&arg2);
-	}
+	result = cmp_numerics(num1, num2) != 0;
 
 	PG_FREE_IF_COPY(num1, 0);
 	PG_FREE_IF_COPY(num2, 1);
@@ -761,24 +711,7 @@ numeric_gt(PG_FUNCTION_ARGS)
 	Numeric		num2 = PG_GETARG_NUMERIC(1);
 	bool		result;
 
-	if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
-		result = false;
-	else
-	{
-		NumericVar	arg1;
-		NumericVar	arg2;
-
-		init_var(&arg1);
-		init_var(&arg2);
-
-		set_var_from_num(num1, &arg1);
-		set_var_from_num(num2, &arg2);
-
-		result = cmp_var(&arg1, &arg2) > 0;
-
-		free_var(&arg1);
-		free_var(&arg2);
-	}
+	result = cmp_numerics(num1, num2) > 0;
 
 	PG_FREE_IF_COPY(num1, 0);
 	PG_FREE_IF_COPY(num2, 1);
@@ -793,24 +726,7 @@ numeric_ge(PG_FUNCTION_ARGS)
 	Numeric		num2 = PG_GETARG_NUMERIC(1);
 	bool		result;
 
-	if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
-		result = false;
-	else
-	{
-		NumericVar	arg1;
-		NumericVar	arg2;
-
-		init_var(&arg1);
-		init_var(&arg2);
-
-		set_var_from_num(num1, &arg1);
-		set_var_from_num(num2, &arg2);
-
-		result = cmp_var(&arg1, &arg2) >= 0;
-
-		free_var(&arg1);
-		free_var(&arg2);
-	}
+	result = cmp_numerics(num1, num2) >= 0;
 
 	PG_FREE_IF_COPY(num1, 0);
 	PG_FREE_IF_COPY(num2, 1);
@@ -825,24 +741,7 @@ numeric_lt(PG_FUNCTION_ARGS)
 	Numeric		num2 = PG_GETARG_NUMERIC(1);
 	bool		result;
 
-	if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
-		result = false;
-	else
-	{
-		NumericVar	arg1;
-		NumericVar	arg2;
-
-		init_var(&arg1);
-		init_var(&arg2);
-
-		set_var_from_num(num1, &arg1);
-		set_var_from_num(num2, &arg2);
-
-		result = cmp_var(&arg1, &arg2) < 0;
-
-		free_var(&arg1);
-		free_var(&arg2);
-	}
+	result = cmp_numerics(num1, num2) < 0;
 
 	PG_FREE_IF_COPY(num1, 0);
 	PG_FREE_IF_COPY(num2, 1);
@@ -857,8 +756,35 @@ numeric_le(PG_FUNCTION_ARGS)
 	Numeric		num2 = PG_GETARG_NUMERIC(1);
 	bool		result;
 
-	if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
-		result = false;
+	result = cmp_numerics(num1, num2) <= 0;
+
+	PG_FREE_IF_COPY(num1, 0);
+	PG_FREE_IF_COPY(num2, 1);
+
+	PG_RETURN_BOOL(result);
+}
+
+static int
+cmp_numerics(Numeric num1, Numeric num2)
+{
+	int			result;
+
+	/*
+	 * We consider all NANs to be equal and larger than any non-NAN.
+	 * This is somewhat arbitrary; the important thing is to have a
+	 * consistent sort order.
+	 */
+	if (NUMERIC_IS_NAN(num1))
+	{
+		if (NUMERIC_IS_NAN(num2))
+			result = 0;			/* NAN = NAN */
+		else
+			result = 1;			/* NAN > non-NAN */
+	}
+	else if (NUMERIC_IS_NAN(num2))
+	{
+		result = -1;			/* non-NAN < NAN */
+	}
 	else
 	{
 		NumericVar	arg1;
@@ -870,16 +796,13 @@ numeric_le(PG_FUNCTION_ARGS)
 		set_var_from_num(num1, &arg1);
 		set_var_from_num(num2, &arg2);
 
-		result = cmp_var(&arg1, &arg2) <= 0;
+		result = cmp_var(&arg1, &arg2);
 
 		free_var(&arg1);
 		free_var(&arg2);
 	}
 
-	PG_FREE_IF_COPY(num1, 0);
-	PG_FREE_IF_COPY(num2, 1);
-
-	PG_RETURN_BOOL(result);
+	return result;
 }
 
 
@@ -1663,6 +1586,7 @@ numeric_int2(PG_FUNCTION_ARGS)
 	char	   *str;
 	Datum		result;
 
+	/* XXX would it be better to return NULL? */
 	if (NUMERIC_IS_NAN(num))
 		elog(ERROR, "Cannot convert NaN to int2");
 
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index f4c90dc3723..186103252b3 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.47 2001/04/03 18:05:53 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.48 2001/05/03 19:00:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -542,22 +542,34 @@ SetTimestamp(Timestamp dt)
 
 /*
  *		timestamp_relop - is timestamp1 relop timestamp2
+ *
+ *		collate invalid timestamp at the end
  */
+static int
+timestamp_cmp_internal(Timestamp dt1, Timestamp dt2)
+{
+	if (TIMESTAMP_IS_INVALID(dt1))
+		return (TIMESTAMP_IS_INVALID(dt2) ? 0 : 1);
+	else if (TIMESTAMP_IS_INVALID(dt2))
+		return -1;
+	else
+	{
+		if (TIMESTAMP_IS_RELATIVE(dt1))
+			dt1 = SetTimestamp(dt1);
+		if (TIMESTAMP_IS_RELATIVE(dt2))
+			dt2 = SetTimestamp(dt2);
+
+		return ((dt1 < dt2) ? -1 : ((dt1 > dt2) ? 1 : 0));
+	}
+}
+
 Datum
 timestamp_eq(PG_FUNCTION_ARGS)
 {
 	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
 	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
 
-	if (TIMESTAMP_IS_INVALID(dt1) || TIMESTAMP_IS_INVALID(dt2))
-		PG_RETURN_BOOL(false);
-
-	if (TIMESTAMP_IS_RELATIVE(dt1))
-		dt1 = SetTimestamp(dt1);
-	if (TIMESTAMP_IS_RELATIVE(dt2))
-		dt2 = SetTimestamp(dt2);
-
-	PG_RETURN_BOOL(dt1 == dt2);
+	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
 }
 
 Datum
@@ -566,15 +578,7 @@ timestamp_ne(PG_FUNCTION_ARGS)
 	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
 	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
 
-	if (TIMESTAMP_IS_INVALID(dt1) || TIMESTAMP_IS_INVALID(dt2))
-		PG_RETURN_BOOL(false);
-
-	if (TIMESTAMP_IS_RELATIVE(dt1))
-		dt1 = SetTimestamp(dt1);
-	if (TIMESTAMP_IS_RELATIVE(dt2))
-		dt2 = SetTimestamp(dt2);
-
-	PG_RETURN_BOOL(dt1 != dt2);
+	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
 }
 
 Datum
@@ -583,15 +587,7 @@ timestamp_lt(PG_FUNCTION_ARGS)
 	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
 	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
 
-	if (TIMESTAMP_IS_INVALID(dt1) || TIMESTAMP_IS_INVALID(dt2))
-		PG_RETURN_BOOL(false);
-
-	if (TIMESTAMP_IS_RELATIVE(dt1))
-		dt1 = SetTimestamp(dt1);
-	if (TIMESTAMP_IS_RELATIVE(dt2))
-		dt2 = SetTimestamp(dt2);
-
-	PG_RETURN_BOOL(dt1 < dt2);
+	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
 }
 
 Datum
@@ -600,15 +596,7 @@ timestamp_gt(PG_FUNCTION_ARGS)
 	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
 	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
 
-	if (TIMESTAMP_IS_INVALID(dt1) || TIMESTAMP_IS_INVALID(dt2))
-		PG_RETURN_BOOL(false);
-
-	if (TIMESTAMP_IS_RELATIVE(dt1))
-		dt1 = SetTimestamp(dt1);
-	if (TIMESTAMP_IS_RELATIVE(dt2))
-		dt2 = SetTimestamp(dt2);
-
-	PG_RETURN_BOOL(dt1 > dt2);
+	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
 }
 
 Datum
@@ -617,15 +605,7 @@ timestamp_le(PG_FUNCTION_ARGS)
 	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
 	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
 
-	if (TIMESTAMP_IS_INVALID(dt1) || TIMESTAMP_IS_INVALID(dt2))
-		PG_RETURN_BOOL(false);
-
-	if (TIMESTAMP_IS_RELATIVE(dt1))
-		dt1 = SetTimestamp(dt1);
-	if (TIMESTAMP_IS_RELATIVE(dt2))
-		dt2 = SetTimestamp(dt2);
-
-	PG_RETURN_BOOL(dt1 <= dt2);
+	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
 }
 
 Datum
@@ -634,57 +614,54 @@ timestamp_ge(PG_FUNCTION_ARGS)
 	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
 	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
 
-	if (TIMESTAMP_IS_INVALID(dt1) || TIMESTAMP_IS_INVALID(dt2))
-		PG_RETURN_BOOL(false);
-
-	if (TIMESTAMP_IS_RELATIVE(dt1))
-		dt1 = SetTimestamp(dt1);
-	if (TIMESTAMP_IS_RELATIVE(dt2))
-		dt2 = SetTimestamp(dt2);
-
-	PG_RETURN_BOOL(dt1 >= dt2);
+	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
 }
 
-
-/*		timestamp_cmp	- 3-state comparison for timestamp
- *		collate invalid timestamp at the end
- */
 Datum
 timestamp_cmp(PG_FUNCTION_ARGS)
 {
 	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
 	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
 
-	if (TIMESTAMP_IS_INVALID(dt1))
-		PG_RETURN_INT32(TIMESTAMP_IS_INVALID(dt2) ? 0 : 1);
-	else if (TIMESTAMP_IS_INVALID(dt2))
-		PG_RETURN_INT32(-1);
-	else
-	{
-		if (TIMESTAMP_IS_RELATIVE(dt1))
-			dt1 = SetTimestamp(dt1);
-		if (TIMESTAMP_IS_RELATIVE(dt2))
-			dt2 = SetTimestamp(dt2);
-	}
-
-	PG_RETURN_INT32((dt1 < dt2) ? -1 : ((dt1 > dt2) ? 1 : 0));
+	PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
 }
 
 
 /*
  *		interval_relop	- is interval1 relop interval2
+ *
+ *		collate invalid interval at the end
  */
+static int
+interval_cmp_internal(Interval *interval1, Interval *interval2)
+{
+	if (INTERVAL_IS_INVALID(*interval1))
+		return (INTERVAL_IS_INVALID(*interval2) ? 0 : 1);
+	else if (INTERVAL_IS_INVALID(*interval2))
+		return -1;
+	else
+	{
+		double		span1,
+					span2;
+
+		span1 = interval1->time;
+		if (interval1->month != 0)
+			span1 += (interval1->month * (30.0 * 86400));
+		span2 = interval2->time;
+		if (interval2->month != 0)
+			span2 += (interval2->month * (30.0 * 86400));
+
+		return ((span1 < span2) ? -1 : (span1 > span2) ? 1 : 0);
+	}
+}
+
 Datum
 interval_eq(PG_FUNCTION_ARGS)
 {
 	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
 	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
 
-	if (INTERVAL_IS_INVALID(*interval1) || INTERVAL_IS_INVALID(*interval2))
-		PG_RETURN_BOOL(false);
-
-	PG_RETURN_BOOL((interval1->time == interval2->time) &&
-				   (interval1->month == interval2->month));
+	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) == 0);
 }
 
 Datum
@@ -693,11 +670,7 @@ interval_ne(PG_FUNCTION_ARGS)
 	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
 	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
 
-	if (INTERVAL_IS_INVALID(*interval1) || INTERVAL_IS_INVALID(*interval2))
-		PG_RETURN_BOOL(false);
-
-	PG_RETURN_BOOL((interval1->time != interval2->time) ||
-				   (interval1->month != interval2->month));
+	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) != 0);
 }
 
 Datum
@@ -705,20 +678,8 @@ interval_lt(PG_FUNCTION_ARGS)
 {
 	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
 	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
-	double		span1,
-				span2;
 
-	if (INTERVAL_IS_INVALID(*interval1) || INTERVAL_IS_INVALID(*interval2))
-		PG_RETURN_BOOL(false);
-
-	span1 = interval1->time;
-	if (interval1->month != 0)
-		span1 += (interval1->month * (30.0 * 86400));
-	span2 = interval2->time;
-	if (interval2->month != 0)
-		span2 += (interval2->month * (30.0 * 86400));
-
-	PG_RETURN_BOOL(span1 < span2);
+	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) < 0);
 }
 
 Datum
@@ -726,20 +687,8 @@ interval_gt(PG_FUNCTION_ARGS)
 {
 	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
 	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
-	double		span1,
-				span2;
-
-	if (INTERVAL_IS_INVALID(*interval1) || INTERVAL_IS_INVALID(*interval2))
-		PG_RETURN_BOOL(false);
-
-	span1 = interval1->time;
-	if (interval1->month != 0)
-		span1 += (interval1->month * (30.0 * 86400));
-	span2 = interval2->time;
-	if (interval2->month != 0)
-		span2 += (interval2->month * (30.0 * 86400));
 
-	PG_RETURN_BOOL(span1 > span2);
+	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) > 0);
 }
 
 Datum
@@ -747,20 +696,8 @@ interval_le(PG_FUNCTION_ARGS)
 {
 	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
 	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
-	double		span1,
-				span2;
-
-	if (INTERVAL_IS_INVALID(*interval1) || INTERVAL_IS_INVALID(*interval2))
-		PG_RETURN_BOOL(false);
-
-	span1 = interval1->time;
-	if (interval1->month != 0)
-		span1 += (interval1->month * (30.0 * 86400));
-	span2 = interval2->time;
-	if (interval2->month != 0)
-		span2 += (interval2->month * (30.0 * 86400));
 
-	PG_RETURN_BOOL(span1 <= span2);
+	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) <= 0);
 }
 
 Datum
@@ -768,46 +705,17 @@ interval_ge(PG_FUNCTION_ARGS)
 {
 	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
 	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
-	double		span1,
-				span2;
 
-	if (INTERVAL_IS_INVALID(*interval1) || INTERVAL_IS_INVALID(*interval2))
-		PG_RETURN_BOOL(false);
-
-	span1 = interval1->time;
-	if (interval1->month != 0)
-		span1 += (interval1->month * (30.0 * 86400));
-	span2 = interval2->time;
-	if (interval2->month != 0)
-		span2 += (interval2->month * (30.0 * 86400));
-
-	PG_RETURN_BOOL(span1 >= span2);
+	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) >= 0);
 }
 
-
-/*		interval_cmp	- 3-state comparison for interval
- */
 Datum
 interval_cmp(PG_FUNCTION_ARGS)
 {
 	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
 	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
-	double		span1,
-				span2;
-
-	if (INTERVAL_IS_INVALID(*interval1))
-		PG_RETURN_INT32(INTERVAL_IS_INVALID(*interval2) ? 0 : 1);
-	else if (INTERVAL_IS_INVALID(*interval2))
-		PG_RETURN_INT32(-1);
-
-	span1 = interval1->time;
-	if (interval1->month != 0)
-		span1 += (interval1->month * (30.0 * 86400));
-	span2 = interval2->time;
-	if (interval2->month != 0)
-		span2 += (interval2->month * (30.0 * 86400));
 
-	PG_RETURN_INT32((span1 < span2) ? -1 : (span1 > span2) ? 1 : 0);
+	PG_RETURN_INT32(interval_cmp_internal(interval1, interval2));
 }
 
 /*
diff --git a/src/backend/utils/adt/varbit.c b/src/backend/utils/adt/varbit.c
index 775382568bb..5d03683dd6b 100644
--- a/src/backend/utils/adt/varbit.c
+++ b/src/backend/utils/adt/varbit.c
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/varbit.c,v 1.16 2001/03/22 03:59:54 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/varbit.c,v 1.17 2001/05/03 19:00:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -537,6 +537,36 @@ _varbit(PG_FUNCTION_ARGS)
  * need to be so careful.
  */
 
+/* bit_cmp
+ *
+ * Compares two bitstrings and returns <0, 0, >0 depending on whether the first
+ * string is smaller, equal, or bigger than the second. All bits are considered
+ * and additional zero bits may make one string smaller/larger than the other,
+ * even if their zero-padded values would be the same.
+ */
+static int32
+bit_cmp(VarBit *arg1, VarBit *arg2)
+{
+	int			bitlen1,
+				bytelen1,
+				bitlen2,
+				bytelen2;
+	int32		cmp;
+
+	bytelen1 = VARBITBYTES(arg1);
+	bytelen2 = VARBITBYTES(arg2);
+
+	cmp = memcmp(VARBITS(arg1), VARBITS(arg2), Min(bytelen1, bytelen2));
+	if (cmp == 0)
+	{
+		bitlen1 = VARBITLEN(arg1);
+		bitlen2 = VARBITLEN(arg2);
+		if (bitlen1 != bitlen2)
+			cmp = (bitlen1 < bitlen2) ? -1 : 1;
+	}
+	return cmp;
+}
+
 Datum
 biteq(PG_FUNCTION_ARGS)
 {
@@ -548,13 +578,12 @@ biteq(PG_FUNCTION_ARGS)
 
 	bitlen1 = VARBITLEN(arg1);
 	bitlen2 = VARBITLEN(arg2);
+
+	/* fast path for different-length inputs */
 	if (bitlen1 != bitlen2)
 		result = false;
 	else
-	{
-		/* bit strings are always stored in a full number of bytes */
-		result = memcmp(VARBITS(arg1), VARBITS(arg2), VARBITBYTES(arg1)) == 0;
-	}
+		result = (bit_cmp(arg1, arg2) == 0);
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
@@ -573,13 +602,12 @@ bitne(PG_FUNCTION_ARGS)
 
 	bitlen1 = VARBITLEN(arg1);
 	bitlen2 = VARBITLEN(arg2);
+
+	/* fast path for different-length inputs */
 	if (bitlen1 != bitlen2)
 		result = true;
 	else
-	{
-		/* bit strings are always stored in a full number of bytes */
-		result = memcmp(VARBITS(arg1), VARBITS(arg2), VARBITBYTES(arg1)) != 0;
-	}
+		result = (bit_cmp(arg1, arg2) != 0);
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
@@ -587,36 +615,6 @@ bitne(PG_FUNCTION_ARGS)
 	PG_RETURN_BOOL(result);
 }
 
-/* bit_cmp
- *
- * Compares two bitstrings and returns <0, 0, >0 depending on whether the first
- * string is smaller, equal, or bigger than the second. All bits are considered
- * and additional zero bits may make one string smaller/larger than the other,
- * even if their zero-padded values would be the same.
- */
-static int32
-bit_cmp(VarBit *arg1, VarBit *arg2)
-{
-	int			bitlen1,
-				bytelen1,
-				bitlen2,
-				bytelen2;
-	int32		cmp;
-
-	bytelen1 = VARBITBYTES(arg1);
-	bytelen2 = VARBITBYTES(arg2);
-
-	cmp = memcmp(VARBITS(arg1), VARBITS(arg2), Min(bytelen1, bytelen2));
-	if (cmp == 0)
-	{
-		bitlen1 = VARBITLEN(arg1);
-		bitlen2 = VARBITLEN(arg2);
-		if (bitlen1 != bitlen2)
-			cmp = (bitlen1 < bitlen2) ? -1 : 1;
-	}
-	return cmp;
-}
-
 Datum
 bitlt(PG_FUNCTION_ARGS)
 {
diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c
index adb41eed689..588c735ca10 100644
--- a/src/backend/utils/adt/varchar.c
+++ b/src/backend/utils/adt/varchar.c
@@ -8,11 +8,10 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.76 2001/04/19 19:01:23 petere Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.77 2001/05/03 19:00:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-
 #include "postgres.h"
 
 #include "access/hash.h"
@@ -526,10 +525,11 @@ bpchareq(PG_FUNCTION_ARGS)
 	len1 = bcTruelen(arg1);
 	len2 = bcTruelen(arg2);
 
+	/* fast path for different-length inputs */
 	if (len1 != len2)
 		result = false;
 	else
-		result = (strncmp(VARDATA(arg1), VARDATA(arg2), len1) == 0);
+		result = (varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2) == 0);
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
@@ -549,10 +549,11 @@ bpcharne(PG_FUNCTION_ARGS)
 	len1 = bcTruelen(arg1);
 	len2 = bcTruelen(arg2);
 
+	/* fast path for different-length inputs */
 	if (len1 != len2)
 		result = true;
 	else
-		result = (strncmp(VARDATA(arg1), VARDATA(arg2), len1) != 0);
+		result = (varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2) != 0);
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
@@ -745,10 +746,11 @@ varchareq(PG_FUNCTION_ARGS)
 	len1 = VARSIZE(arg1) - VARHDRSZ;
 	len2 = VARSIZE(arg2) - VARHDRSZ;
 
+	/* fast path for different-length inputs */
 	if (len1 != len2)
 		result = false;
 	else
-		result = (strncmp(VARDATA(arg1), VARDATA(arg2), len1) == 0);
+		result = (varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2) == 0);
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
@@ -768,10 +770,11 @@ varcharne(PG_FUNCTION_ARGS)
 	len1 = VARSIZE(arg1) - VARHDRSZ;
 	len2 = VARSIZE(arg2) - VARHDRSZ;
 
+	/* fast path for different-length inputs */
 	if (len1 != len2)
 		result = true;
 	else
-		result = (strncmp(VARDATA(arg1), VARDATA(arg2), len1) != 0);
+		result = (varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2) != 0);
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index efa6ab65527..c534c7d92e5 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.69 2001/03/22 03:59:55 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.70 2001/05/03 19:00:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -440,72 +440,6 @@ textpos(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(pos);
 }
 
-/*
- *		texteq			- returns true iff arguments are equal
- *		textne			- returns true iff arguments are not equal
- *
- * Note: btree indexes need these routines not to leak memory; therefore,
- * be careful to free working copies of toasted datums.  Most places don't
- * need to be so careful.
- */
-Datum
-texteq(PG_FUNCTION_ARGS)
-{
-	text	   *arg1 = PG_GETARG_TEXT_P(0);
-	text	   *arg2 = PG_GETARG_TEXT_P(1);
-	bool		result;
-
-	if (VARSIZE(arg1) != VARSIZE(arg2))
-		result = false;
-	else
-	{
-		int			len;
-		char	   *a1p,
-				   *a2p;
-
-		len = VARSIZE(arg1) - VARHDRSZ;
-
-		a1p = VARDATA(arg1);
-		a2p = VARDATA(arg2);
-
-		result = (memcmp(a1p, a2p, len) == 0);
-	}
-
-	PG_FREE_IF_COPY(arg1, 0);
-	PG_FREE_IF_COPY(arg2, 1);
-
-	PG_RETURN_BOOL(result);
-}
-
-Datum
-textne(PG_FUNCTION_ARGS)
-{
-	text	   *arg1 = PG_GETARG_TEXT_P(0);
-	text	   *arg2 = PG_GETARG_TEXT_P(1);
-	bool		result;
-
-	if (VARSIZE(arg1) != VARSIZE(arg2))
-		result = true;
-	else
-	{
-		int			len;
-		char	   *a1p,
-				   *a2p;
-
-		len = VARSIZE(arg1) - VARHDRSZ;
-
-		a1p = VARDATA(arg1);
-		a2p = VARDATA(arg2);
-
-		result = (memcmp(a1p, a2p, len) != 0);
-	}
-
-	PG_FREE_IF_COPY(arg1, 0);
-	PG_FREE_IF_COPY(arg2, 1);
-
-	PG_RETURN_BOOL(result);
-}
-
 /* varstr_cmp()
  * Comparison function for text strings with given lengths.
  * Includes locale support, but must copy strings to temporary memory
@@ -520,8 +454,8 @@ varstr_cmp(char *arg1, int len1, char *arg2, int len2)
 			   *a2p;
 
 #ifdef USE_LOCALE
-	a1p = (unsigned char *) palloc(len1 + 1);
-	a2p = (unsigned char *) palloc(len2 + 1);
+	a1p = (char *) palloc(len1 + 1);
+	a2p = (char *) palloc(len2 + 1);
 
 	memcpy(a1p, arg1, len1);
 	*(a1p + len1) = '\0';
@@ -548,11 +482,7 @@ varstr_cmp(char *arg1, int len1, char *arg2, int len2)
 
 
 /* text_cmp()
- * Comparison function for text strings.
- * Includes locale support, but must copy strings to temporary memory
- *	to allow null-termination for inputs to strcoll().
- * XXX HACK code for textlen() indicates that there can be embedded nulls
- *	but it appears that most routines (incl. this one) assume not! - tgl 97/04/07
+ * Internal comparison function for text strings.
  * Returns -1, 0 or 1
  */
 static int
@@ -580,6 +510,44 @@ text_cmp(text *arg1, text *arg2)
  * need to be so careful.
  */
 
+Datum
+texteq(PG_FUNCTION_ARGS)
+{
+	text	   *arg1 = PG_GETARG_TEXT_P(0);
+	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	bool		result;
+
+	/* fast path for different-length inputs */
+	if (VARSIZE(arg1) != VARSIZE(arg2))
+		result = false;
+	else
+		result = (text_cmp(arg1, arg2) == 0);
+
+	PG_FREE_IF_COPY(arg1, 0);
+	PG_FREE_IF_COPY(arg2, 1);
+
+	PG_RETURN_BOOL(result);
+}
+
+Datum
+textne(PG_FUNCTION_ARGS)
+{
+	text	   *arg1 = PG_GETARG_TEXT_P(0);
+	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	bool		result;
+
+	/* fast path for different-length inputs */
+	if (VARSIZE(arg1) != VARSIZE(arg2))
+		result = true;
+	else
+		result = (text_cmp(arg1, arg2) != 0);
+
+	PG_FREE_IF_COPY(arg1, 0);
+	PG_FREE_IF_COPY(arg2, 1);
+
+	PG_RETURN_BOOL(result);
+}
+
 Datum
 text_lt(PG_FUNCTION_ARGS)
 {
@@ -640,6 +608,22 @@ text_ge(PG_FUNCTION_ARGS)
 	PG_RETURN_BOOL(result);
 }
 
+Datum
+bttextcmp(PG_FUNCTION_ARGS)
+{
+	text	   *arg1 = PG_GETARG_TEXT_P(0);
+	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	int32		result;
+
+	result = text_cmp(arg1, arg2);
+
+	PG_FREE_IF_COPY(arg1, 0);
+	PG_FREE_IF_COPY(arg2, 1);
+
+	PG_RETURN_INT32(result);
+}
+
+
 Datum
 text_larger(PG_FUNCTION_ARGS)
 {
diff --git a/src/include/utils/nabstime.h b/src/include/utils/nabstime.h
index 1e09e82e69a..d5343e5a14c 100644
--- a/src/include/utils/nabstime.h
+++ b/src/include/utils/nabstime.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nabstime.h,v 1.29 2001/03/22 04:01:13 momjian Exp $
+ * $Id: nabstime.h,v 1.30 2001/05/03 19:00:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -158,7 +158,6 @@ extern Datum timeofday(PG_FUNCTION_ARGS);
 
 /* non-fmgr-callable support routines */
 extern AbsoluteTime GetCurrentAbsoluteTime(void);
-extern bool AbsoluteTimeIsBefore(AbsoluteTime time1, AbsoluteTime time2);
 extern void abstime2tm(AbsoluteTime time, int *tzp, struct tm * tm, char *tzn);
 
 #endif	 /* NABSTIME_H */
diff --git a/src/test/regress/expected/abstime-solaris-1947.out b/src/test/regress/expected/abstime-solaris-1947.out
index f5b78a1b4f8..569205ab6b4 100644
--- a/src/test/regress/expected/abstime-solaris-1947.out
+++ b/src/test/regress/expected/abstime-solaris-1947.out
@@ -62,7 +62,8 @@ SELECT '' AS six, ABSTIME_TBL.*
      | current
      | infinity
      | Sat May 10 23:59:12 1947 PDT
-(6 rows)
+     | invalid
+(7 rows)
 
 SELECT '' AS six, ABSTIME_TBL.*
    WHERE abstime 'May 10, 1947 23:59:12' <> ABSTIME_TBL.f1;
@@ -74,7 +75,8 @@ SELECT '' AS six, ABSTIME_TBL.*
      | current
      | infinity
      | -infinity
-(6 rows)
+     | invalid
+(7 rows)
 
 SELECT '' AS one, ABSTIME_TBL.*
    WHERE abstime 'current' = ABSTIME_TBL.f1;
diff --git a/src/test/regress/expected/abstime.out b/src/test/regress/expected/abstime.out
index ddafc77ce07..391b4e361ae 100644
--- a/src/test/regress/expected/abstime.out
+++ b/src/test/regress/expected/abstime.out
@@ -62,7 +62,8 @@ SELECT '' AS six, ABSTIME_TBL.*
      | current
      | infinity
      | Sat May 10 23:59:12 1947 PST
-(6 rows)
+     | invalid
+(7 rows)
 
 SELECT '' AS six, ABSTIME_TBL.*
    WHERE abstime 'May 10, 1947 23:59:12' <> ABSTIME_TBL.f1;
@@ -74,7 +75,8 @@ SELECT '' AS six, ABSTIME_TBL.*
      | current
      | infinity
      | -infinity
-(6 rows)
+     | invalid
+(7 rows)
 
 SELECT '' AS one, ABSTIME_TBL.*
    WHERE abstime 'current' = ABSTIME_TBL.f1;
diff --git a/src/test/regress/expected/timestamp.out b/src/test/regress/expected/timestamp.out
index a7f4e6c0ae3..60d36735435 100644
--- a/src/test/regress/expected/timestamp.out
+++ b/src/test/regress/expected/timestamp.out
@@ -265,8 +265,11 @@ SELECT '' AS "47", d1 FROM TIMESTAMP_TBL
    WHERE d1 > timestamp '1997-01-02' and d1 != timestamp 'current';
  47 |               d1                
 ----+---------------------------------
+    | invalid
     | infinity
     | Mon Feb 10 17:32:01 1997 PST
+    | invalid
+    | invalid
     | Mon Feb 10 17:32:01.00 1997 PST
     | Mon Feb 10 17:32:02.00 1997 PST
     | Mon Feb 10 17:32:01.40 1997 PST
@@ -312,7 +315,7 @@ SELECT '' AS "47", d1 FROM TIMESTAMP_TBL
     | Sat Jan 01 17:32:01 2000 PST
     | Sun Dec 31 17:32:01 2000 PST
     | Mon Jan 01 17:32:01 2001 PST
-(47 rows)
+(50 rows)
 
 SELECT '' AS "15", d1 FROM TIMESTAMP_TBL
    WHERE d1 < timestamp '1997-01-02' and d1 != timestamp 'current';
@@ -346,10 +349,13 @@ SELECT '' AS "62", d1 FROM TIMESTAMP_TBL
    WHERE d1 != timestamp '1997-01-02' and d1 != timestamp 'current';
  62 |               d1                
 ----+---------------------------------
+    | invalid
     | -infinity
     | infinity
     | epoch
     | Mon Feb 10 17:32:01 1997 PST
+    | invalid
+    | invalid
     | Mon Feb 10 17:32:01.00 1997 PST
     | Mon Feb 10 17:32:02.00 1997 PST
     | Mon Feb 10 17:32:01.40 1997 PST
@@ -408,7 +414,7 @@ SELECT '' AS "62", d1 FROM TIMESTAMP_TBL
     | Sat Jan 01 17:32:01 2000 PST
     | Sun Dec 31 17:32:01 2000 PST
     | Mon Jan 01 17:32:01 2001 PST
-(62 rows)
+(65 rows)
 
 SELECT '' AS "16", d1 FROM TIMESTAMP_TBL
    WHERE d1 <= timestamp '1997-01-02' and d1 != timestamp 'current';
@@ -436,8 +442,11 @@ SELECT '' AS "48", d1 FROM TIMESTAMP_TBL
    WHERE d1 >= timestamp '1997-01-02' and d1 != timestamp 'current';
  48 |               d1                
 ----+---------------------------------
+    | invalid
     | infinity
     | Mon Feb 10 17:32:01 1997 PST
+    | invalid
+    | invalid
     | Mon Feb 10 17:32:01.00 1997 PST
     | Mon Feb 10 17:32:02.00 1997 PST
     | Mon Feb 10 17:32:01.40 1997 PST
@@ -484,7 +493,7 @@ SELECT '' AS "48", d1 FROM TIMESTAMP_TBL
     | Sat Jan 01 17:32:01 2000 PST
     | Sun Dec 31 17:32:01 2000 PST
     | Mon Jan 01 17:32:01 2001 PST
-(48 rows)
+(51 rows)
 
 SELECT '' AS "66", d1 + interval '1 year' AS one_year FROM TIMESTAMP_TBL;
  66 |            one_year             
-- 
GitLab