From 2792374cff361a7a4ec0e750b5fa935d85afc9ac Mon Sep 17 00:00:00 2001 From: Tom Lane 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 | 324 +++++++----------- src/backend/utils/adt/timestamp.c | 212 ++++-------- src/backend/utils/adt/varbit.c | 100 +++--- 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, 536 insertions(+), 747 deletions(-) diff --git a/src/backend/access/nbtree/nbtcompare.c b/src/backend/access/nbtree/nbtcompare.c index fc85906d9b..b1affe2018 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 5e7d3c92f2..4504f5f043 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 7a83ee6577..06405d0cee 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 #include #include /* faked on sunos4 */ #include -#include "postgres.h" - #include /* 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 69ae1f1702..0f8ed87e5b 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 d4e93cf875..99df5331bf 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,8 +665,126 @@ numeric_cmp(PG_FUNCTION_ARGS) Numeric num2 = PG_GETARG_NUMERIC(1); int result; - if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2)) - result = 0; + result = cmp_numerics(num1, num2); + + PG_FREE_IF_COPY(num1, 0); + PG_FREE_IF_COPY(num2, 1); + + PG_RETURN_INT32(result); +} + + +Datum +numeric_eq(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + bool result; + + result = cmp_numerics(num1, num2) == 0; + + PG_FREE_IF_COPY(num1, 0); + PG_FREE_IF_COPY(num2, 1); + + PG_RETURN_BOOL(result); +} + +Datum +numeric_ne(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + bool result; + + result = cmp_numerics(num1, num2) != 0; + + PG_FREE_IF_COPY(num1, 0); + PG_FREE_IF_COPY(num2, 1); + + PG_RETURN_BOOL(result); +} + +Datum +numeric_gt(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + bool result; + + result = cmp_numerics(num1, num2) > 0; + + PG_FREE_IF_COPY(num1, 0); + PG_FREE_IF_COPY(num2, 1); + + PG_RETURN_BOOL(result); +} + +Datum +numeric_ge(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + bool result; + + result = cmp_numerics(num1, num2) >= 0; + + PG_FREE_IF_COPY(num1, 0); + PG_FREE_IF_COPY(num2, 1); + + PG_RETURN_BOOL(result); +} + +Datum +numeric_lt(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + bool result; + + result = cmp_numerics(num1, num2) < 0; + + PG_FREE_IF_COPY(num1, 0); + PG_FREE_IF_COPY(num2, 1); + + PG_RETURN_BOOL(result); +} + +Datum +numeric_le(PG_FUNCTION_ARGS) +{ + Numeric num1 = PG_GETARG_NUMERIC(0); + Numeric num2 = PG_GETARG_NUMERIC(1); + bool result; + + 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; @@ -683,203 +802,7 @@ numeric_cmp(PG_FUNCTION_ARGS) free_var(&arg2); } - PG_FREE_IF_COPY(num1, 0); - PG_FREE_IF_COPY(num2, 1); - - PG_RETURN_INT32(result); -} - - -Datum -numeric_eq(PG_FUNCTION_ARGS) -{ - Numeric num1 = PG_GETARG_NUMERIC(0); - 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); - } - - PG_FREE_IF_COPY(num1, 0); - PG_FREE_IF_COPY(num2, 1); - - PG_RETURN_BOOL(result); -} - -Datum -numeric_ne(PG_FUNCTION_ARGS) -{ - Numeric num1 = PG_GETARG_NUMERIC(0); - 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); - } - - PG_FREE_IF_COPY(num1, 0); - PG_FREE_IF_COPY(num2, 1); - - PG_RETURN_BOOL(result); -} - -Datum -numeric_gt(PG_FUNCTION_ARGS) -{ - Numeric num1 = PG_GETARG_NUMERIC(0); - 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); - } - - PG_FREE_IF_COPY(num1, 0); - PG_FREE_IF_COPY(num2, 1); - - PG_RETURN_BOOL(result); -} - -Datum -numeric_ge(PG_FUNCTION_ARGS) -{ - Numeric num1 = PG_GETARG_NUMERIC(0); - 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); - } - - PG_FREE_IF_COPY(num1, 0); - PG_FREE_IF_COPY(num2, 1); - - PG_RETURN_BOOL(result); -} - -Datum -numeric_lt(PG_FUNCTION_ARGS) -{ - Numeric num1 = PG_GETARG_NUMERIC(0); - 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); - } - - PG_FREE_IF_COPY(num1, 0); - PG_FREE_IF_COPY(num2, 1); - - PG_RETURN_BOOL(result); -} - -Datum -numeric_le(PG_FUNCTION_ARGS) -{ - Numeric num1 = PG_GETARG_NUMERIC(0); - 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); - } - - 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 f4c90dc372..186103252b 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 775382568b..5d03683dd6 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,56 +537,6 @@ _varbit(PG_FUNCTION_ARGS) * need to be so careful. */ -Datum -biteq(PG_FUNCTION_ARGS) -{ - VarBit *arg1 = PG_GETARG_VARBIT_P(0); - VarBit *arg2 = PG_GETARG_VARBIT_P(1); - bool result; - int bitlen1, - bitlen2; - - bitlen1 = VARBITLEN(arg1); - bitlen2 = VARBITLEN(arg2); - 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; - } - - PG_FREE_IF_COPY(arg1, 0); - PG_FREE_IF_COPY(arg2, 1); - - PG_RETURN_BOOL(result); -} - -Datum -bitne(PG_FUNCTION_ARGS) -{ - VarBit *arg1 = PG_GETARG_VARBIT_P(0); - VarBit *arg2 = PG_GETARG_VARBIT_P(1); - bool result; - int bitlen1, - bitlen2; - - bitlen1 = VARBITLEN(arg1); - bitlen2 = VARBITLEN(arg2); - 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; - } - - PG_FREE_IF_COPY(arg1, 0); - PG_FREE_IF_COPY(arg2, 1); - - PG_RETURN_BOOL(result); -} - /* bit_cmp * * Compares two bitstrings and returns <0, 0, >0 depending on whether the first @@ -617,6 +567,54 @@ bit_cmp(VarBit *arg1, VarBit *arg2) return cmp; } +Datum +biteq(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + VarBit *arg2 = PG_GETARG_VARBIT_P(1); + bool result; + int bitlen1, + bitlen2; + + bitlen1 = VARBITLEN(arg1); + bitlen2 = VARBITLEN(arg2); + + /* fast path for different-length inputs */ + if (bitlen1 != bitlen2) + result = false; + else + result = (bit_cmp(arg1, arg2) == 0); + + PG_FREE_IF_COPY(arg1, 0); + PG_FREE_IF_COPY(arg2, 1); + + PG_RETURN_BOOL(result); +} + +Datum +bitne(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + VarBit *arg2 = PG_GETARG_VARBIT_P(1); + bool result; + int bitlen1, + bitlen2; + + bitlen1 = VARBITLEN(arg1); + bitlen2 = VARBITLEN(arg2); + + /* fast path for different-length inputs */ + if (bitlen1 != bitlen2) + result = true; + else + result = (bit_cmp(arg1, arg2) != 0); + + PG_FREE_IF_COPY(arg1, 0); + PG_FREE_IF_COPY(arg2, 1); + + PG_RETURN_BOOL(result); +} + Datum bitlt(PG_FUNCTION_ARGS) { diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c index adb41eed68..588c735ca1 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 efa6ab6552..c534c7d92e 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 1e09e82e69..d5343e5a14 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 f5b78a1b4f..569205ab6b 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 ddafc77ce0..391b4e361a 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 a7f4e6c0ae..60d3673543 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