From f2b6bb42ab24242a9b9d73d0324b00db5194b1cf Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 8 Aug 2003 00:10:31 +0000 Subject: [PATCH] Fix floating-point timestamp comparisons to not go nuts if NaN is encountered; per bug report from Christian van der Leeden 8/7/03. Also, adjust larger/smaller routines (MAX/MIN) to share code with comparisons for timestamp, interval, timetz. --- src/backend/utils/adt/date.c | 24 +++--- src/backend/utils/adt/timestamp.c | 132 ++++++++++++------------------ 2 files changed, 67 insertions(+), 89 deletions(-) diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 1e1a15da25..0f9fcaded9 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.89 2003/08/04 02:40:04 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.90 2003/08/08 00:10:31 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1671,12 +1671,13 @@ timetz_larger(PG_FUNCTION_ARGS) { TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0); TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1); + TimeTzADT *result; - if (DatumGetBool(DirectFunctionCall2(timetz_gt, - TimeTzADTPGetDatum(time1), - TimeTzADTPGetDatum(time2)))) - PG_RETURN_TIMETZADT_P(time1); - PG_RETURN_TIMETZADT_P(time2); + if (timetz_cmp_internal(time1, time2) > 0) + result = time1; + else + result = time2; + PG_RETURN_TIMETZADT_P(result); } Datum @@ -1684,12 +1685,13 @@ timetz_smaller(PG_FUNCTION_ARGS) { TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0); TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1); + TimeTzADT *result; - if (DatumGetBool(DirectFunctionCall2(timetz_lt, - TimeTzADTPGetDatum(time1), - TimeTzADTPGetDatum(time2)))) - PG_RETURN_TIMETZADT_P(time1); - PG_RETURN_TIMETZADT_P(time2); + if (timetz_cmp_internal(time1, time2) < 0) + result = time1; + else + result = time2; + PG_RETURN_TIMETZADT_P(result); } /* timetz_pl_interval() diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index b03a4d0c58..b8a5e3fcb1 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.90 2003/08/04 02:40:05 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.91 2003/08/08 00:10:31 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,10 @@ #include #include #include +/* for finite() on Solaris */ +#ifdef HAVE_IEEEFP_H +#include +#endif #include "access/hash.h" #include "access/xact.h" @@ -1290,6 +1294,9 @@ SetEpochTimestamp(void) } /* SetEpochTimestamp() */ /* + * We are currently sharing some code between timestamp and timestamptz. + * The comparison functions are among them. - thomas 2001-09-25 + * * timestamp_relop - is timestamp1 relop timestamp2 * * collate invalid timestamp at the end @@ -1297,7 +1304,37 @@ SetEpochTimestamp(void) static int timestamp_cmp_internal(Timestamp dt1, Timestamp dt2) { +#ifdef HAVE_INT64_TIMESTAMP return ((dt1 < dt2) ? -1 : ((dt1 > dt2) ? 1 : 0)); +#else + /* + * When using float representation, we have to be wary of NaNs. + * + * 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(dt1)) + { + if (isnan(dt2)) + return 0; /* NAN = NAN */ + else + return 1; /* NAN > non-NAN */ + } + else if (isnan(dt2)) + { + return -1; /* non-NAN < NAN */ + } + else + { + if (dt1 > dt2) + return 1; + else if (dt1 < dt2) + return -1; + else + return 0; + } +#endif } Datum @@ -1610,9 +1647,6 @@ overlaps_timestamp(PG_FUNCTION_ARGS) * "Arithmetic" operators on date/times. *---------------------------------------------------------*/ -/* We are currently sharing some code between timestamp and timestamptz. - * The comparison functions are among them. - thomas 2001-09-25 - */ Datum timestamp_smaller(PG_FUNCTION_ARGS) { @@ -1620,8 +1654,11 @@ timestamp_smaller(PG_FUNCTION_ARGS) Timestamp dt2 = PG_GETARG_TIMESTAMP(1); Timestamp result; - result = ((dt2 < dt1) ? dt2 : dt1); - + /* use timestamp_cmp_internal to be sure this agrees with comparisons */ + if (timestamp_cmp_internal(dt1, dt2) < 0) + result = dt1; + else + result = dt2; PG_RETURN_TIMESTAMP(result); } @@ -1632,8 +1669,10 @@ timestamp_larger(PG_FUNCTION_ARGS) Timestamp dt2 = PG_GETARG_TIMESTAMP(1); Timestamp result; - result = ((dt2 > dt1) ? dt2 : dt1); - + if (timestamp_cmp_internal(dt1, dt2) > 0) + result = dt1; + else + result = dt2; PG_RETURN_TIMESTAMP(result); } @@ -1846,42 +1885,11 @@ interval_smaller(PG_FUNCTION_ARGS) Interval *interval2 = PG_GETARG_INTERVAL_P(1); Interval *result; -#ifdef HAVE_INT64_TIMESTAMP - int64 span1, - span2; - -#else - double span1, - span2; -#endif - - result = (Interval *) palloc(sizeof(Interval)); - - span1 = interval1->time; - span2 = interval2->time; -#ifdef HAVE_INT64_TIMESTAMP - if (interval1->month != 0) - span1 += ((interval1->month * INT64CONST(30) * INT64CONST(86400000000))); - if (interval2->month != 0) - span2 += ((interval2->month * INT64CONST(30) * INT64CONST(86400000000))); -#else - if (interval1->month != 0) - span1 += (interval1->month * (30.0 * 86400)); - if (interval2->month != 0) - span2 += (interval2->month * (30.0 * 86400)); -#endif - - if (span2 < span1) - { - result->time = interval2->time; - result->month = interval2->month; - } + /* use interval_cmp_internal to be sure this agrees with comparisons */ + if (interval_cmp_internal(interval1, interval2) < 0) + result = interval1; else - { - result->time = interval1->time; - result->month = interval1->month; - } - + result = interval2; PG_RETURN_INTERVAL_P(result); } @@ -1892,42 +1900,10 @@ interval_larger(PG_FUNCTION_ARGS) Interval *interval2 = PG_GETARG_INTERVAL_P(1); Interval *result; -#ifdef HAVE_INT64_TIMESTAMP - int64 span1, - span2; - -#else - double span1, - span2; -#endif - - result = (Interval *) palloc(sizeof(Interval)); - - span1 = interval1->time; - span2 = interval2->time; -#ifdef HAVE_INT64_TIMESTAMP - if (interval1->month != 0) - span1 += ((interval1->month * INT64CONST(30) * INT64CONST(86400000000))); - if (interval2->month != 0) - span2 += ((interval2->month * INT64CONST(30) * INT64CONST(86400000000))); -#else - if (interval1->month != 0) - span1 += (interval1->month * (30.0 * 86400)); - if (interval2->month != 0) - span2 += (interval2->month * (30.0 * 86400)); -#endif - - if (span2 > span1) - { - result->time = interval2->time; - result->month = interval2->month; - } + if (interval_cmp_internal(interval1, interval2) > 0) + result = interval1; else - { - result->time = interval1->time; - result->month = interval1->month; - } - + result = interval2; PG_RETURN_INTERVAL_P(result); }