From fc5f028b22c2b707e1b0a9a3e984060bd4c74f97 Mon Sep 17 00:00:00 2001 From: luo_zihao5524 Date: Tue, 26 Jul 2022 09:59:54 +0800 Subject: [PATCH] allow infinity to timestamp --- src/common/backend/catalog/builtin_funcs.ini | 2 +- src/common/backend/utils/adt/timestamp.cpp | 42 +++++++++++++++++++ src/common/backend/utils/init/globals.cpp | 2 +- .../rollback_catalog_maindb_92_611.sql | 3 ++ .../rollback_catalog_otherdb_92_611.sql | 3 ++ .../upgrade-post_catalog_maindb_92_611.sql | 3 ++ .../upgrade-post_catalog_otherdb_92_611.sql | 3 ++ src/include/datatype/timestamp.h | 12 ++++++ src/test/regress/expected/hw_to_timestamp.out | 15 +++++++ src/test/regress/sql/hw_to_timestamp.sql | 4 ++ 10 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback_catalog_maindb_92_611.sql create mode 100644 src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback_catalog_otherdb_92_611.sql create mode 100644 src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade-post_catalog_maindb_92_611.sql create mode 100644 src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade-post_catalog_otherdb_92_611.sql diff --git a/src/common/backend/catalog/builtin_funcs.ini b/src/common/backend/catalog/builtin_funcs.ini index d89b24050..bd59d4584 100755 --- a/src/common/backend/catalog/builtin_funcs.ini +++ b/src/common/backend/catalog/builtin_funcs.ini @@ -11395,7 +11395,7 @@ ), AddFuncGroup( "to_timestamp", 3, - AddBuiltinFunc(_0(1158), _1("to_timestamp"), _2(1), _3(true), _4(false), _5(NULL), _6(1184), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(SQLlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('i'), _19(0), _20(1, 701), _21(NULL), _22(NULL), _23(NULL), _24(NULL), _25("select ('epoch'::pg_catalog.timestamptz + $1 * '1 second'::pg_catalog.interval)"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)), + AddBuiltinFunc(_0(1158), _1("to_timestamp"), _2(1), _3(true), _4(false), _5(float8_timestamptz), _6(1184), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('i'), _19(0), _20(1, 701), _21(NULL), _22(NULL), _23(NULL), _24(NULL), _25("float8_timestamptz"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)), AddBuiltinFunc(_0(TOTIMESTAMPFUNCOID), _1("to_timestamp"), _2(2), _3(true), _4(false), _5(to_timestamp), _6(1114), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('s'), _19(0), _20(2, 25, 25), _21(NULL), _22(NULL), _23(NULL), _24(NULL), _25("to_timestamp"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(true), _32(false), _33("convert text to timestamp with time zone"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)), AddBuiltinFunc(_0(TOTIMESTAMPDEFAULTFUNCOID), _1("to_timestamp"), _2(1), _3(true), _4(false), _5(to_timestamp_default_format), _6(1114), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('s'), _19(0), _20(1, 25), _21(NULL), _22(NULL), _23(NULL), _24(NULL), _25("to_timestamp_default_format"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(true), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) ), diff --git a/src/common/backend/utils/adt/timestamp.cpp b/src/common/backend/utils/adt/timestamp.cpp index d41ed9748..a7149ee38 100644 --- a/src/common/backend/utils/adt/timestamp.cpp +++ b/src/common/backend/utils/adt/timestamp.cpp @@ -5840,3 +5840,45 @@ void WalReplicationTimestampToString(WalReplicationTimestampInfo *timeStampInfo, MAXTIMESTAMPLEN + 1); securec_check(rc, "\0", "\0"); } + +/* + * to_timestamp(double precision) + * Convert UNIX epoch to timestamptz. + */ +Datum float8_timestamptz(PG_FUNCTION_ARGS) +{ + float8 seconds = PG_GETARG_FLOAT8(0); + TimestampTz result; + + /* Deal with NaN and infinite inputs ... */ + if (isnan(seconds)) { + ereport(ERROR, (errmsg("timestamp cannot be NaN"))); + } + + if (isinf(seconds)) { + if (seconds < 0) { + TIMESTAMP_NOBEGIN(result); + } else { + TIMESTAMP_NOEND(result); + } + } else { + /* Out of range? */ + if (seconds < (float8) SECS_PER_DAY * (DATETIME_MIN_JULIAN - UNIX_EPOCH_JDATE) || + seconds >= (float8) SECS_PER_DAY * (TIMESTAMP_END_JULIAN - UNIX_EPOCH_JDATE)) { + ereport(ERROR, (errmsg("timestamp out of range: \"%g\"", seconds))); + } + + /* Convert UNIX epoch to Postgres epoch */ + seconds -= ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY); + + seconds = rint(seconds * USECS_PER_SEC); + result = (int64) seconds; + + /* Recheck in case roundoff produces something just out of range */ + if (!IS_VALID_TIMESTAMP(result)) { + ereport(ERROR, (errmsg("timestamp out of range: \"%g\"", PG_GETARG_FLOAT8(0)))); + } + } + + PG_RETURN_TIMESTAMP(result); +} diff --git a/src/common/backend/utils/init/globals.cpp b/src/common/backend/utils/init/globals.cpp index 0089318eb..ffa9499bb 100644 --- a/src/common/backend/utils/init/globals.cpp +++ b/src/common/backend/utils/init/globals.cpp @@ -59,7 +59,7 @@ bool open_join_children = true; bool will_shutdown = false; /* hard-wired binary version number */ -const uint32 GRAND_VERSION_NUM = 92610; +const uint32 GRAND_VERSION_NUM = 92611; const uint32 PREDPUSH_SAME_LEVEL_VERSION_NUM = 92522; const uint32 UPSERT_WHERE_VERSION_NUM = 92514; diff --git a/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback_catalog_maindb_92_611.sql b/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback_catalog_maindb_92_611.sql new file mode 100644 index 000000000..bac1ae505 --- /dev/null +++ b/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback_catalog_maindb_92_611.sql @@ -0,0 +1,3 @@ +DROP FUNCTION IF EXISTS pg_catalog.to_timestamp(float8); +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_PROC, 1158; +CREATE FUNCTION pg_catalog.to_timestamp(float8) RETURNS timestamptz LANGUAGE SQL AS 'select (''epoch''::pg_catalog.timestamptz + $1 * ''1 second''::pg_catalog.interval)'; \ No newline at end of file diff --git a/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback_catalog_otherdb_92_611.sql b/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback_catalog_otherdb_92_611.sql new file mode 100644 index 000000000..bac1ae505 --- /dev/null +++ b/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback_catalog_otherdb_92_611.sql @@ -0,0 +1,3 @@ +DROP FUNCTION IF EXISTS pg_catalog.to_timestamp(float8); +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_PROC, 1158; +CREATE FUNCTION pg_catalog.to_timestamp(float8) RETURNS timestamptz LANGUAGE SQL AS 'select (''epoch''::pg_catalog.timestamptz + $1 * ''1 second''::pg_catalog.interval)'; \ No newline at end of file diff --git a/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade-post_catalog_maindb_92_611.sql b/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade-post_catalog_maindb_92_611.sql new file mode 100644 index 000000000..8a0a89a77 --- /dev/null +++ b/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade-post_catalog_maindb_92_611.sql @@ -0,0 +1,3 @@ +DROP FUNCTION IF EXISTS pg_catalog.to_timestamp(float8); +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_PROC, 1158; +CREATE FUNCTION pg_catalog.to_timestamp(float8) RETURNS timestamptz LANGUAGE INTERNAL STABLE AS 'float8_timestamptz'; \ No newline at end of file diff --git a/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade-post_catalog_otherdb_92_611.sql b/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade-post_catalog_otherdb_92_611.sql new file mode 100644 index 000000000..8a0a89a77 --- /dev/null +++ b/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade-post_catalog_otherdb_92_611.sql @@ -0,0 +1,3 @@ +DROP FUNCTION IF EXISTS pg_catalog.to_timestamp(float8); +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_PROC, 1158; +CREATE FUNCTION pg_catalog.to_timestamp(float8) RETURNS timestamptz LANGUAGE INTERNAL STABLE AS 'float8_timestamptz'; \ No newline at end of file diff --git a/src/include/datatype/timestamp.h b/src/include/datatype/timestamp.h index 2f5492266..e02651132 100644 --- a/src/include/datatype/timestamp.h +++ b/src/include/datatype/timestamp.h @@ -156,6 +156,18 @@ typedef struct { #define TIMESTAMP_NOT_FINITE(j) (TIMESTAMP_IS_NOBEGIN(j) || TIMESTAMP_IS_NOEND(j)) +/* Timestamp limits */ +#define MIN_TIMESTAMP INT64CONST(-211813488000000000) +#define END_TIMESTAMP INT64CONST(9223371331200000000) + +/* First allowed date, and first disallowed date, in Julian-date form */ +#define DATETIME_MIN_JULIAN (0) +#define DATE_END_JULIAN (2147483494) /* == date2j(JULIAN_MAXYEAR, 1, 1) */ +#define TIMESTAMP_END_JULIAN (109203528) /* == date2j(294277, 1, 1) */ + +/* Range-check a timestamp */ +#define IS_VALID_TIMESTAMP(t) (MIN_TIMESTAMP <= (t) && (t) < END_TIMESTAMP) + /* * Julian date support. * diff --git a/src/test/regress/expected/hw_to_timestamp.out b/src/test/regress/expected/hw_to_timestamp.out index 293476f82..4a3d4f25a 100644 --- a/src/test/regress/expected/hw_to_timestamp.out +++ b/src/test/regress/expected/hw_to_timestamp.out @@ -1020,3 +1020,18 @@ end; NOTICE: @ 0 NOTICE: @ 0 NOTICE: @ 0 +SELECT to_timestamp(' Infinity'::float); + to_timestamp +-------------- + infinity +(1 row) + +SELECT to_timestamp('-Infinity'::float); + to_timestamp +-------------- + -infinity +(1 row) + +SELECT to_timestamp('NaN'::float); +ERROR: timestamp cannot be NaN +CONTEXT: referenced column: to_timestamp diff --git a/src/test/regress/sql/hw_to_timestamp.sql b/src/test/regress/sql/hw_to_timestamp.sql index bee0e0c45..c4b0d54e8 100644 --- a/src/test/regress/sql/hw_to_timestamp.sql +++ b/src/test/regress/sql/hw_to_timestamp.sql @@ -265,3 +265,7 @@ begin set timezone to default; end; / + +SELECT to_timestamp(' Infinity'::float); +SELECT to_timestamp('-Infinity'::float); +SELECT to_timestamp('NaN'::float);