From eafdf9de06e9b60168f5e47cedcfceecdc6d4b5f Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 20 Apr 2022 18:08:15 -0400 Subject: [PATCH] Disallow infinite endpoints in generate_series() for timestamps. Such cases will lead to infinite loops, so they're of no practical value. The numeric variant of generate_series() already threw error for this, so borrow its message wording. Per report from Richard Wesley. Back-patch to all supported branches. Discussion: https://postgr.es/m/91B44E7B-68D5-448F-95C8-B4B3B0F5DEAF@duckdblabs.com --- src/backend/utils/adt/timestamp.c | 28 +++++++++++++ src/test/regress/expected/timestamp.out | 49 +++++++++++++++++++++++ src/test/regress/expected/timestamptz.out | 49 +++++++++++++++++++++++ src/test/regress/sql/timestamp.sql | 15 +++++++ src/test/regress/sql/timestamptz.sql | 15 +++++++ 5 files changed, 156 insertions(+) diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 552b631ba7..93c10e1ae4 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -5778,6 +5778,20 @@ generate_series_timestamp(PG_FUNCTION_ARGS) MemoryContext oldcontext; Interval interval_zero; + /* Reject infinities in start and stop values */ + if (TIMESTAMP_IS_NOBEGIN(start) || + TIMESTAMP_IS_NOEND(start)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("start value cannot be infinity"))); + if (TIMESTAMP_IS_NOBEGIN(finish) || + TIMESTAMP_IS_NOEND(finish)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("stop value cannot be infinity"))); + + /* Interval doesn't (currently) have infinity, so nothing to check */ + /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); @@ -5858,6 +5872,20 @@ generate_series_timestamptz(PG_FUNCTION_ARGS) MemoryContext oldcontext; Interval interval_zero; + /* Reject infinities in start and stop values */ + if (TIMESTAMP_IS_NOBEGIN(start) || + TIMESTAMP_IS_NOEND(start)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("start value cannot be infinity"))); + if (TIMESTAMP_IS_NOBEGIN(finish) || + TIMESTAMP_IS_NOEND(finish)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("stop value cannot be infinity"))); + + /* Interval doesn't (currently) have infinity, so nothing to check */ + /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); diff --git a/src/test/regress/expected/timestamp.out b/src/test/regress/expected/timestamp.out index 1a2d48cae9..d5ca6b76a4 100644 --- a/src/test/regress/expected/timestamp.out +++ b/src/test/regress/expected/timestamp.out @@ -2019,3 +2019,52 @@ SELECT make_timestamp(-44, 3, 15, 12, 30, 15); -- should fail select make_timestamp(0, 7, 15, 12, 30, 15); ERROR: date field value out of range: 0-07-15 +-- generate_series for timestamp +select * from generate_series('2020-01-01 00:00'::timestamp, + '2020-01-02 03:00'::timestamp, + '1 hour'::interval); + generate_series +-------------------------- + Wed Jan 01 00:00:00 2020 + Wed Jan 01 01:00:00 2020 + Wed Jan 01 02:00:00 2020 + Wed Jan 01 03:00:00 2020 + Wed Jan 01 04:00:00 2020 + Wed Jan 01 05:00:00 2020 + Wed Jan 01 06:00:00 2020 + Wed Jan 01 07:00:00 2020 + Wed Jan 01 08:00:00 2020 + Wed Jan 01 09:00:00 2020 + Wed Jan 01 10:00:00 2020 + Wed Jan 01 11:00:00 2020 + Wed Jan 01 12:00:00 2020 + Wed Jan 01 13:00:00 2020 + Wed Jan 01 14:00:00 2020 + Wed Jan 01 15:00:00 2020 + Wed Jan 01 16:00:00 2020 + Wed Jan 01 17:00:00 2020 + Wed Jan 01 18:00:00 2020 + Wed Jan 01 19:00:00 2020 + Wed Jan 01 20:00:00 2020 + Wed Jan 01 21:00:00 2020 + Wed Jan 01 22:00:00 2020 + Wed Jan 01 23:00:00 2020 + Thu Jan 02 00:00:00 2020 + Thu Jan 02 01:00:00 2020 + Thu Jan 02 02:00:00 2020 + Thu Jan 02 03:00:00 2020 +(28 rows) + +-- errors +select * from generate_series('-infinity'::timestamp, + '2020-01-02 03:00'::timestamp, + '1 hour'::interval); +ERROR: start value cannot be infinity +select * from generate_series('2020-01-01 00:00'::timestamp, + 'infinity'::timestamp, + '1 hour'::interval); +ERROR: stop value cannot be infinity +select * from generate_series('2020-01-01 00:00'::timestamp, + '2020-01-02 03:00'::timestamp, + '0 hour'::interval); +ERROR: step size cannot equal zero diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out index d402802db0..4a29484ec3 100644 --- a/src/test/regress/expected/timestamptz.out +++ b/src/test/regress/expected/timestamptz.out @@ -2362,6 +2362,55 @@ SELECT make_timestamptz(2014, 12, 10, 10, 10, 10, 'PST8PDT'); (1 row) RESET TimeZone; +-- generate_series for timestamptz +select * from generate_series('2020-01-01 00:00'::timestamptz, + '2020-01-02 03:00'::timestamptz, + '1 hour'::interval); + generate_series +------------------------------ + Wed Jan 01 00:00:00 2020 PST + Wed Jan 01 01:00:00 2020 PST + Wed Jan 01 02:00:00 2020 PST + Wed Jan 01 03:00:00 2020 PST + Wed Jan 01 04:00:00 2020 PST + Wed Jan 01 05:00:00 2020 PST + Wed Jan 01 06:00:00 2020 PST + Wed Jan 01 07:00:00 2020 PST + Wed Jan 01 08:00:00 2020 PST + Wed Jan 01 09:00:00 2020 PST + Wed Jan 01 10:00:00 2020 PST + Wed Jan 01 11:00:00 2020 PST + Wed Jan 01 12:00:00 2020 PST + Wed Jan 01 13:00:00 2020 PST + Wed Jan 01 14:00:00 2020 PST + Wed Jan 01 15:00:00 2020 PST + Wed Jan 01 16:00:00 2020 PST + Wed Jan 01 17:00:00 2020 PST + Wed Jan 01 18:00:00 2020 PST + Wed Jan 01 19:00:00 2020 PST + Wed Jan 01 20:00:00 2020 PST + Wed Jan 01 21:00:00 2020 PST + Wed Jan 01 22:00:00 2020 PST + Wed Jan 01 23:00:00 2020 PST + Thu Jan 02 00:00:00 2020 PST + Thu Jan 02 01:00:00 2020 PST + Thu Jan 02 02:00:00 2020 PST + Thu Jan 02 03:00:00 2020 PST +(28 rows) + +-- errors +select * from generate_series('-infinity'::timestamptz, + '2020-01-02 03:00'::timestamptz, + '1 hour'::interval); +ERROR: start value cannot be infinity +select * from generate_series('2020-01-01 00:00'::timestamptz, + 'infinity'::timestamptz, + '1 hour'::interval); +ERROR: stop value cannot be infinity +select * from generate_series('2020-01-01 00:00'::timestamptz, + '2020-01-02 03:00'::timestamptz, + '0 hour'::interval); +ERROR: step size cannot equal zero -- -- Test behavior with a dynamic (time-varying) timezone abbreviation. -- These tests rely on the knowledge that MSK (Europe/Moscow standard time) diff --git a/src/test/regress/sql/timestamp.sql b/src/test/regress/sql/timestamp.sql index e011e779ea..0778e5d7c0 100644 --- a/src/test/regress/sql/timestamp.sql +++ b/src/test/regress/sql/timestamp.sql @@ -370,3 +370,18 @@ SELECT make_timestamp(2014, 12, 28, 6, 30, 45.887); SELECT make_timestamp(-44, 3, 15, 12, 30, 15); -- should fail select make_timestamp(0, 7, 15, 12, 30, 15); + +-- generate_series for timestamp +select * from generate_series('2020-01-01 00:00'::timestamp, + '2020-01-02 03:00'::timestamp, + '1 hour'::interval); +-- errors +select * from generate_series('-infinity'::timestamp, + '2020-01-02 03:00'::timestamp, + '1 hour'::interval); +select * from generate_series('2020-01-01 00:00'::timestamp, + 'infinity'::timestamp, + '1 hour'::interval); +select * from generate_series('2020-01-01 00:00'::timestamp, + '2020-01-02 03:00'::timestamp, + '0 hour'::interval); diff --git a/src/test/regress/sql/timestamptz.sql b/src/test/regress/sql/timestamptz.sql index 7cd2420b08..c1643de0f1 100644 --- a/src/test/regress/sql/timestamptz.sql +++ b/src/test/regress/sql/timestamptz.sql @@ -432,6 +432,21 @@ SELECT make_timestamptz(2014, 12, 10, 10, 10, 10, 'PST8PDT'); RESET TimeZone; +-- generate_series for timestamptz +select * from generate_series('2020-01-01 00:00'::timestamptz, + '2020-01-02 03:00'::timestamptz, + '1 hour'::interval); +-- errors +select * from generate_series('-infinity'::timestamptz, + '2020-01-02 03:00'::timestamptz, + '1 hour'::interval); +select * from generate_series('2020-01-01 00:00'::timestamptz, + 'infinity'::timestamptz, + '1 hour'::interval); +select * from generate_series('2020-01-01 00:00'::timestamptz, + '2020-01-02 03:00'::timestamptz, + '0 hour'::interval); + -- -- Test behavior with a dynamic (time-varying) timezone abbreviation. -- These tests rely on the knowledge that MSK (Europe/Moscow standard time)