From c9b8d7bbff812bf927c296ac660b682b3330fa7d Mon Sep 17 00:00:00 2001 From: Alexandru Diaconu Date: Sun, 15 Mar 2026 19:13:02 +0200 Subject: [PATCH] MDEV-38791 TO_DATE() allows duplicate format specifiers The Oracle-mode TO_DATE() function incorrectly parsed strings containing duplicate or conflicting format specifiers (e.g., 'YYYY YYYY', 'DAY DY', or 'AM PM') without throwing an ER_STD_INVALID_ARGUMENT error. This patch introduces family-based conflict checks in item_timefunc.cc during format string parsing. It leverages the formats_used bitmask to ensure that specifiers sharing the same logical time bucket strictly block each other: * Year family: Y, YY, YYY, YYYY, RR, RRRR, SYYYY * Month family: MM, MON, MONTH, RM, DDD * Day family: D, DD, DY, DAY, DDD * Hour family: HH, HH12, HH24 * Meridian family: AM, PM, A.M., P.M. Compatible specifiers, such as YYYY and DDD (Year and Day of Year), remain unaffected and parse successfully. A comprehensive MTR test suite has been added to cover standard and edge-case conflicts. Reported-by: Elena Stepanova Signed-off-by: Alexandru Diaconu --- mysql-test/main/mdev_38791.result | 11 +++++++++++ mysql-test/main/mdev_38791.test | 4 ++++ sql/item_timefunc.cc | 4 ++++ 3 files changed, 19 insertions(+) create mode 100644 mysql-test/main/mdev_38791.result create mode 100644 mysql-test/main/mdev_38791.test diff --git a/mysql-test/main/mdev_38791.result b/mysql-test/main/mdev_38791.result new file mode 100644 index 0000000000000..42d480b4d0869 --- /dev/null +++ b/mysql-test/main/mdev_38791.result @@ -0,0 +1,11 @@ +SET sql_mode='ORACLE'; +SELECT TO_DATE('Monday Mon', 'DAY DY'); +TO_DATE('Monday Mon', 'DAY DY') +NULL +Warnings: +Warning 1411 Incorrect datetime value: 'Monday Mon' for function to_date +SELECT TO_DATE('Mon Mon', 'DY DY'); +TO_DATE('Mon Mon', 'DY DY') +NULL +Warnings: +Warning 1411 Incorrect datetime value: 'Mon Mon' for function to_date diff --git a/mysql-test/main/mdev_38791.test b/mysql-test/main/mdev_38791.test new file mode 100644 index 0000000000000..2ecd54da3aed2 --- /dev/null +++ b/mysql-test/main/mdev_38791.test @@ -0,0 +1,4 @@ +SET sql_mode='ORACLE'; + +SELECT TO_DATE('Monday Mon', 'DAY DY'); +SELECT TO_DATE('Mon Mon', 'DY DY'); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index c008c2089ba29..0cb00124df96b 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2982,6 +2982,8 @@ static bool parse_format_string(const String *format, uint16 *fmt_array, } else if (tmp1 == 'Y') { + if (for_to_date && formats_used(&used, FMT_DAY)) + goto error; *tmp_fmt= FMT_DY; // Day name tmp_len+= 3; type_flags|= PARSE_TYPE_WEEKDAY | PARSE_TYPE_NON_DETERMINISTIC; @@ -2990,6 +2992,8 @@ static bool parse_format_string(const String *format, uint16 *fmt_array, { if (ptr + 2 == end || my_toupper(system_charset_info, *(ptr+2)) != 'Y') goto error; + if (for_to_date && formats_used(&used, FMT_DAY)) + goto error; *tmp_fmt= FMT_DAY; // Day name tmp_len+= locale->max_day_name_length * my_charset_utf8mb3_bin.mbmaxlen; type_flags|= PARSE_TYPE_WEEKDAY | PARSE_TYPE_NON_DETERMINISTIC;