Attached is the described patch. I have two notes after implementing it: - It feels like a bit of an abstraction break to set tmask without actually setting any fields in tm. - I'm not sure if we should hard code in those three specific reserved keywords or set tmask in the default case.
Any thoughts? - Joe Koshakow
From 78d8f39db8df68502369ffd9edd6f6e38f4dadb8 Mon Sep 17 00:00:00 2001 From: Joseph Koshakow <kosh...@gmail.com> Date: Sun, 11 Dec 2022 16:08:43 -0500 Subject: [PATCH] Handle extraneous fields in date-time input DecodeDateTime sometimest allowed extraneous fields to be included with reserved keywords. For example `date '1995-08-06 epoch'` would be parsed successfully, but the date was ignored. This commit fixes the issue so an error is returned instead. --- src/backend/utils/adt/datetime.c | 18 ++++++++++--- src/test/regress/expected/date.out | 33 +++++++++++++++++++++++ src/test/regress/expected/timestamp.out | 33 +++++++++++++++++++++++ src/test/regress/expected/timestamptz.out | 33 +++++++++++++++++++++++ src/test/regress/sql/date.sql | 10 +++++++ src/test/regress/sql/timestamp.sql | 10 +++++++ src/test/regress/sql/timestamptz.sql | 10 +++++++ 7 files changed, 143 insertions(+), 4 deletions(-) diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 01660637a2..6f82465fd1 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -1431,6 +1431,13 @@ DecodeDateTime(char **field, int *ftype, int nf, *tzp = 0; break; + case DTK_EPOCH: + case DTK_LATE: + case DTK_EARLY: + tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ)); + *dtype = val; + break; + default: *dtype = val; } @@ -1567,10 +1574,13 @@ DecodeDateTime(char **field, int *ftype, int nf, fmask |= tmask; } /* end loop over fields */ - /* do final checking/adjustment of Y/M/D fields */ - dterr = ValidateDate(fmask, isjulian, is2digits, bc, tm); - if (dterr) - return dterr; + if (*dtype == DTK_DATE) + { + /* do final checking/adjustment of Y/M/D fields */ + dterr = ValidateDate(fmask, isjulian, is2digits, bc, tm); + if (dterr) + return dterr; + } /* handle AM/PM */ if (mer != HR24 && tm->tm_hour > HOURS_PER_DAY / 2) diff --git a/src/test/regress/expected/date.out b/src/test/regress/expected/date.out index f5949f3d17..c874f06546 100644 --- a/src/test/regress/expected/date.out +++ b/src/test/regress/expected/date.out @@ -1532,3 +1532,36 @@ select make_time(10, 55, 100.1); ERROR: time field value out of range: 10:55:100.1 select make_time(24, 0, 2.1); ERROR: time field value out of range: 24:00:2.1 +-- test errors with reserved keywords +SELECT date '1995-08-06 epoch'; +ERROR: invalid input syntax for type date: "1995-08-06 epoch" +LINE 1: SELECT date '1995-08-06 epoch'; + ^ +SELECT date '1995-08-06 infinity'; +ERROR: invalid input syntax for type date: "1995-08-06 infinity" +LINE 1: SELECT date '1995-08-06 infinity'; + ^ +SELECT date '1995-08-06 -infinity'; +ERROR: invalid input syntax for type date: "1995-08-06 -infinity" +LINE 1: SELECT date '1995-08-06 -infinity'; + ^ +SELECT date 'epoch 1995-08-06'; +ERROR: invalid input syntax for type date: "epoch 1995-08-06" +LINE 1: SELECT date 'epoch 1995-08-06'; + ^ +SELECT date 'infinity 1995-08-06'; +ERROR: invalid input syntax for type date: "infinity 1995-08-06" +LINE 1: SELECT date 'infinity 1995-08-06'; + ^ +SELECT date '-infinity 1995-08-06'; +ERROR: invalid input syntax for type date: "-infinity 1995-08-06" +LINE 1: SELECT date '-infinity 1995-08-06'; + ^ +SELECT date 'now infinity'; +ERROR: invalid input syntax for type date: "now infinity" +LINE 1: SELECT date 'now infinity'; + ^ +SELECT date '-infinity infinity'; +ERROR: invalid input syntax for type date: "-infinity infinity" +LINE 1: SELECT date '-infinity infinity'; + ^ diff --git a/src/test/regress/expected/timestamp.out b/src/test/regress/expected/timestamp.out index c64bcb7c12..c2159c2cec 100644 --- a/src/test/regress/expected/timestamp.out +++ b/src/test/regress/expected/timestamp.out @@ -2125,3 +2125,36 @@ 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 +-- test errors with reserved keywords +SELECT timestamp '1995-08-06 01:01:01 epoch'; +ERROR: invalid input syntax for type timestamp: "1995-08-06 01:01:01 epoch" +LINE 1: SELECT timestamp '1995-08-06 01:01:01 epoch'; + ^ +SELECT timestamp '1995-08-06 01:01:01 infinity'; +ERROR: invalid input syntax for type timestamp: "1995-08-06 01:01:01 infinity" +LINE 1: SELECT timestamp '1995-08-06 01:01:01 infinity'; + ^ +SELECT timestamp '1995-08-06 01:01:01 -infinity'; +ERROR: invalid input syntax for type timestamp: "1995-08-06 01:01:01 -infinity" +LINE 1: SELECT timestamp '1995-08-06 01:01:01 -infinity'; + ^ +SELECT timestamp 'epoch 1995-08-06 01:01:01'; +ERROR: invalid input syntax for type timestamp: "epoch 1995-08-06 01:01:01" +LINE 1: SELECT timestamp 'epoch 1995-08-06 01:01:01'; + ^ +SELECT timestamp 'infinity 1995-08-06 01:01:01'; +ERROR: invalid input syntax for type timestamp: "infinity 1995-08-06 01:01:01" +LINE 1: SELECT timestamp 'infinity 1995-08-06 01:01:01'; + ^ +SELECT timestamp '-infinity 1995-08-06 01:01:01'; +ERROR: invalid input syntax for type timestamp: "-infinity 1995-08-06 01:01:01" +LINE 1: SELECT timestamp '-infinity 1995-08-06 01:01:01'; + ^ +SELECT timestamp 'today epoch'; +ERROR: invalid input syntax for type timestamp: "today epoch" +LINE 1: SELECT timestamp 'today epoch'; + ^ +SELECT timestamp '-infinity infinity'; +ERROR: invalid input syntax for type timestamp: "-infinity infinity" +LINE 1: SELECT timestamp '-infinity infinity'; + ^ diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out index 91d7c1f5cc..2f67ba9cc9 100644 --- a/src/test/regress/expected/timestamptz.out +++ b/src/test/regress/expected/timestamptz.out @@ -3100,3 +3100,36 @@ select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00'; Tue Jan 17 16:00:00 2017 PST (1 row) +-- test errors with reserved keywords +SELECT timestamptz '1995-08-06 01:01:01 epoch'; +ERROR: invalid input syntax for type timestamp with time zone: "1995-08-06 01:01:01 epoch" +LINE 1: SELECT timestamptz '1995-08-06 01:01:01 epoch'; + ^ +SELECT timestamptz '1995-08-06 01:01:01 infinity'; +ERROR: invalid input syntax for type timestamp with time zone: "1995-08-06 01:01:01 infinity" +LINE 1: SELECT timestamptz '1995-08-06 01:01:01 infinity'; + ^ +SELECT timestamptz '1995-08-06 01:01:01 -infinity'; +ERROR: invalid input syntax for type timestamp with time zone: "1995-08-06 01:01:01 -infinity" +LINE 1: SELECT timestamptz '1995-08-06 01:01:01 -infinity'; + ^ +SELECT timestamptz 'epoch 1995-08-06 01:01:01'; +ERROR: invalid input syntax for type timestamp with time zone: "epoch 1995-08-06 01:01:01" +LINE 1: SELECT timestamptz 'epoch 1995-08-06 01:01:01'; + ^ +SELECT timestamptz 'infinity 1995-08-06 01:01:01'; +ERROR: invalid input syntax for type timestamp with time zone: "infinity 1995-08-06 01:01:01" +LINE 1: SELECT timestamptz 'infinity 1995-08-06 01:01:01'; + ^ +SELECT timestamptz '-infinity 1995-08-06 01:01:01'; +ERROR: invalid input syntax for type timestamp with time zone: "-infinity 1995-08-06 01:01:01" +LINE 1: SELECT timestamptz '-infinity 1995-08-06 01:01:01'; + ^ +SELECT timestamptz 'today epoch'; +ERROR: invalid input syntax for type timestamp with time zone: "today epoch" +LINE 1: SELECT timestamptz 'today epoch'; + ^ +SELECT timestamptz '-infinity infinity'; +ERROR: invalid input syntax for type timestamp with time zone: "-infinity infinity" +LINE 1: SELECT timestamptz '-infinity infinity'; + ^ diff --git a/src/test/regress/sql/date.sql b/src/test/regress/sql/date.sql index 1c58ff6966..5e7457b155 100644 --- a/src/test/regress/sql/date.sql +++ b/src/test/regress/sql/date.sql @@ -373,3 +373,13 @@ select make_date(2013, 13, 1); select make_date(2013, 11, -1); select make_time(10, 55, 100.1); select make_time(24, 0, 2.1); + +-- test errors with reserved keywords +SELECT date '1995-08-06 epoch'; +SELECT date '1995-08-06 infinity'; +SELECT date '1995-08-06 -infinity'; +SELECT date 'epoch 1995-08-06'; +SELECT date 'infinity 1995-08-06'; +SELECT date '-infinity 1995-08-06'; +SELECT date 'now infinity'; +SELECT date '-infinity infinity'; diff --git a/src/test/regress/sql/timestamp.sql b/src/test/regress/sql/timestamp.sql index b9bcce9cfe..18b51e3b46 100644 --- a/src/test/regress/sql/timestamp.sql +++ b/src/test/regress/sql/timestamp.sql @@ -397,3 +397,13 @@ select generate_series('2022-01-01 00:00'::timestamp, select * from generate_series('2020-01-01 00:00'::timestamp, '2020-01-02 03:00'::timestamp, '0 hour'::interval); + +-- test errors with reserved keywords +SELECT timestamp '1995-08-06 01:01:01 epoch'; +SELECT timestamp '1995-08-06 01:01:01 infinity'; +SELECT timestamp '1995-08-06 01:01:01 -infinity'; +SELECT timestamp 'epoch 1995-08-06 01:01:01'; +SELECT timestamp 'infinity 1995-08-06 01:01:01'; +SELECT timestamp '-infinity 1995-08-06 01:01:01'; +SELECT timestamp 'today epoch'; +SELECT timestamp '-infinity infinity'; diff --git a/src/test/regress/sql/timestamptz.sql b/src/test/regress/sql/timestamptz.sql index ae9ee4b56a..f91b6418d1 100644 --- a/src/test/regress/sql/timestamptz.sql +++ b/src/test/regress/sql/timestamptz.sql @@ -600,3 +600,13 @@ insert into tmptz values ('2017-01-18 00:00+00'); explain (costs off) select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00'; select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00'; + +-- test errors with reserved keywords +SELECT timestamptz '1995-08-06 01:01:01 epoch'; +SELECT timestamptz '1995-08-06 01:01:01 infinity'; +SELECT timestamptz '1995-08-06 01:01:01 -infinity'; +SELECT timestamptz 'epoch 1995-08-06 01:01:01'; +SELECT timestamptz 'infinity 1995-08-06 01:01:01'; +SELECT timestamptz '-infinity 1995-08-06 01:01:01'; +SELECT timestamptz 'today epoch'; +SELECT timestamptz '-infinity infinity'; -- 2.34.1