From 3ba70d4e152372fd8ab90ed0887ee08d505ef306 Mon Sep 17 00:00:00 2001 From: John Naylor Date: Wed, 28 Jul 2021 11:22:58 -0400 Subject: [PATCH] Disallow negative strides in date_bin() It's not clear what the semantics of negative strides would be, so throw an error instead. Per report from Bauyrzhan Sakhariyev Reviewed-by: Tom Lane, Michael Paquier Discussion: https://www.postgresql.org/message-id/CAKpL73vZmLuFVuwF26FJ%2BNk11PVHhAnQRoREFcA03x7znRoFvA%40mail.gmail.com Backpatch to v14 --- doc/src/sgml/func.sgml | 8 ++------ src/backend/utils/adt/timestamp.c | 8 ++++---- src/test/regress/expected/timestamp.out | 5 ++++- src/test/regress/expected/timestamptz.out | 5 ++++- src/test/regress/sql/timestamp.sql | 3 +++ src/test/regress/sql/timestamptz.sql | 3 +++ 6 files changed, 20 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 741dfbae4a5..c12d03e5832 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -10008,12 +10008,8 @@ SELECT date_bin('15 minutes', TIMESTAMP '2020-02-11 15:44:17', TIMESTAMP '2001-0 - Negative intervals are allowed and are treated the same as positive intervals. - - - - The stride interval cannot contain units of month - or larger. + The stride interval must be greater than zero and + cannot contain units of month or larger. diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index ea847576cd2..1c0bf0aa5c8 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -3843,10 +3843,10 @@ timestamp_bin(PG_FUNCTION_ARGS) stride_usecs = stride->day * USECS_PER_DAY + stride->time; - if (stride_usecs == 0) + if (stride_usecs <= 0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("stride cannot equal zero"))); + errmsg("stride must be greater than zero"))); tm_diff = timestamp - origin; tm_delta = tm_diff - tm_diff % stride_usecs; @@ -4026,10 +4026,10 @@ timestamptz_bin(PG_FUNCTION_ARGS) stride_usecs = stride->day * USECS_PER_DAY + stride->time; - if (stride_usecs == 0) + if (stride_usecs <= 0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("stride cannot equal zero"))); + errmsg("stride must be greater than zero"))); tm_diff = timestamp - origin; tm_delta = tm_diff - tm_diff % stride_usecs; diff --git a/src/test/regress/expected/timestamp.out b/src/test/regress/expected/timestamp.out index 7a2cbd9b3f0..1a2d48cae96 100644 --- a/src/test/regress/expected/timestamp.out +++ b/src/test/regress/expected/timestamp.out @@ -706,7 +706,10 @@ SELECT date_bin('5 years'::interval, timestamp '2020-02-01 01:01:01', timestamp ERROR: timestamps cannot be binned into intervals containing months or years -- disallow zero intervals SELECT date_bin('0 days'::interval, timestamp '1970-01-01 01:00:00' , timestamp '1970-01-01 00:00:00'); -ERROR: stride cannot equal zero +ERROR: stride must be greater than zero +-- disallow negative intervals +SELECT date_bin('-2 days'::interval, timestamp '1970-01-01 01:00:00' , timestamp '1970-01-01 00:00:00'); +ERROR: stride must be greater than zero -- Test casting within a BETWEEN qualifier SELECT d1 - timestamp without time zone '1997-01-02' AS diff FROM TIMESTAMP_TBL diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out index be6ead0fb50..990c4eddf13 100644 --- a/src/test/regress/expected/timestamptz.out +++ b/src/test/regress/expected/timestamptz.out @@ -750,7 +750,10 @@ SELECT date_bin('5 years'::interval, timestamp with time zone '2020-02-01 01:01 ERROR: timestamps cannot be binned into intervals containing months or years -- disallow zero intervals SELECT date_bin('0 days'::interval, timestamp with time zone '1970-01-01 01:00:00+00' , timestamp with time zone '1970-01-01 00:00:00+00'); -ERROR: stride cannot equal zero +ERROR: stride must be greater than zero +-- disallow negative intervals +SELECT date_bin('-2 days'::interval, timestamp with time zone '1970-01-01 01:00:00+00' , timestamp with time zone '1970-01-01 00:00:00+00'); +ERROR: stride must be greater than zero -- Test casting within a BETWEEN qualifier SELECT d1 - timestamp with time zone '1997-01-02' AS diff FROM TIMESTAMPTZ_TBL diff --git a/src/test/regress/sql/timestamp.sql b/src/test/regress/sql/timestamp.sql index 7307a240927..e011e779ea2 100644 --- a/src/test/regress/sql/timestamp.sql +++ b/src/test/regress/sql/timestamp.sql @@ -266,6 +266,9 @@ SELECT date_bin('5 years'::interval, timestamp '2020-02-01 01:01:01', timestamp -- disallow zero intervals SELECT date_bin('0 days'::interval, timestamp '1970-01-01 01:00:00' , timestamp '1970-01-01 00:00:00'); +-- disallow negative intervals +SELECT date_bin('-2 days'::interval, timestamp '1970-01-01 01:00:00' , timestamp '1970-01-01 00:00:00'); + -- Test casting within a BETWEEN qualifier SELECT d1 - timestamp without time zone '1997-01-02' AS diff FROM TIMESTAMP_TBL diff --git a/src/test/regress/sql/timestamptz.sql b/src/test/regress/sql/timestamptz.sql index 3642d8c143c..b18821de53b 100644 --- a/src/test/regress/sql/timestamptz.sql +++ b/src/test/regress/sql/timestamptz.sql @@ -241,6 +241,9 @@ SELECT date_bin('5 years'::interval, timestamp with time zone '2020-02-01 01:01 -- disallow zero intervals SELECT date_bin('0 days'::interval, timestamp with time zone '1970-01-01 01:00:00+00' , timestamp with time zone '1970-01-01 00:00:00+00'); +-- disallow negative intervals +SELECT date_bin('-2 days'::interval, timestamp with time zone '1970-01-01 01:00:00+00' , timestamp with time zone '1970-01-01 00:00:00+00'); + -- Test casting within a BETWEEN qualifier SELECT d1 - timestamp with time zone '1997-01-02' AS diff FROM TIMESTAMPTZ_TBL