mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-21 13:14:19 +08:00
Add support for infinite timestamps
The internal conversion functions for timestamps didn't account for timestamps that are infinite (`-Infinity` or `+Infinity`), and they would therefore generate an error if such timestamps were encountered. This change adds extra checks to the conversion functions to allow infinite timestamps.
This commit is contained in:
parent
418f283443
commit
e1c94484cf
25
src/utils.c
25
src/utils.c
@ -45,6 +45,12 @@ ts_pg_timestamp_to_unix_microseconds(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
|
TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
|
||||||
|
|
||||||
|
if (TIMESTAMP_IS_NOBEGIN(timestamp))
|
||||||
|
PG_RETURN_INT64(PG_INT64_MIN);
|
||||||
|
|
||||||
|
if (TIMESTAMP_IS_NOEND(timestamp))
|
||||||
|
PG_RETURN_INT64(PG_INT64_MAX);
|
||||||
|
|
||||||
if (timestamp < MIN_TIMESTAMP)
|
if (timestamp < MIN_TIMESTAMP)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range")));
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range")));
|
||||||
@ -68,10 +74,16 @@ ts_pg_unix_microseconds_to_timestamp(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
int64 microseconds = PG_GETARG_INT64(0);
|
int64 microseconds = PG_GETARG_INT64(0);
|
||||||
|
|
||||||
|
if (microseconds == PG_INT64_MIN)
|
||||||
|
PG_RETURN_TIMESTAMPTZ(DT_NOBEGIN);
|
||||||
|
|
||||||
|
if (microseconds == PG_INT64_MAX)
|
||||||
|
PG_RETURN_TIMESTAMPTZ(DT_NOEND);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Test that the UNIX us timestamp is within bounds. Note that an int64 at
|
* Test that the UNIX us timestamp is within bounds. Note that an int64 at
|
||||||
* UNIX epoch and microsecond precision cannot represent the upper limit
|
* UNIX epoch and microsecond precision cannot represent the upper limit
|
||||||
* of the supported date range (Julian end date), so INT64_MAX is the
|
* of the supported date range (Julian end date), so INT64_MAX-1 is the
|
||||||
* natural upper bound for this function.
|
* natural upper bound for this function.
|
||||||
*/
|
*/
|
||||||
if (microseconds < TS_INTERNAL_TIMESTAMP_MIN)
|
if (microseconds < TS_INTERNAL_TIMESTAMP_MIN)
|
||||||
@ -85,8 +97,15 @@ Datum
|
|||||||
ts_pg_unix_microseconds_to_date(PG_FUNCTION_ARGS)
|
ts_pg_unix_microseconds_to_date(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
int64 microseconds = PG_GETARG_INT64(0);
|
int64 microseconds = PG_GETARG_INT64(0);
|
||||||
Datum res =
|
Datum res;
|
||||||
DirectFunctionCall1(ts_pg_unix_microseconds_to_timestamp, Int64GetDatum(microseconds));
|
|
||||||
|
if (microseconds == PG_INT64_MIN)
|
||||||
|
PG_RETURN_DATEADT(DATEVAL_NOBEGIN);
|
||||||
|
|
||||||
|
if (microseconds == PG_INT64_MAX)
|
||||||
|
PG_RETURN_DATEADT(DATEVAL_NOEND);
|
||||||
|
|
||||||
|
res = DirectFunctionCall1(ts_pg_unix_microseconds_to_timestamp, Int64GetDatum(microseconds));
|
||||||
res = DirectFunctionCall1(timestamp_date, res);
|
res = DirectFunctionCall1(timestamp_date, res);
|
||||||
PG_RETURN_DATUM(res);
|
PG_RETURN_DATUM(res);
|
||||||
}
|
}
|
||||||
|
@ -157,15 +157,38 @@ SELECT _timescaledb_internal.to_unix_microseconds('2017-02-07 15:09:36.236538+00
|
|||||||
1486480176236538
|
1486480176236538
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
-- In UNIX microseconds, BIGINT MAX is smaller than internal date upper bound
|
-- For timestamps, BIGINT MAX represents +Infinity and BIGINT MIN
|
||||||
-- and should therefore be OK. Further, converting to the internal postgres
|
-- -Infinity. We keep this notion for UNIX epoch time:
|
||||||
-- epoch cannot overflow a 64-bit INTEGER since the postgres epoch is at a
|
SELECT _timescaledb_internal.to_unix_microseconds('+infinity');
|
||||||
-- later date compared to the UNIX epoch, and is therefore represented by a
|
ERROR: invalid input syntax for type timestamp with time zone: "+infinity" at character 51
|
||||||
-- smaller number
|
|
||||||
SELECT _timescaledb_internal.to_timestamp(9223372036854775807);
|
SELECT _timescaledb_internal.to_timestamp(9223372036854775807);
|
||||||
to_timestamp
|
to_timestamp
|
||||||
|
--------------
|
||||||
|
infinity
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT _timescaledb_internal.to_unix_microseconds('-infinity');
|
||||||
|
to_unix_microseconds
|
||||||
|
----------------------
|
||||||
|
-9223372036854775808
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT _timescaledb_internal.to_timestamp(-9223372036854775808);
|
||||||
|
to_timestamp
|
||||||
|
--------------
|
||||||
|
-infinity
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- In UNIX microseconds, the largest bigint value below infinity
|
||||||
|
-- (BIGINT MAX) is smaller than internal date upper bound and should
|
||||||
|
-- therefore be OK. Further, converting to the internal postgres epoch
|
||||||
|
-- cannot overflow a 64-bit INTEGER since the postgres epoch is at a
|
||||||
|
-- later date compared to the UNIX epoch, and is therefore represented
|
||||||
|
-- by a smaller number
|
||||||
|
SELECT _timescaledb_internal.to_timestamp(9223372036854775806);
|
||||||
|
to_timestamp
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
Sun Jan 10 04:00:54.775807 294247 UTC
|
Sun Jan 10 04:00:54.775806 294247 UTC
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
-- Julian day zero is -210866803200000000 microseconds from UNIX epoch
|
-- Julian day zero is -210866803200000000 microseconds from UNIX epoch
|
||||||
|
@ -100,12 +100,20 @@ SELECT _timescaledb_internal.to_timestamp(1486480176236538);
|
|||||||
-- Should be the inverse of the statement above.
|
-- Should be the inverse of the statement above.
|
||||||
SELECT _timescaledb_internal.to_unix_microseconds('2017-02-07 15:09:36.236538+00');
|
SELECT _timescaledb_internal.to_unix_microseconds('2017-02-07 15:09:36.236538+00');
|
||||||
|
|
||||||
-- In UNIX microseconds, BIGINT MAX is smaller than internal date upper bound
|
-- For timestamps, BIGINT MAX represents +Infinity and BIGINT MIN
|
||||||
-- and should therefore be OK. Further, converting to the internal postgres
|
-- -Infinity. We keep this notion for UNIX epoch time:
|
||||||
-- epoch cannot overflow a 64-bit INTEGER since the postgres epoch is at a
|
SELECT _timescaledb_internal.to_unix_microseconds('+infinity');
|
||||||
-- later date compared to the UNIX epoch, and is therefore represented by a
|
|
||||||
-- smaller number
|
|
||||||
SELECT _timescaledb_internal.to_timestamp(9223372036854775807);
|
SELECT _timescaledb_internal.to_timestamp(9223372036854775807);
|
||||||
|
SELECT _timescaledb_internal.to_unix_microseconds('-infinity');
|
||||||
|
SELECT _timescaledb_internal.to_timestamp(-9223372036854775808);
|
||||||
|
|
||||||
|
-- In UNIX microseconds, the largest bigint value below infinity
|
||||||
|
-- (BIGINT MAX) is smaller than internal date upper bound and should
|
||||||
|
-- therefore be OK. Further, converting to the internal postgres epoch
|
||||||
|
-- cannot overflow a 64-bit INTEGER since the postgres epoch is at a
|
||||||
|
-- later date compared to the UNIX epoch, and is therefore represented
|
||||||
|
-- by a smaller number
|
||||||
|
SELECT _timescaledb_internal.to_timestamp(9223372036854775806);
|
||||||
|
|
||||||
-- Julian day zero is -210866803200000000 microseconds from UNIX epoch
|
-- Julian day zero is -210866803200000000 microseconds from UNIX epoch
|
||||||
SELECT _timescaledb_internal.to_timestamp(-210866803200000000);
|
SELECT _timescaledb_internal.to_timestamp(-210866803200000000);
|
||||||
|
@ -111,7 +111,10 @@ ts_test_time_to_internal_conversion(PG_FUNCTION_ARGS)
|
|||||||
ts_time_value_to_internal(ts_internal_to_time_value(i64, TIMESTAMPOID),
|
ts_time_value_to_internal(ts_internal_to_time_value(i64, TIMESTAMPOID),
|
||||||
TIMESTAMPOID));
|
TIMESTAMPOID));
|
||||||
|
|
||||||
TestEnsureError(ts_internal_to_time_value(PG_INT64_MIN, TIMESTAMPOID));
|
TestAssertInt64Eq(PG_INT64_MIN, ts_time_value_to_internal(DT_NOBEGIN, TIMESTAMPOID));
|
||||||
|
TestAssertInt64Eq(PG_INT64_MAX, ts_time_value_to_internal(DT_NOEND, TIMESTAMPOID));
|
||||||
|
|
||||||
|
TestAssertInt64Eq(DT_NOBEGIN, ts_internal_to_time_value(PG_INT64_MIN, TIMESTAMPOID));
|
||||||
TestEnsureError(ts_internal_to_time_value(TS_INTERNAL_TIMESTAMP_MIN - 1, TIMESTAMPOID));
|
TestEnsureError(ts_internal_to_time_value(TS_INTERNAL_TIMESTAMP_MIN - 1, TIMESTAMPOID));
|
||||||
|
|
||||||
TestAssertInt64Eq(TS_INTERNAL_TIMESTAMP_MIN,
|
TestAssertInt64Eq(TS_INTERNAL_TIMESTAMP_MIN,
|
||||||
@ -119,10 +122,10 @@ ts_test_time_to_internal_conversion(PG_FUNCTION_ARGS)
|
|||||||
TIMESTAMPOID),
|
TIMESTAMPOID),
|
||||||
TIMESTAMPOID));
|
TIMESTAMPOID));
|
||||||
|
|
||||||
TestAssertInt64Eq(PG_INT64_MAX - TS_EPOCH_DIFF_MICROSECONDS,
|
TestAssertInt64Eq(DT_NOEND,
|
||||||
DatumGetInt64(ts_internal_to_time_value(PG_INT64_MAX, TIMESTAMPOID)));
|
(ts_time_value_to_internal(ts_internal_to_time_value(PG_INT64_MAX,
|
||||||
TestEnsureError(ts_time_value_to_internal(ts_internal_to_time_value(PG_INT64_MAX, TIMESTAMPOID),
|
TIMESTAMPOID),
|
||||||
TIMESTAMPOID));
|
TIMESTAMPOID)));
|
||||||
|
|
||||||
/* TIMESTAMPTZ */
|
/* TIMESTAMPTZ */
|
||||||
for (i64 = -100; i64 < 100; i64++)
|
for (i64 = -100; i64 < 100; i64++)
|
||||||
@ -140,7 +143,10 @@ ts_test_time_to_internal_conversion(PG_FUNCTION_ARGS)
|
|||||||
ts_time_value_to_internal(ts_internal_to_time_value(i64, TIMESTAMPTZOID),
|
ts_time_value_to_internal(ts_internal_to_time_value(i64, TIMESTAMPTZOID),
|
||||||
TIMESTAMPTZOID));
|
TIMESTAMPTZOID));
|
||||||
|
|
||||||
TestEnsureError(ts_internal_to_time_value(PG_INT64_MIN, TIMESTAMPTZOID));
|
TestAssertInt64Eq(PG_INT64_MIN, ts_time_value_to_internal(DT_NOBEGIN, TIMESTAMPTZOID));
|
||||||
|
TestAssertInt64Eq(PG_INT64_MAX, ts_time_value_to_internal(DT_NOEND, TIMESTAMPTZOID));
|
||||||
|
|
||||||
|
TestAssertInt64Eq(DT_NOBEGIN, ts_internal_to_time_value(PG_INT64_MIN, TIMESTAMPTZOID));
|
||||||
TestEnsureError(ts_internal_to_time_value(TS_INTERNAL_TIMESTAMP_MIN - 1, TIMESTAMPTZOID));
|
TestEnsureError(ts_internal_to_time_value(TS_INTERNAL_TIMESTAMP_MIN - 1, TIMESTAMPTZOID));
|
||||||
|
|
||||||
TestAssertInt64Eq(TS_INTERNAL_TIMESTAMP_MIN,
|
TestAssertInt64Eq(TS_INTERNAL_TIMESTAMP_MIN,
|
||||||
@ -148,21 +154,19 @@ ts_test_time_to_internal_conversion(PG_FUNCTION_ARGS)
|
|||||||
TIMESTAMPTZOID),
|
TIMESTAMPTZOID),
|
||||||
TIMESTAMPTZOID));
|
TIMESTAMPTZOID));
|
||||||
|
|
||||||
TestAssertInt64Eq(PG_INT64_MAX - TS_EPOCH_DIFF_MICROSECONDS,
|
TestAssertInt64Eq(DT_NOEND,
|
||||||
DatumGetInt64(ts_internal_to_time_value(PG_INT64_MAX, TIMESTAMPTZOID)));
|
ts_time_value_to_internal(ts_internal_to_time_value(PG_INT64_MAX,
|
||||||
TestEnsureError(
|
TIMESTAMPTZOID),
|
||||||
ts_time_value_to_internal(ts_internal_to_time_value(PG_INT64_MAX, TIMESTAMPTZOID),
|
|
||||||
TIMESTAMPTZOID));
|
TIMESTAMPTZOID));
|
||||||
|
|
||||||
/* DATE */
|
/* DATE */
|
||||||
|
|
||||||
for (i64 = -100 * USECS_PER_DAY; i64 < 100 * USECS_PER_DAY; i64 += USECS_PER_DAY)
|
for (i64 = -100 * USECS_PER_DAY; i64 < 100 * USECS_PER_DAY; i64 += USECS_PER_DAY)
|
||||||
TestAssertInt64Eq(i64,
|
TestAssertInt64Eq(i64,
|
||||||
ts_time_value_to_internal(ts_internal_to_time_value(i64, DATEOID),
|
ts_time_value_to_internal(ts_internal_to_time_value(i64, DATEOID),
|
||||||
DATEOID));
|
DATEOID));
|
||||||
|
TestAssertInt64Eq(DATEVAL_NOBEGIN, ts_internal_to_time_value(PG_INT64_MIN, DATEOID));
|
||||||
TestEnsureError(ts_internal_to_time_value(PG_INT64_MIN, DATEOID));
|
TestAssertInt64Eq(DATEVAL_NOEND, ts_internal_to_time_value(PG_INT64_MAX, DATEOID));
|
||||||
TestAssertInt64Eq(106741034, DatumGetDateADT(ts_internal_to_time_value(PG_INT64_MAX, DATEOID)));
|
|
||||||
|
|
||||||
TestEnsureError(ts_time_value_to_internal((DATEVAL_NOBEGIN + 1), DATEOID));
|
TestEnsureError(ts_time_value_to_internal((DATEVAL_NOBEGIN + 1), DATEOID));
|
||||||
TestEnsureError(ts_time_value_to_internal((DATEVAL_NOEND - 1), DATEOID));
|
TestEnsureError(ts_time_value_to_internal((DATEVAL_NOEND - 1), DATEOID));
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user