From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Wed, 20 Apr 2022 22:08:15 +0000 (-0400)
Subject: Disallow infinite endpoints in generate_series() for timestamps.
X-Git-Tag: REL_10_21~22
X-Git-Url: http://git.postgresql.org/gitweb/?a=commitdiff_plain;h=a1e4782a0bca3f19f05042c6f05675dbf4fcacc7;p=postgresql.git

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
---

diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 199c0a2e7ba..7421031f2f7 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -5165,6 +5165,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();
 
@@ -5246,6 +5260,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 06d32e221be..ec4f364c3b1 100644
--- a/src/test/regress/expected/timestamp.out
+++ b/src/test/regress/expected/timestamp.out
@@ -1744,3 +1744,52 @@ SELECT make_timestamp(2014,12,28,6,30,45.887);
  Sun Dec 28 06:30:45.887 2014
 (1 row)
 
+-- 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 2c9f843ffa2..32be7716c2d 100644
--- a/src/test/regress/expected/timestamptz.out
+++ b/src/test/regress/expected/timestamptz.out
@@ -2072,6 +2072,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 2cd4e769d87..5a8defa9bbe 100644
--- a/src/test/regress/sql/timestamp.sql
+++ b/src/test/regress/sql/timestamp.sql
@@ -243,3 +243,18 @@ SELECT i,
 
 -- timestamp numeric fields constructor
 SELECT make_timestamp(2014,12,28,6,30,45.887);
+
+-- 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 5584721f319..0483ee3e556 100644
--- a/src/test/regress/sql/timestamptz.sql
+++ b/src/test/regress/sql/timestamptz.sql
@@ -330,6 +330,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)