diff options
-rw-r--r-- | src/backend/utils/adt/timestamp.c | 66 | ||||
-rw-r--r-- | src/test/regress/expected/interval.out | 13 | ||||
-rw-r--r-- | src/test/regress/sql/interval.sql | 6 |
3 files changed, 57 insertions, 28 deletions
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 79e106c83bf..1ea2ca5e793 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.157.2.3 2008/07/07 18:10:12 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.157.2.4 2009/04/04 04:53:50 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1654,32 +1654,36 @@ timestamptz_cmp_timestamp(PG_FUNCTION_ARGS) * * collate invalid interval at the end */ -static int -interval_cmp_internal(Interval *interval1, Interval *interval2) -{ #ifdef HAVE_INT64_TIMESTAMP - int64 span1, - span2; +typedef int64 TimeOffset; #else - double span1, - span2; +typedef double TimeOffset; #endif - span1 = interval1->time; - span2 = interval2->time; +static inline TimeOffset +interval_cmp_value(const Interval *interval) +{ + TimeOffset span; + + span = interval->time; #ifdef HAVE_INT64_TIMESTAMP - span1 += interval1->month * INT64CONST(30) * USECS_PER_DAY; - span1 += interval1->day * INT64CONST(24) * USECS_PER_HOUR; - span2 += interval2->month * INT64CONST(30) * USECS_PER_DAY; - span2 += interval2->day * INT64CONST(24) * USECS_PER_HOUR; + span += interval->month * INT64CONST(30) * USECS_PER_DAY; + span += interval->day * INT64CONST(24) * USECS_PER_HOUR; #else - span1 += interval1->month * ((double) DAYS_PER_MONTH * SECS_PER_DAY); - span1 += interval1->day * ((double) HOURS_PER_DAY * SECS_PER_HOUR); - span2 += interval2->month * ((double) DAYS_PER_MONTH * SECS_PER_DAY); - span2 += interval2->day * ((double) HOURS_PER_DAY * SECS_PER_HOUR); + span += interval->month * ((double) DAYS_PER_MONTH * SECS_PER_DAY); + span += interval->day * ((double) HOURS_PER_DAY * SECS_PER_HOUR); #endif + return span; +} + +static int +interval_cmp_internal(Interval *interval1, Interval *interval2) +{ + TimeOffset span1 = interval_cmp_value(interval1); + TimeOffset span2 = interval_cmp_value(interval2); + return ((span1 < span2) ? -1 : (span1 > span2) ? 1 : 0); } @@ -1747,20 +1751,28 @@ interval_cmp(PG_FUNCTION_ARGS) } /* - * interval, being an unusual size, needs a specialized hash function. + * Hashing for intervals + * + * We must produce equal hashvals for values that interval_cmp_internal() + * considers equal. So, compute the net span the same way it does, + * and then hash that, using either int64 or float8 hashing. */ Datum interval_hash(PG_FUNCTION_ARGS) { - Interval *key = PG_GETARG_INTERVAL_P(0); + Interval *interval = PG_GETARG_INTERVAL_P(0); + TimeOffset span = interval_cmp_value(interval); + uint32 thash; - /* - * Specify hash length as sizeof(double) + sizeof(int4), not as - * sizeof(Interval), so that any garbage pad bytes in the structure won't - * be included in the hash! - */ - return hash_any((unsigned char *) key, - sizeof(key->time) + sizeof(key->day) + sizeof(key->month)); +#ifdef HAVE_INT64_TIMESTAMP + thash = DatumGetUInt32(DirectFunctionCall1(hashint8, + Int64GetDatumFast(span))); +#else + thash = DatumGetUInt32(DirectFunctionCall1(hashfloat8, + Float8GetDatumFast(span))); +#endif + + PG_RETURN_UINT32(thash); } /* overlaps_timestamp() --- implements the SQL92 OVERLAPS operator. diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out index 17b18a1a0f9..0a54b06cda1 100644 --- a/src/test/regress/expected/interval.out +++ b/src/test/regress/expected/interval.out @@ -264,3 +264,16 @@ SELECT '5.5 seconds 3 milliseconds'::interval; -- error ERROR: invalid input syntax for type interval: "5.5 seconds 3 milliseconds" SELECT '1:20:05 5 microseconds'::interval; -- error ERROR: invalid input syntax for type interval: "1:20:05 5 microseconds" +-- check that '30 days' equals '1 month' according to the hash function +select '30 days'::interval = '1 month'::interval as t; + t +--- + t +(1 row) + +select interval_hash('30 days'::interval) = interval_hash('1 month'::interval) as t; + t +--- + t +(1 row) + diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql index 920ce8a09a4..540b887992d 100644 --- a/src/test/regress/sql/interval.sql +++ b/src/test/regress/sql/interval.sql @@ -85,4 +85,8 @@ SELECT '3 days 5 milliseconds'::interval; SELECT '1 second 2 seconds'::interval; -- error SELECT '10 milliseconds 20 milliseconds'::interval; -- error SELECT '5.5 seconds 3 milliseconds'::interval; -- error -SELECT '1:20:05 5 microseconds'::interval; -- error
\ No newline at end of file +SELECT '1:20:05 5 microseconds'::interval; -- error + +-- check that '30 days' equals '1 month' according to the hash function +select '30 days'::interval = '1 month'::interval as t; +select interval_hash('30 days'::interval) = interval_hash('1 month'::interval) as t; |