1
0
mirror of https://github.com/MariaDB/server.git synced 2025-04-18 21:44:20 +03:00

MDEV-36216 TO_CHAR FM format not recognized in SQL_MODE=Oracle

Adding support for the "FM" format in function TO_CHAR(date_time, fmt).

"FM" in the format string disables padding of all components following it.

So now TO_CHAR() works as follows:

- By default string format components DAY (weekday name) and
  MONTH (month name) are right-padded with spaces to the maximum
  possible DAY and MONTH name lengths respectively,
  according to the current locale specified in @@lc_time_names.
  So for example, with lc_time_names='en_US' all month names are
  right-padded with spaces up to 9 characters ('September' is the longest).

    SET lc_time_names='en_US';
    SELECT TO_CHAR('0001-02-03', 'MONTH');   -> 'February '  (padded to 9 chars)

  NEW: When typed after FM, DAY and MONTH names are not right-padded
  with trailing spaces any more:

   SET lc_time_names='en_US';
   SELECT TO_CHAR('0001-02-03', 'FMMONTH'); -> 'February' (not padded)

- By default numeric components YYYY, YYY, YY, Y, DD, H12, H24, MI, SS
  are left-padded with leading digits '0' up to the maximum possible
  number of digits in the component (e.g. 4 for YYYY):

    SELECT TO_CHAR('0001-02-03', 'YYYY'); -> '0001' (padded to 4 chars)

  NEW: When typed after FM, these numeric components are not left-padded
  with leading zeros any more:

    SELECT TO_CHAR('0001-02-03', 'FMYYYY'); -> '1' (not padded)

- If FM is specified multiple times in a format string,
  every FM negates the previous padding state:
  * an odd FM disables padding
  * an even FM enables padding

Implementation details:
- Adding a helper class Date_time_format_oracle.
- Adding a helper method Date_time_format_oracle::append_lex_cstring()
- Moving the function append_val() to Date_time_format_oracle as a method.
- Moving the function make_date_time_oracle() to Date_time_format_oracle
  as a method format().
- Adding helper methods month_name() and day_name() in class MY_LOCALE,
  to return the corresponding components as LEX_CSTRINGs.
This commit is contained in:
Alexander Barkov 2025-04-07 17:17:44 +04:00
parent d3c9a2ee21
commit 2dc9b8b78e
4 changed files with 426 additions and 31 deletions

View File

@ -446,3 +446,239 @@ SELECT TO_CHAR((VALUES('2022-12-12','2020-10-10')));
ERROR HY000: Illegal parameter data type row for operation 'to_char'
SELECT TO_CHAR((STR_TO_DATE('2023-01-01', '%d-%m-%Y'), 'YYYY-MM-DD') );
ERROR HY000: Illegal parameter data type row for operation 'to_char'
#
# MDEV-36216 TO_CHAR FM format not recognized in SQL_MODE=Oracle
#
SET NAMES utf8mb3;
CREATE TABLE t1 (fmt VARCHAR(256));
INSERT INTO t1 VALUES
/* Add the slash character before FM to see the position of FM in the results */
('YYYY-MM-DD HH24:MI:SS DAY MONTH; YYYY-MM-DD HH24:MI:SS DAY MONTH;'),
('/FMYYYY-MM-DD HH24:MI:SS DAY MONTH; YYYY-MM-DD HH24:MI:SS DAY MONTH;'),
('YYYY-/FMMM-DD HH24:MI:SS DAY MONTH; YYYY-MM-DD HH24:MI:SS DAY MONTH;'),
('YYYY-MM-/FMDD HH24:MI:SS DAY MONTH; YYYY-MM-DD HH24:MI:SS DAY MONTH;'),
('YYYY-MM-DD /FMHH24:MI:SS DAY MONTH; YYYY-MM-DD HH24:MI:SS DAY MONTH;'),
('YYYY-MM-DD HH24:/FMMI:SS DAY MONTH; YYYY-MM-DD HH24:MI:SS DAY MONTH;'),
('YYYY-MM-DD HH24:MI:/FMSS DAY MONTH; YYYY-MM-DD HH24:MI:SS DAY MONTH;'),
('YYYY-MM-DD HH24:MI:SS /FMDAY MONTH; YYYY-MM-DD HH24:MI:SS DAY MONTH;'),
('YYYY-MM-DD HH24:MI:SS DAY /FMMONTH; YYYY-MM-DD HH24:MI:SS DAY MONTH;'),
('YYYY-MM-DD HH24:MI:SS DAY MONTH; /FMYYYY-MM-DD HH24:MI:SS DAY MONTH;'),
('YYYY-MM-DD HH24:MI:SS DAY MONTH; YYYY-/FMMM-DD HH24:MI:SS DAY MONTH;'),
('YYYY-MM-DD HH24:MI:SS DAY MONTH; YYYY-MM-/FMDD HH24:MI:SS DAY MONTH;'),
('YYYY-MM-DD HH24:MI:SS DAY MONTH; YYYY-MM-DD /FMHH24:MI:SS DAY MONTH;'),
('YYYY-MM-DD HH24:MI:SS DAY MONTH; YYYY-MM-DD HH24:/FMMI:SS DAY MONTH;'),
('YYYY-MM-DD HH24:MI:SS DAY MONTH; YYYY-MM-DD HH24:MI:/FMSS DAY MONTH;'),
('YYYY-MM-DD HH24:MI:SS DAY MONTH; YYYY-MM-DD HH24:MI:SS /FMDAY MONTH;'),
('YYYY-MM-DD HH24:MI:SS DAY MONTH; YYYY-MM-DD HH24:MI:SS DAY /FMMONTH;'),
('YYYY-MM-DD HH24:MI:SS DAY MONTH; YYYY-MM-DD HH24:MI:SS DAY MONTH/FM;'),
/*Formats not covered above */
('YYY YY Y HH12; /FMYYY YY Y HH12;'),
/*FM specified multiple times*/
('FMFMFMFMFMFMFMFMYYYY-MM-DD [DAY] [MONTH]'),
('FMFMFMFMFMFMFMFMFMYYYY-MM-DD [DAY] [MONTH]'),
(
'YYYY-MM-DD [DAY] [MONTH]; FMYYYY-MM-DD [DAY] [MONTH]; '
'FMYYYY-MM-DD [DAY] [MONTH]; FMYYYY-MM-DD [DAY] [MONTH]; '
'FMYYYY-MM-DD [DAY] [MONTH];'
),
/*Corner cases*/
('FX') /*Unknown format starting with 'F'*/,
('F') /*Unexpected end of the format string*/;
SET lc_time_names='en_US';
SELECT TO_CHAR('0001-02-03 04:05:06', fmt) AS c1 FROM t1;
c1
0001-02-03 04:05:06 Saturday February ; 0001-02-03 04:05:06 Saturday February ;
/1-2-3 4:5:6 Saturday February; 1-2-3 4:5:6 Saturday February;
0001-/2-3 4:5:6 Saturday February; 1-2-3 4:5:6 Saturday February;
0001-02-/3 4:5:6 Saturday February; 1-2-3 4:5:6 Saturday February;
0001-02-03 /4:5:6 Saturday February; 1-2-3 4:5:6 Saturday February;
0001-02-03 04:/5:6 Saturday February; 1-2-3 4:5:6 Saturday February;
0001-02-03 04:05:/6 Saturday February; 1-2-3 4:5:6 Saturday February;
0001-02-03 04:05:06 /Saturday February; 1-2-3 4:5:6 Saturday February;
0001-02-03 04:05:06 Saturday /February; 1-2-3 4:5:6 Saturday February;
0001-02-03 04:05:06 Saturday February ; /1-2-3 4:5:6 Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-/2-3 4:5:6 Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-02-/3 4:5:6 Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-02-03 /4:5:6 Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-02-03 04:/5:6 Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-02-03 04:05:/6 Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-02-03 04:05:06 /Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-02-03 04:05:06 Saturday /February;
0001-02-03 04:05:06 Saturday February ; 0001-02-03 04:05:06 Saturday February /;
001 01 1 04; /1 1 1 4;
0001-02-03 [Saturday ] [February ]
1-2-3 [Saturday] [February]
0001-02-03 [Saturday ] [February ]; 1-2-3 [Saturday] [February]; 0001-02-03 [Saturday ] [February ]; 1-2-3 [Saturday] [February]; 0001-02-03 [Saturday ] [February ];
NULL
NULL
Warnings:
Warning 3047 Invalid argument error: date format not recognized at FX in function to_char.
Warning 3047 Invalid argument error: date format not recognized at F in function to_char.
SET sql_mode=ORACLE;
CREATE VIEW v1 AS SELECT TO_CHAR('0001-02-03 04:05:06', fmt) AS c1 FROM t1;
SHOW CREATE VIEW v1;
View Create View character_set_client collation_connection
v1 CREATE VIEW "v1" AS select to_char('0001-02-03 04:05:06',"t1"."fmt") AS "c1" from "t1" utf8mb3 utf8mb3_general_ci
SELECT * FROM v1;
c1
0001-02-03 04:05:06 Saturday February ; 0001-02-03 04:05:06 Saturday February ;
/1-2-3 4:5:6 Saturday February; 1-2-3 4:5:6 Saturday February;
0001-/2-3 4:5:6 Saturday February; 1-2-3 4:5:6 Saturday February;
0001-02-/3 4:5:6 Saturday February; 1-2-3 4:5:6 Saturday February;
0001-02-03 /4:5:6 Saturday February; 1-2-3 4:5:6 Saturday February;
0001-02-03 04:/5:6 Saturday February; 1-2-3 4:5:6 Saturday February;
0001-02-03 04:05:/6 Saturday February; 1-2-3 4:5:6 Saturday February;
0001-02-03 04:05:06 /Saturday February; 1-2-3 4:5:6 Saturday February;
0001-02-03 04:05:06 Saturday /February; 1-2-3 4:5:6 Saturday February;
0001-02-03 04:05:06 Saturday February ; /1-2-3 4:5:6 Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-/2-3 4:5:6 Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-02-/3 4:5:6 Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-02-03 /4:5:6 Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-02-03 04:/5:6 Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-02-03 04:05:/6 Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-02-03 04:05:06 /Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-02-03 04:05:06 Saturday /February;
0001-02-03 04:05:06 Saturday February ; 0001-02-03 04:05:06 Saturday February /;
001 01 1 04; /1 1 1 4;
0001-02-03 [Saturday ] [February ]
1-2-3 [Saturday] [February]
0001-02-03 [Saturday ] [February ]; 1-2-3 [Saturday] [February]; 0001-02-03 [Saturday ] [February ]; 1-2-3 [Saturday] [February]; 0001-02-03 [Saturday ] [February ];
NULL
NULL
Warnings:
Warning 3047 Invalid argument error: date format not recognized at FX in function to_char.
Warning 3047 Invalid argument error: date format not recognized at F in function to_char.
SET sql_mode=DEFAULT;
SHOW CREATE VIEW v1;
View Create View character_set_client collation_connection
v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select to_char('0001-02-03 04:05:06',`t1`.`fmt`) AS `c1` from `t1` utf8mb3 utf8mb3_general_ci
SELECT * FROM v1;
c1
0001-02-03 04:05:06 Saturday February ; 0001-02-03 04:05:06 Saturday February ;
/1-2-3 4:5:6 Saturday February; 1-2-3 4:5:6 Saturday February;
0001-/2-3 4:5:6 Saturday February; 1-2-3 4:5:6 Saturday February;
0001-02-/3 4:5:6 Saturday February; 1-2-3 4:5:6 Saturday February;
0001-02-03 /4:5:6 Saturday February; 1-2-3 4:5:6 Saturday February;
0001-02-03 04:/5:6 Saturday February; 1-2-3 4:5:6 Saturday February;
0001-02-03 04:05:/6 Saturday February; 1-2-3 4:5:6 Saturday February;
0001-02-03 04:05:06 /Saturday February; 1-2-3 4:5:6 Saturday February;
0001-02-03 04:05:06 Saturday /February; 1-2-3 4:5:6 Saturday February;
0001-02-03 04:05:06 Saturday February ; /1-2-3 4:5:6 Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-/2-3 4:5:6 Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-02-/3 4:5:6 Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-02-03 /4:5:6 Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-02-03 04:/5:6 Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-02-03 04:05:/6 Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-02-03 04:05:06 /Saturday February;
0001-02-03 04:05:06 Saturday February ; 0001-02-03 04:05:06 Saturday /February;
0001-02-03 04:05:06 Saturday February ; 0001-02-03 04:05:06 Saturday February /;
001 01 1 04; /1 1 1 4;
0001-02-03 [Saturday ] [February ]
1-2-3 [Saturday] [February]
0001-02-03 [Saturday ] [February ]; 1-2-3 [Saturday] [February]; 0001-02-03 [Saturday ] [February ]; 1-2-3 [Saturday] [February]; 0001-02-03 [Saturday ] [February ];
NULL
NULL
Warnings:
Warning 3047 Invalid argument error: date format not recognized at FX in function to_char.
Warning 3047 Invalid argument error: date format not recognized at F in function to_char.
DROP VIEW v1;
SET lc_time_names='zh_CN';
SELECT TO_CHAR('0001-02-03 04:05:06', fmt) AS c1 FROM t1;
c1
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 04:05:06 星期六 二月 ;
/1-2-3 4:5:6 星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-/2-3 4:5:6 星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-02-/3 4:5:6 星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-02-03 /4:5:6 星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-02-03 04:/5:6 星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-02-03 04:05:/6 星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-02-03 04:05:06 /星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-02-03 04:05:06 星期六 /二月; 1-2-3 4:5:6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; /1-2-3 4:5:6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-/2-3 4:5:6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-/3 4:5:6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 /4:5:6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 04:/5:6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 04:05:/6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 04:05:06 /星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 04:05:06 星期六 /二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 04:05:06 星期六 二月 /;
001 01 1 04; /1 1 1 4;
0001-02-03 [星期六] [二月 ]
1-2-3 [星期六] [二月]
0001-02-03 [星期六] [二月 ]; 1-2-3 [星期六] [二月]; 0001-02-03 [星期六] [二月 ]; 1-2-3 [星期六] [二月]; 0001-02-03 [星期六] [二月 ];
NULL
NULL
Warnings:
Warning 3047 Invalid argument error: date format not recognized at FX in function to_char.
Warning 3047 Invalid argument error: date format not recognized at F in function to_char.
SET sql_mode=ORACLE;
CREATE VIEW v1 AS SELECT TO_CHAR('0001-02-03 04:05:06', fmt) AS c1 FROM t1;
SHOW CREATE VIEW v1;
View Create View character_set_client collation_connection
v1 CREATE VIEW "v1" AS select to_char('0001-02-03 04:05:06',"t1"."fmt") AS "c1" from "t1" utf8mb3 utf8mb3_general_ci
SELECT * FROM v1;
c1
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 04:05:06 星期六 二月 ;
/1-2-3 4:5:6 星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-/2-3 4:5:6 星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-02-/3 4:5:6 星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-02-03 /4:5:6 星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-02-03 04:/5:6 星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-02-03 04:05:/6 星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-02-03 04:05:06 /星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-02-03 04:05:06 星期六 /二月; 1-2-3 4:5:6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; /1-2-3 4:5:6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-/2-3 4:5:6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-/3 4:5:6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 /4:5:6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 04:/5:6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 04:05:/6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 04:05:06 /星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 04:05:06 星期六 /二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 04:05:06 星期六 二月 /;
001 01 1 04; /1 1 1 4;
0001-02-03 [星期六] [二月 ]
1-2-3 [星期六] [二月]
0001-02-03 [星期六] [二月 ]; 1-2-3 [星期六] [二月]; 0001-02-03 [星期六] [二月 ]; 1-2-3 [星期六] [二月]; 0001-02-03 [星期六] [二月 ];
NULL
NULL
Warnings:
Warning 3047 Invalid argument error: date format not recognized at FX in function to_char.
Warning 3047 Invalid argument error: date format not recognized at F in function to_char.
SET sql_mode=DEFAULT;
SHOW CREATE VIEW v1;
View Create View character_set_client collation_connection
v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select to_char('0001-02-03 04:05:06',`t1`.`fmt`) AS `c1` from `t1` utf8mb3 utf8mb3_general_ci
SELECT * FROM v1;
c1
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 04:05:06 星期六 二月 ;
/1-2-3 4:5:6 星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-/2-3 4:5:6 星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-02-/3 4:5:6 星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-02-03 /4:5:6 星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-02-03 04:/5:6 星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-02-03 04:05:/6 星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-02-03 04:05:06 /星期六 二月; 1-2-3 4:5:6 星期六 二月;
0001-02-03 04:05:06 星期六 /二月; 1-2-3 4:5:6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; /1-2-3 4:5:6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-/2-3 4:5:6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-/3 4:5:6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 /4:5:6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 04:/5:6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 04:05:/6 星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 04:05:06 /星期六 二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 04:05:06 星期六 /二月;
0001-02-03 04:05:06 星期六 二月 ; 0001-02-03 04:05:06 星期六 二月 /;
001 01 1 04; /1 1 1 4;
0001-02-03 [星期六] [二月 ]
1-2-3 [星期六] [二月]
0001-02-03 [星期六] [二月 ]; 1-2-3 [星期六] [二月]; 0001-02-03 [星期六] [二月 ]; 1-2-3 [星期六] [二月]; 0001-02-03 [星期六] [二月 ];
NULL
NULL
Warnings:
Warning 3047 Invalid argument error: date format not recognized at FX in function to_char.
Warning 3047 Invalid argument error: date format not recognized at F in function to_char.
DROP VIEW v1;
SET lc_time_names=DEFAULT;
DROP TABLE t1;

View File

@ -234,3 +234,74 @@ SELECT TO_CHAR((VALUES('2022-12-12','2020-10-10')));
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
SELECT TO_CHAR((STR_TO_DATE('2023-01-01', '%d-%m-%Y'), 'YYYY-MM-DD') );
--echo #
--echo # MDEV-36216 TO_CHAR FM format not recognized in SQL_MODE=Oracle
--echo #
SET NAMES utf8mb3;
CREATE TABLE t1 (fmt VARCHAR(256));
INSERT INTO t1 VALUES
/* Add the slash character before FM to see the position of FM in the results */
('YYYY-MM-DD HH24:MI:SS DAY MONTH; YYYY-MM-DD HH24:MI:SS DAY MONTH;'),
('/FMYYYY-MM-DD HH24:MI:SS DAY MONTH; YYYY-MM-DD HH24:MI:SS DAY MONTH;'),
('YYYY-/FMMM-DD HH24:MI:SS DAY MONTH; YYYY-MM-DD HH24:MI:SS DAY MONTH;'),
('YYYY-MM-/FMDD HH24:MI:SS DAY MONTH; YYYY-MM-DD HH24:MI:SS DAY MONTH;'),
('YYYY-MM-DD /FMHH24:MI:SS DAY MONTH; YYYY-MM-DD HH24:MI:SS DAY MONTH;'),
('YYYY-MM-DD HH24:/FMMI:SS DAY MONTH; YYYY-MM-DD HH24:MI:SS DAY MONTH;'),
('YYYY-MM-DD HH24:MI:/FMSS DAY MONTH; YYYY-MM-DD HH24:MI:SS DAY MONTH;'),
('YYYY-MM-DD HH24:MI:SS /FMDAY MONTH; YYYY-MM-DD HH24:MI:SS DAY MONTH;'),
('YYYY-MM-DD HH24:MI:SS DAY /FMMONTH; YYYY-MM-DD HH24:MI:SS DAY MONTH;'),
('YYYY-MM-DD HH24:MI:SS DAY MONTH; /FMYYYY-MM-DD HH24:MI:SS DAY MONTH;'),
('YYYY-MM-DD HH24:MI:SS DAY MONTH; YYYY-/FMMM-DD HH24:MI:SS DAY MONTH;'),
('YYYY-MM-DD HH24:MI:SS DAY MONTH; YYYY-MM-/FMDD HH24:MI:SS DAY MONTH;'),
('YYYY-MM-DD HH24:MI:SS DAY MONTH; YYYY-MM-DD /FMHH24:MI:SS DAY MONTH;'),
('YYYY-MM-DD HH24:MI:SS DAY MONTH; YYYY-MM-DD HH24:/FMMI:SS DAY MONTH;'),
('YYYY-MM-DD HH24:MI:SS DAY MONTH; YYYY-MM-DD HH24:MI:/FMSS DAY MONTH;'),
('YYYY-MM-DD HH24:MI:SS DAY MONTH; YYYY-MM-DD HH24:MI:SS /FMDAY MONTH;'),
('YYYY-MM-DD HH24:MI:SS DAY MONTH; YYYY-MM-DD HH24:MI:SS DAY /FMMONTH;'),
('YYYY-MM-DD HH24:MI:SS DAY MONTH; YYYY-MM-DD HH24:MI:SS DAY MONTH/FM;'),
/*Formats not covered above */
('YYY YY Y HH12; /FMYYY YY Y HH12;'),
/*FM specified multiple times*/
('FMFMFMFMFMFMFMFMYYYY-MM-DD [DAY] [MONTH]'),
('FMFMFMFMFMFMFMFMFMYYYY-MM-DD [DAY] [MONTH]'),
(
'YYYY-MM-DD [DAY] [MONTH]; FMYYYY-MM-DD [DAY] [MONTH]; '
'FMYYYY-MM-DD [DAY] [MONTH]; FMYYYY-MM-DD [DAY] [MONTH]; '
'FMYYYY-MM-DD [DAY] [MONTH];'
),
/*Corner cases*/
('FX') /*Unknown format starting with 'F'*/,
('F') /*Unexpected end of the format string*/;
SET lc_time_names='en_US';
SELECT TO_CHAR('0001-02-03 04:05:06', fmt) AS c1 FROM t1;
SET sql_mode=ORACLE;
CREATE VIEW v1 AS SELECT TO_CHAR('0001-02-03 04:05:06', fmt) AS c1 FROM t1;
SHOW CREATE VIEW v1;
SELECT * FROM v1;
SET sql_mode=DEFAULT;
SHOW CREATE VIEW v1;
SELECT * FROM v1;
DROP VIEW v1;
SET lc_time_names='zh_CN';
SELECT TO_CHAR('0001-02-03 04:05:06', fmt) AS c1 FROM t1;
SET sql_mode=ORACLE;
CREATE VIEW v1 AS SELECT TO_CHAR('0001-02-03 04:05:06', fmt) AS c1 FROM t1;
SHOW CREATE VIEW v1;
SELECT * FROM v1;
SET sql_mode=DEFAULT;
SHOW CREATE VIEW v1;
SELECT * FROM v1;
DROP VIEW v1;
SET lc_time_names=DEFAULT;
DROP TABLE t1;

View File

@ -1962,6 +1962,79 @@ null_date:
return 0;
}
/* A class to print TO_CHAR(date_time, format) */
class Date_time_format_oracle
{
// m_fm is true if "FM" was found in the format string odd number of times
bool m_fm;
public:
Date_time_format_oracle()
:m_fm(false)
{ }
/*
Append a numeric value to a String.
If m_fm is false, then left-pad the numeric value with '0'.
@param val - the numeric value to be appended to str
@param size - the maximum number of digits possible in val
@param [OUT] str - the result String (val will be appended to it)
@retval false - on success
@retval true - on error (e.g. EOM)
*/
bool append_val(int val, uint size, String *str) const
{
if (m_fm)
return str->append_longlong(val);
return str->append_zerofill(val, size);
}
/*
Append a LEX_CSTRING value to a String.
If m_fm is false, then right-pad the appended value with spaces.
@param ls - the LEX_CSTRING to be append to str
@param max_char_length - the maximum possible length of ls, in characters
@param [OUT] str - the result String (ls will be appended to it)
@retval false - on success
@retval true - on error (e.g. EOM)
*/
bool append_lex_cstring(const LEX_CSTRING ls, uint max_char_length,
String *str) const
{
// Locale data uses utf8mb3
static constexpr CHARSET_INFO *cs= &my_charset_utf8mb3_general_ci;
str->append(ls.str, ls.length, cs);
if (!m_fm)
{
size_t char_length= cs->numchars(ls.str, ls.str + ls.length);
if (char_length < max_char_length)
return str->fill(str->length() + max_char_length - char_length, ' ');
}
return false;
}
/*
Print a date/time value to a String according to the given format
@param fmt_array - the format array
@param l_time - the date/time value
@param locale - the locale to use for textual components
(MONTH and DAY)
@param [OUT] str - the string to print into.
@retval false - on success
@retval true - on error (e.g. EOM)
*/
bool format(const uint16 *fmt_array,
const MYSQL_TIME *l_time,
const MY_LOCALE *locale,
String *str);
};
/*
Oracle has many formatting models, we list all but only part of them
are implemented, because some models depend on oracle functions
@ -2138,7 +2211,8 @@ bool Item_func_tochar::parse_format_string(const String *format, uint *fmt_len)
/*
Oracle datetime format support text in double quotation marks like
'YYYY"abc"MM"xyz"DD', When this happens, store the text and quotation
marks, and use the text as a separator in make_date_time_oracle.
marks, and use the text as a separator in
Date_time_format_oracle::format().
NOTE: the quotation mark is not print in return value. for example:
select TO_CHAR(sysdate, 'YYYY"abc"MM"xyzDD"') will return 2021abc01xyz11
@ -2419,6 +2493,17 @@ bool Item_func_tochar::parse_format_string(const String *format, uint *fmt_len)
tmp_fmt--;
break;
case 'F':
if (ptr + 1 == end)
goto error;
if (my_toupper(system_charset_info, ptr[1]) == 'M')
{
*tmp_fmt= FMT_FM;
ptr+= 1;
continue;
}
goto error;
default:
offset= parse_special(cfmt, ptr, end, tmp_fmt);
if (!offset)
@ -2441,16 +2526,10 @@ error:
}
static inline bool append_val(int val, int size, String *str)
{
return str->append_zerofill(val, size);
}
static bool make_date_time_oracle(const uint16 *fmt_array,
const MYSQL_TIME *l_time,
const MY_LOCALE *locale,
String *str)
bool Date_time_format_oracle::format(const uint16 *fmt_array,
const MYSQL_TIME *l_time,
const MY_LOCALE *locale,
String *str)
{
bool quotation_flag= false;
const uint16 *ptr= fmt_array;
@ -2558,16 +2637,8 @@ static bool make_date_time_oracle(const uint16 *fmt_array,
}
else
{
const char *month_name= (locale->month_names->
type_names[l_time->month-1]);
size_t month_byte_len= strlen(month_name);
size_t month_char_len;
str->append(month_name, month_byte_len, system_charset_info);
month_char_len= my_numchars_mb(&my_charset_utf8mb3_general_ci,
month_name, month_name +
month_byte_len);
if (str->fill(str->length() + locale->max_month_name_length -
month_char_len, ' '))
if (append_lex_cstring(locale->month_name(l_time->month - 1),
locale->max_month_name_length, str))
goto err_exit;
}
}
@ -2598,17 +2669,10 @@ static bool make_date_time_oracle(const uint16 *fmt_array,
str->append("00", 2, system_charset_info);
else
{
const char *day_name;
size_t day_byte_len, day_char_len;
weekday=calc_weekday(calc_daynr(l_time->year,l_time->month,
l_time->day), 0);
day_name= locale->day_names->type_names[weekday];
day_byte_len= strlen(day_name);
str->append(day_name, day_byte_len, system_charset_info);
day_char_len= my_numchars_mb(&my_charset_utf8mb3_general_ci,
day_name, day_name + day_byte_len);
if (str->fill(str->length() + locale->max_day_name_length -
day_char_len, ' '))
if (append_lex_cstring(locale->day_name(weekday),
locale->max_day_name_length, str))
goto err_exit;
}
}
@ -2636,6 +2700,10 @@ static bool make_date_time_oracle(const uint16 *fmt_array,
goto err_exit;
break;
case FMT_FM:
m_fm= !m_fm;
break;
default:
str->append((char) *ptr);
}
@ -2743,7 +2811,7 @@ String *Item_func_tochar::val_str(String* str)
/* Create the result string */
str->set_charset(collation.collation);
if (!make_date_time_oracle(fmt_array, &l_time, lc, str))
if (!Date_time_format_oracle().format(fmt_array, &l_time, lc, str))
return str;
null_date:

View File

@ -62,6 +62,26 @@ public:
{}
my_repertoire_t repertoire() const
{ return is_ascii ? MY_REPERTOIRE_ASCII : MY_REPERTOIRE_EXTENDED; }
/*
Get a non-abbreviated month name by index
@param month - the month index 0..11
*/
LEX_CSTRING month_name(uint month) const
{
if (month > 11)
return Lex_cstring("##", 2);
return Lex_cstring_strlen(month_names->type_names[month]);
}
/*
Get a non-abbreviated weekday name by index
@param weekday - the weekday index 0..6
*/
LEX_CSTRING day_name(uint weekday) const
{
if (weekday > 6)
return Lex_cstring("##", 2);
return Lex_cstring_strlen(day_names->type_names[weekday]);
}
};
/* Exported variables */