aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2024-12-07 13:12:32 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2024-12-07 13:12:32 -0500
commit5882a4ba0917c056d9ca78d61af44708b3e93b4c (patch)
treef5f2fe76a1691136bf88f331d713c081e6f25859
parentd24eb0e91f3f7fc54ed4e56d93b26e2f8d52c1ce (diff)
downloadpostgresql-5882a4ba0917c056d9ca78d61af44708b3e93b4c.tar.gz
postgresql-5882a4ba0917c056d9ca78d61af44708b3e93b4c.zip
Fix is_digit labeling of to_timestamp's FFn format codes.
These format codes produce or consume strings of digits, so they should be labeled with is_digit = true, but they were not. This has effect in only one place, where is_next_separator() is checked to see if the preceding format code should slurp up all the available digits. Thus, with a format such as '...SSFF3' with remaining input '12345', the 'SS' code would consume all five digits (and then complain about seconds being out of range) when it should eat only two digits. Per report from Nick Davies. This bug goes back to d589f9446 where the FFn codes were introduced, so back-patch to v13. Discussion: https://postgr.es/m/AM8PR08MB6356AC979252CFEA78B56678B6312@AM8PR08MB6356.eurprd08.prod.outlook.com
-rw-r--r--src/backend/utils/adt/formatting.c26
-rw-r--r--src/test/regress/expected/horology.out11
-rw-r--r--src/test/regress/sql/horology.sql1
3 files changed, 25 insertions, 13 deletions
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index 5bd4d1967b9..9169539b76d 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -624,7 +624,7 @@ typedef enum
DCH_Day,
DCH_Dy,
DCH_D,
- DCH_FF1,
+ DCH_FF1, /* FFn codes must be consecutive */
DCH_FF2,
DCH_FF3,
DCH_FF4,
@@ -787,12 +787,12 @@ static const KeyWord DCH_keywords[] = {
{"Day", 3, DCH_Day, false, FROM_CHAR_DATE_NONE},
{"Dy", 2, DCH_Dy, false, FROM_CHAR_DATE_NONE},
{"D", 1, DCH_D, true, FROM_CHAR_DATE_GREGORIAN},
- {"FF1", 3, DCH_FF1, false, FROM_CHAR_DATE_NONE}, /* F */
- {"FF2", 3, DCH_FF2, false, FROM_CHAR_DATE_NONE},
- {"FF3", 3, DCH_FF3, false, FROM_CHAR_DATE_NONE},
- {"FF4", 3, DCH_FF4, false, FROM_CHAR_DATE_NONE},
- {"FF5", 3, DCH_FF5, false, FROM_CHAR_DATE_NONE},
- {"FF6", 3, DCH_FF6, false, FROM_CHAR_DATE_NONE},
+ {"FF1", 3, DCH_FF1, true, FROM_CHAR_DATE_NONE}, /* F */
+ {"FF2", 3, DCH_FF2, true, FROM_CHAR_DATE_NONE},
+ {"FF3", 3, DCH_FF3, true, FROM_CHAR_DATE_NONE},
+ {"FF4", 3, DCH_FF4, true, FROM_CHAR_DATE_NONE},
+ {"FF5", 3, DCH_FF5, true, FROM_CHAR_DATE_NONE},
+ {"FF6", 3, DCH_FF6, true, FROM_CHAR_DATE_NONE},
{"FX", 2, DCH_FX, false, FROM_CHAR_DATE_NONE},
{"HH24", 4, DCH_HH24, true, FROM_CHAR_DATE_NONE}, /* H */
{"HH12", 4, DCH_HH12, true, FROM_CHAR_DATE_NONE},
@@ -843,12 +843,12 @@ static const KeyWord DCH_keywords[] = {
{"dd", 2, DCH_DD, true, FROM_CHAR_DATE_GREGORIAN},
{"dy", 2, DCH_dy, false, FROM_CHAR_DATE_NONE},
{"d", 1, DCH_D, true, FROM_CHAR_DATE_GREGORIAN},
- {"ff1", 3, DCH_FF1, false, FROM_CHAR_DATE_NONE}, /* f */
- {"ff2", 3, DCH_FF2, false, FROM_CHAR_DATE_NONE},
- {"ff3", 3, DCH_FF3, false, FROM_CHAR_DATE_NONE},
- {"ff4", 3, DCH_FF4, false, FROM_CHAR_DATE_NONE},
- {"ff5", 3, DCH_FF5, false, FROM_CHAR_DATE_NONE},
- {"ff6", 3, DCH_FF6, false, FROM_CHAR_DATE_NONE},
+ {"ff1", 3, DCH_FF1, true, FROM_CHAR_DATE_NONE}, /* f */
+ {"ff2", 3, DCH_FF2, true, FROM_CHAR_DATE_NONE},
+ {"ff3", 3, DCH_FF3, true, FROM_CHAR_DATE_NONE},
+ {"ff4", 3, DCH_FF4, true, FROM_CHAR_DATE_NONE},
+ {"ff5", 3, DCH_FF5, true, FROM_CHAR_DATE_NONE},
+ {"ff6", 3, DCH_FF6, true, FROM_CHAR_DATE_NONE},
{"fx", 2, DCH_FX, false, FROM_CHAR_DATE_NONE},
{"hh24", 4, DCH_HH24, true, FROM_CHAR_DATE_NONE}, /* h */
{"hh12", 4, DCH_HH12, true, FROM_CHAR_DATE_NONE},
diff --git a/src/test/regress/expected/horology.out b/src/test/regress/expected/horology.out
index e4b57f62159..bbd7e282343 100644
--- a/src/test/regress/expected/horology.out
+++ b/src/test/regress/expected/horology.out
@@ -2979,6 +2979,17 @@ SELECT i, to_timestamp('2018-11-02 12:34:56.123456', 'YYYY-MM-DD HH24:MI:SS.FF'
SELECT i, to_timestamp('2018-11-02 12:34:56.123456789', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
ERROR: date/time field value out of range: "2018-11-02 12:34:56.123456789"
+SELECT i, to_timestamp('20181102123456123456', 'YYYYMMDDHH24MISSFF' || i) FROM generate_series(1, 6) i;
+ i | to_timestamp
+---+-------------------------------------
+ 1 | Fri Nov 02 12:34:56.1 2018 PDT
+ 2 | Fri Nov 02 12:34:56.12 2018 PDT
+ 3 | Fri Nov 02 12:34:56.123 2018 PDT
+ 4 | Fri Nov 02 12:34:56.1235 2018 PDT
+ 5 | Fri Nov 02 12:34:56.12346 2018 PDT
+ 6 | Fri Nov 02 12:34:56.123456 2018 PDT
+(6 rows)
+
SELECT to_date('1 4 1902', 'Q MM YYYY'); -- Q is ignored
to_date
------------
diff --git a/src/test/regress/sql/horology.sql b/src/test/regress/sql/horology.sql
index 13f86ce8fc0..d3b9f1628a4 100644
--- a/src/test/regress/sql/horology.sql
+++ b/src/test/regress/sql/horology.sql
@@ -450,6 +450,7 @@ SELECT i, to_timestamp('2018-11-02 12:34:56.1234', 'YYYY-MM-DD HH24:MI:SS.FF' ||
SELECT i, to_timestamp('2018-11-02 12:34:56.12345', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
SELECT i, to_timestamp('2018-11-02 12:34:56.123456', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
SELECT i, to_timestamp('2018-11-02 12:34:56.123456789', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
+SELECT i, to_timestamp('20181102123456123456', 'YYYYMMDDHH24MISSFF' || i) FROM generate_series(1, 6) i;
SELECT to_date('1 4 1902', 'Q MM YYYY'); -- Q is ignored
SELECT to_date('3 4 21 01', 'W MM CC YY');