diff --git a/mysql-test/suite/compat/oracle/r/func_to_char.result b/mysql-test/suite/compat/oracle/r/func_to_char.result
new file mode 100644
index 00000000000..a5c3d59d40b
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/r/func_to_char.result
@@ -0,0 +1,479 @@
+#
+# test for datetime
+#
+CREATE TABLE t_to_char1(c0 int, c1 date, c2 time, c3 datetime);
+INSERT INTO t_to_char1 VALUES (1, '1000-1-1', '00:00:00', '1000-1-1 00:00:00');
+INSERT INTO t_to_char1 VALUES (2, '9999-12-31', '23:59:59', '9999-12-31 23:59:59');
+INSERT INTO t_to_char1 VALUES (3, '2021-01-03', '08:30:00', '2021-01-03 08:30:00');
+INSERT INTO t_to_char1 VALUES (4, '2021-07-03', '18:30:00', '2021-07-03 18:30:00');
+CREATE TABLE t_to_char2(c1 timestamp);
+INSERT INTO t_to_char2 VALUES ('1980-01-11 04:50:39');
+INSERT INTO t_to_char2 VALUES ('2000-11-11 12:50:00');
+INSERT INTO t_to_char2 VALUES ('2030-11-11 18:20:10');
+SELECT TO_CHAR(c1, 'YYYY-MM-DD') FROM t_to_char2;
+TO_CHAR(c1, 'YYYY-MM-DD')
+1980-01-11
+2000-11-11
+2030-11-11
+SELECT TO_CHAR(c1, 'HH24-MI-SS') FROM t_to_char2;
+TO_CHAR(c1, 'HH24-MI-SS')
+04-50-39
+12-50-00
+18-20-10
+#
+# test YYYY/YY/MM/DD/HH/HH24/MI/SS
+#
+SELECT TO_CHAR(c1, 'YYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'YY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+C1	C2	C3
+1000-01-01	12:00:00	00-01-01 00:00:00
+9999-12-31	11:59:59	99-12-31 23:59:59
+2021-01-03	08:30:00	21-01-03 08:30:00
+2021-07-03	06:30:00	21-07-03 18:30:00
+SELECT TO_CHAR(c1, 'yyyy-mm-dd') AS C1, TO_CHAR(c2, 'hh:mi:ss') AS C2, TO_CHAR(c3, 'yy-mm-dd hh24:mi:ss') AS C3 FROM t_to_char1;
+C1	C2	C3
+1000-01-01	12:00:00	00-01-01 00:00:00
+9999-12-31	11:59:59	99-12-31 23:59:59
+2021-01-03	08:30:00	21-01-03 08:30:00
+2021-07-03	06:30:00	21-07-03 18:30:00
+#
+# test YYY/Y/MON/DD/DY/HH/HH12/MI/SS
+#
+SELECT TO_CHAR(c1, 'YYY-MON-DD') AS C1, TO_CHAR(c2, 'HH12:MI:SS') AS C2, TO_CHAR(c3, 'Y-MONTH-DY HH:MI:SS') AS C3 FROM t_to_char1;
+C1	C2	C3
+000-Jan-01	12:00:00	0-January  -Wed 12:00:00
+999-Dec-31	11:59:59	9-December -Fri 11:59:59
+021-Jan-03	08:30:00	1-January  -Sun 08:30:00
+021-Jul-03	06:30:00	1-July     -Sat 06:30:00
+SELECT TO_CHAR(c1, 'yyy-Mon-Dd') AS C1, TO_CHAR(c2, 'Hh12:mi:Ss') AS C2, TO_CHAR(c3, 'y-Month-Dy Hh:Mi:Ss') AS C3 FROM t_to_char1;
+C1	C2	C3
+000-Jan-01	12:00:00	0-January  -Wed 12:00:00
+999-Dec-31	11:59:59	9-December -Fri 11:59:59
+021-Jan-03	08:30:00	1-January  -Sun 08:30:00
+021-Jul-03	06:30:00	1-July     -Sat 06:30:00
+#
+# test RRRR/RR/DAY
+#
+SELECT TO_CHAR(c1, 'RRRR-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'RRRR-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+C1	C2	C3
+1000-01-01	12:00:00	1000-01-01 00:00:00
+9999-12-31	11:59:59	9999-12-31 23:59:59
+2021-01-03	08:30:00	2021-01-03 08:30:00
+2021-07-03	06:30:00	2021-07-03 18:30:00
+SELECT TO_CHAR(c1, 'RR-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'YY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+C1	C2	C3
+00-01-01	12:00:00	00-01-01 00:00:00
+99-12-31	11:59:59	99-12-31 23:59:59
+21-01-03	08:30:00	21-01-03 08:30:00
+21-07-03	06:30:00	21-07-03 18:30:00
+SELECT TO_CHAR(c1, 'Rrrr-Mm-Dd') AS C1, TO_CHAR(c2, 'hh:mi:ss') AS C2, TO_CHAR(c3, 'Rrrr-mm-dd Hh24:mi:ss') AS C3 FROM t_to_char1;
+C1	C2	C3
+1000-01-01	12:00:00	1000-01-01 00:00:00
+9999-12-31	11:59:59	9999-12-31 23:59:59
+2021-01-03	08:30:00	2021-01-03 08:30:00
+2021-07-03	06:30:00	2021-07-03 18:30:00
+SELECT TO_CHAR(c1, 'rr-mm-dd') AS C1, TO_CHAR(c2, 'hh:mi:ss') AS C2, TO_CHAR(c3, 'yy-mm-dd hh24:Mi:ss') AS C3 FROM t_to_char1;
+C1	C2	C3
+00-01-01	12:00:00	00-01-01 00:00:00
+99-12-31	11:59:59	99-12-31 23:59:59
+21-01-03	08:30:00	21-01-03 08:30:00
+21-07-03	06:30:00	21-07-03 18:30:00
+#
+# test AD/A.D./BC/B.C./AM/A.M./PM/P.M.
+#
+SELECT TO_CHAR(c1, 'ADYYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'AD.YYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+C1	C2	C3
+AD1000-01-01	12:00:00	AD.1000-01-01 00:00:00
+AD9999-12-31	11:59:59	AD.9999-12-31 23:59:59
+AD2021-01-03	08:30:00	AD.2021-01-03 08:30:00
+AD2021-07-03	06:30:00	AD.2021-07-03 18:30:00
+SELECT TO_CHAR(c1, 'A.D.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+C1	C2	C3
+A.D.1000-01-01	12:00:00	A.D..1000-01-01 00:00:00
+A.D.9999-12-31	11:59:59	A.D..9999-12-31 23:59:59
+A.D.2021-01-03	08:30:00	A.D..2021-01-03 08:30:00
+A.D.2021-07-03	06:30:00	A.D..2021-07-03 18:30:00
+SELECT TO_CHAR(c1, 'ADYYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'AD.YYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+C1	C2	C3
+AD1000-01-01	12:00:00	AD.1000-01-01 00:00:00
+AD9999-12-31	11:59:59	AD.9999-12-31 23:59:59
+AD2021-01-03	08:30:00	AD.2021-01-03 08:30:00
+AD2021-07-03	06:30:00	AD.2021-07-03 18:30:00
+SELECT TO_CHAR(c1, 'A.D.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+C1	C2	C3
+A.D.1000-01-01	12:00:00	A.D..1000-01-01 00:00:00
+A.D.9999-12-31	11:59:59	A.D..9999-12-31 23:59:59
+A.D.2021-01-03	08:30:00	A.D..2021-01-03 08:30:00
+A.D.2021-07-03	06:30:00	A.D..2021-07-03 18:30:00
+SELECT TO_CHAR(c1, 'BCYYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'BCYYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+C1	C2	C3
+AD1000-01-01	12:00:00	AD1000-01-01 00:00:00
+AD9999-12-31	11:59:59	AD9999-12-31 23:59:59
+AD2021-01-03	08:30:00	AD2021-01-03 08:30:00
+AD2021-07-03	06:30:00	AD2021-07-03 18:30:00
+SELECT TO_CHAR(c1, 'B.C.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'B.C.YYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+C1	C2	C3
+A.D.1000-01-01	12:00:00	A.D.1000-01-01 00:00:00
+A.D.9999-12-31	11:59:59	A.D.9999-12-31 23:59:59
+A.D.2021-01-03	08:30:00	A.D.2021-01-03 08:30:00
+A.D.2021-07-03	06:30:00	A.D.2021-07-03 18:30:00
+SELECT TO_CHAR(c1, 'bcyyyy-mm-dd') AS C1, TO_CHAR(c2, 'hh:mi:ss') AS C2, TO_CHAR(c3, 'BcYYyy-MM-DD Hh24:mi:sS') AS C3 FROM t_to_char1;
+C1	C2	C3
+AD1000-01-01	12:00:00	AD1000-01-01 00:00:00
+AD9999-12-31	11:59:59	AD9999-12-31 23:59:59
+AD2021-01-03	08:30:00	AD2021-01-03 08:30:00
+AD2021-07-03	06:30:00	AD2021-07-03 18:30:00
+SELECT TO_CHAR(c1, 'b.c.yyyy-mm-dd') AS C1, TO_CHAR(c2, 'hh:mI:Ss') AS C2, TO_CHAR(c3, 'b.C.Yyyy-Mm-dd hH24:MI:SS') AS C3 FROM t_to_char1;
+C1	C2	C3
+A.D.1000-01-01	12:00:00	A.D.1000-01-01 00:00:00
+A.D.9999-12-31	11:59:59	A.D.9999-12-31 23:59:59
+A.D.2021-01-03	08:30:00	A.D.2021-01-03 08:30:00
+A.D.2021-07-03	06:30:00	A.D.2021-07-03 18:30:00
+SELECT TO_CHAR(c1, 'A.D.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'PMHH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD P.M.HH24:MI:SS') AS C3 FROM t_to_char1;
+C1	C2	C3
+A.D.1000-01-01	AM12:00:00	A.D..1000-01-01 A.M.00:00:00
+A.D.9999-12-31	PM11:59:59	A.D..9999-12-31 P.M.23:59:59
+A.D.2021-01-03	AM08:30:00	A.D..2021-01-03 A.M.08:30:00
+A.D.2021-07-03	PM06:30:00	A.D..2021-07-03 P.M.18:30:00
+SELECT TO_CHAR(c1, 'A.D.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'pmHH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD p.m.HH24:MI:SS') AS C3 FROM t_to_char1;
+C1	C2	C3
+A.D.1000-01-01	AM12:00:00	A.D..1000-01-01 A.M.00:00:00
+A.D.9999-12-31	PM11:59:59	A.D..9999-12-31 P.M.23:59:59
+A.D.2021-01-03	AM08:30:00	A.D..2021-01-03 A.M.08:30:00
+A.D.2021-07-03	PM06:30:00	A.D..2021-07-03 P.M.18:30:00
+SELECT TO_CHAR(c1, 'A.D.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'AMHH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD A.m.HH24:MI:SS') AS C3 FROM t_to_char1;
+C1	C2	C3
+A.D.1000-01-01	AM12:00:00	A.D..1000-01-01 A.M.00:00:00
+A.D.9999-12-31	PM11:59:59	A.D..9999-12-31 P.M.23:59:59
+A.D.2021-01-03	AM08:30:00	A.D..2021-01-03 A.M.08:30:00
+A.D.2021-07-03	PM06:30:00	A.D..2021-07-03 P.M.18:30:00
+SELECT TO_CHAR(c1, 'A.D.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'amHH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD a.M.HH24:MI:SS') AS C3 FROM t_to_char1;
+C1	C2	C3
+A.D.1000-01-01	AM12:00:00	A.D..1000-01-01 A.M.00:00:00
+A.D.9999-12-31	PM11:59:59	A.D..9999-12-31 P.M.23:59:59
+A.D.2021-01-03	AM08:30:00	A.D..2021-01-03 A.M.08:30:00
+A.D.2021-07-03	PM06:30:00	A.D..2021-07-03 P.M.18:30:00
+#
+# test format without order
+#
+SELECT TO_CHAR(c1, 'MM-YYYY-DD') AS C1, TO_CHAR(c2, 'HH:SS:MI') AS C2, TO_CHAR(c3, 'DD-YY-MM MI:SS:HH24') AS C3 FROM t_to_char1;
+C1	C2	C3
+01-1000-01	12:00:00	01-00-01 00:00:00
+12-9999-31	11:59:59	31-99-12 59:59:23
+01-2021-03	08:00:30	03-21-01 30:00:08
+07-2021-03	06:00:30	03-21-07 30:00:18
+SELECT TO_CHAR(c1, 'yyy-Dd-Mon') AS C1, TO_CHAR(c2, 'mi:Hh12:Ss') AS C2, TO_CHAR(c3, 'Ss:Hh:Mi Dy-y-Month') AS C3 FROM t_to_char1;
+C1	C2	C3
+000-01-Jan	00:12:00	00:12:00 Wed-0-January  
+999-31-Dec	59:11:59	59:11:59 Fri-9-December 
+021-03-Jan	30:08:00	00:08:30 Sun-1-January  
+021-03-Jul	30:06:00	00:06:30 Sat-1-July     
+SELECT TO_CHAR(c1, 'Dd-Mm-Rrrr') AS C1, TO_CHAR(c2, 'ss:hh:mi') AS C2, TO_CHAR(c3, 'ss:Rrrr-hh24-dd mon:mi') AS C3 FROM t_to_char1;
+C1	C2	C3
+01-01-1000	00:12:00	00:1000-00-01 Jan:00
+31-12-9999	59:11:59	59:9999-23-31 Dec:59
+03-01-2021	00:08:30	00:2021-08-03 Jan:30
+03-07-2021	00:06:30	00:2021-18-03 Jul:30
+SELECT TO_CHAR(c1, 'YYYYA.D.-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+C1	C2	C3
+1000A.D.-01-01	12:00:00	A.D..1000-01-01 00:00:00
+9999A.D.-12-31	11:59:59	A.D..9999-12-31 23:59:59
+2021A.D.-01-03	08:30:00	A.D..2021-01-03 08:30:00
+2021A.D.-07-03	06:30:00	A.D..2021-07-03 18:30:00
+#
+# test for special characters
+#
+SELECT TO_CHAR(c1, 'YYYYMMDD') AS C1, TO_CHAR(c2, 'HHMISS') AS C2, TO_CHAR(c3, 'YYMMDDHH24MISS') AS C3 FROM t_to_char1;
+C1	C2	C3
+10000101	120000	000101000000
+99991231	115959	991231235959
+20210103	083000	210103083000
+20210703	063000	210703183000
+SELECT TO_CHAR(c1, 'YYYY!!MM@DD') AS C1, TO_CHAR(c2, 'HH#MI$SS') AS C2, TO_CHAR(c3, 'YY%MM^DD*HH24(MI)SS') AS C3 FROM t_to_char1;
+C1	C2	C3
+1000!!01@01	12#00$00	00%01^01*00(00)00
+9999!!12@31	11#59$59	99%12^31*23(59)59
+2021!!01@03	08#30$00	21%01^03*08(30)00
+2021!!07@03	06#30$00	21%07^03*18(30)00
+SELECT TO_CHAR(c1, 'YYYY_MM+DD') AS C1, TO_CHAR(c2, 'HH=MI{SS') AS C2, TO_CHAR(c3, 'YY}MMDDHH24MISS') AS C3 FROM t_to_char1;
+C1	C2	C3
+1000_01+01	12=00{00	00}0101000000
+9999_12+31	11=59{59	99}1231235959
+2021_01+03	08=30{00	21}0103083000
+2021_07+03	06=30{00	21}0703183000
+SELECT TO_CHAR(c1, 'YYYY,MM.DD') AS C1, TO_CHAR(c2, 'HH/MI;SS') AS C2, TO_CHAR(c3, 'YY>MM<DD]HH24[MI\SS') AS C3 FROM t_to_char1;
+C1	C2	C3
+1000,01.01	12/00;00	00>01<01]00[0000
+9999,12.31	11/59;59	99>12<31]23[5959
+2021,01.03	08/30;00	21>01<03]08[3000
+2021,07.03	06/30;00	21>07<03]18[3000
+SELECT TO_CHAR(c1, 'YYYY||||MM|DD') AS C1, TO_CHAR(c2, 'HH&|MI|&|SS') AS C2, TO_CHAR(c3, 'YY&&&\\MM|&&|DD HH24|| MI&||"abx"|SS') AS C3 FROM t_to_char1;
+C1	C2	C3
+1000|||0101	12&|00&|00	00&&&\01&&|01 00| 00&|abx00
+9999|||1231	11&|59&|59	99&&&\12&&|31 23| 59&|abx59
+2021|||0103	08&|30&|00	21&&&\01&&|03 08| 30&|abx00
+2021|||0703	06&|30&|00	21&&&\07&&|03 18| 30&|abx00
+SELECT TO_CHAR(c1, 'YYYY&MM-DD') FROM t_to_char1 where c0=1;
+TO_CHAR(c1, 'YYYY&MM-DD')
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: date format not recognized
+SELECT TO_CHAR(c1, 'YYYY"abx"MM"bsz"DD') AS C1 FROM t_to_char1;
+C1
+1000abx01bsz01
+9999abx12bsz31
+2021abx01bsz03
+2021abx07bsz03
+#
+# test for other locale
+#
+SET character_set_client='utf8';
+SET character_set_connection='utf8';
+SET character_set_results='utf8';
+SET lc_time_names='zh_TW';
+SELECT TO_CHAR(c1, 'YYYY-MON-DAY') FROM t_to_char1;
+TO_CHAR(c1, 'YYYY-MON-DAY')
+1000- 1月-週三   
+9999-12月-週五   
+2021- 1月-週日   
+2021- 7月-週六   
+SET lc_time_names='de_DE';
+SELECT TO_CHAR(c1, 'YYYY-MON-DAY') FROM t_to_char1;
+TO_CHAR(c1, 'YYYY-MON-DAY')
+1000-Jan-Mittwoch 
+9999-Dez-Freitag  
+2021-Jan-Sonntag  
+2021-Jul-Samstag  
+SET lc_time_names='en_US';
+SELECT TO_CHAR(c1, 'YYYY-MON-DAY') FROM t_to_char1;
+TO_CHAR(c1, 'YYYY-MON-DAY')
+1000-Jan-Wednesday
+9999-Dec-Friday   
+2021-Jan-Sunday   
+2021-Jul-Saturday 
+SET lc_time_names='zh_CN';
+SELECT TO_CHAR(c1, 'YYYY-MON-DAY') FROM t_to_char1;
+TO_CHAR(c1, 'YYYY-MON-DAY')
+1000- 1月-星期三
+9999-12月-星期五
+2021- 1月-星期日
+2021- 7月-星期六
+#
+# test for invalid format
+#
+SELECT TO_CHAR(c1, 'YYYYaxMON-DAY') FROM t_to_char1 where c0 = 1;
+TO_CHAR(c1, 'YYYYaxMON-DAY')
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: date format not recognized
+SELECT TO_CHAR(c1, 'YYYY\nMON-DAY') FROM t_to_char1 where c0 = 1;
+TO_CHAR(c1, 'YYYY\nMON-DAY')
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: date format not recognized
+SELECT TO_CHAR(c1, 'YYYY\rMON-DAY') FROM t_to_char1 where c0 = 1;
+TO_CHAR(c1, 'YYYY\rMON-DAY')
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: date format not recognized
+SELECT TO_CHAR(c1, 'YYYY分隔MON-DAY') FROM t_to_char1 where c0 = 1;
+TO_CHAR(c1, 'YYYY分隔MON-DAY')
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: date format not recognized
+SELECT TO_CHAR(c1, 'YYYY-分隔MON-DAY') FROM t_to_char1 where c0 = 1;
+TO_CHAR(c1, 'YYYY-分隔MON-DAY')
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: date format not recognized
+select to_char(c3, 'YYYYxDDD') from t_to_char1 where c0 = 1;
+to_char(c3, 'YYYYxDDD')
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: date format not recognized
+select to_char(c3, 'YYYY&DDD') from t_to_char1 where c0 = 1;
+to_char(c3, 'YYYY&DDD')
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: date format not recognized
+select to_char(c3, 'xxYYYY-DD') from t_to_char1 where c0 = 1;
+to_char(c3, 'xxYYYY-DD')
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: date format not recognized
+SET character_set_client='latin1';
+SET character_set_connection='latin1';
+SET character_set_results='latin1';
+#
+# test for unusual format
+#
+select to_char(c3, 'YYYYYYYYYYYYYYY') from t_to_char1;
+to_char(c3, 'YYYYYYYYYYYYYYY')
+100010001000000
+999999999999999
+202120212021021
+202120212021021
+select to_char(c3, 'YYYYYYYYYYYYYYYDDDDDD') from t_to_char1;
+to_char(c3, 'YYYYYYYYYYYYYYYDDDDDD')
+100010001000000010101
+999999999999999313131
+202120212021021030303
+202120212021021030303
+#
+# oracle max length is 144
+#
+select to_char(c3, 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY') from t_to_char1 where c0 = 1;
+to_char(c3, 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY')
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: datetime format string is too long
+CREATE TABLE t_f(c1 varchar(150));
+insert into t_f values('YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY-DD');
+select to_char('2000-11-11', c1) from t_f;
+to_char('2000-11-11', c1)
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: datetime format string is too long
+DROP TABLE t_f;
+select to_char(c3, 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY-DD-MM') from t_to_char1 where c0 = 1;
+to_char(c3, 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY-DD-MM')
+100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000-01-01
+#
+# now only support two parameter.
+#
+select to_char(c3) from t_to_char1 where c0 =1;
+to_char(c3)
+1000-01-01 00:00:00
+select to_char(c3, "YYYY-MM-DD HH:MI:SS") from t_to_char1 where c0 =1;
+to_char(c3, "YYYY-MM-DD HH:MI:SS")
+1000-01-01 12:00:00
+select to_char(c3, "YYYY-MM-DD HH:MI:SS", "zh_CN") from t_to_char1 where c0 = 1;
+ERROR 42000: Incorrect parameter count in the call to native function 'to_char'
+select to_char(c3, "YYYY-MM-DD HH:MI:SS", "NLS_DATE_LANGUAGE = zh_CN") from t_to_char1 where c0 = 1;
+ERROR 42000: Incorrect parameter count in the call to native function 'to_char'
+#
+# oracle support format but mariadb dont support
+#
+select to_char(c3, 'DDD') from t_to_char1 where c0 = 1;
+to_char(c3, 'DDD')
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: date format not recognized
+select to_char(c3, 'D') from t_to_char1 where c0 = 1;
+to_char(c3, 'D')
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: date format not recognized
+select to_char(c3, 'DS') from t_to_char1 where c0 = 1;
+to_char(c3, 'DS')
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: date format not recognized
+select to_char(c3, 'IY') from t_to_char1 where c0 = 1;
+to_char(c3, 'IY')
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: date format not recognized
+select to_char(c3, 'IYYY') from t_to_char1 where c0 = 1;
+to_char(c3, 'IYYY')
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: date format not recognized
+#
+# test for first argument data type
+# 
+select to_char(1, 'yyyy');
+to_char(1, 'yyyy')
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: data type con not convert
+select to_char(1.1, 'yyyy');
+to_char(1.1, 'yyyy')
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: data type con not convert
+CREATE TABLE t_a(c1 int, c2 float, c3 decimal, c4 char(20), c5 varchar(20), c6 nchar(20), c7 nvarchar(20));
+insert into t_a VALUES (1, 3.2, 2002.02, '2000-11-11', '2000-11-11', '2000-11-11', '2000-11-11');
+Warnings:
+Note	1265	Data truncated for column 'c3' at row 1
+SELECT TO_CHAR(c1, 'YYYY') from t_a;
+TO_CHAR(c1, 'YYYY')
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: data type con not convert
+SELECT TO_CHAR(c2, 'YYYY') from t_a;
+TO_CHAR(c2, 'YYYY')
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: data type con not convert
+SELECT TO_CHAR(c3, 'YYYY') from t_a;
+TO_CHAR(c3, 'YYYY')
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: data type con not convert
+SELECT TO_CHAR(c4, 'YYYY') from t_a;
+TO_CHAR(c4, 'YYYY')
+2000
+SELECT TO_CHAR(c5, 'YYYY') from t_a;
+TO_CHAR(c5, 'YYYY')
+2000
+SELECT TO_CHAR(c6, 'YYYY') from t_a;
+TO_CHAR(c6, 'YYYY')
+2000
+SELECT TO_CHAR(c7, 'YYYY') from t_a;
+TO_CHAR(c7, 'YYYY')
+2000
+DROP TABLE t_a;
+CREATE TABLE t_b(c0 int, c1 char(20), c2 varchar(20), c3 nchar(20), c4 nvarchar(20));
+INSERT INTO t_b VALUES (1111, 'YYYY-MM-DD', 'YYYY-MM-DD', 'YYYY-MM-DD', 'YYYY-MM-DD');
+SELECT TO_CHAR('2000-11-11', c0) FROM t_b;
+TO_CHAR('2000-11-11', c0)
+NULL
+Warnings:
+Warning	4174	Oracle compatibility function error: date format not recognized
+SELECT TO_CHAR('2000-11-11', c1) FROM t_b;
+TO_CHAR('2000-11-11', c1)
+2000-11-11
+SELECT TO_CHAR('2000-11-11', c2) FROM t_b;
+TO_CHAR('2000-11-11', c2)
+2000-11-11
+SELECT TO_CHAR('2000-11-11', c3) FROM t_b;
+TO_CHAR('2000-11-11', c3)
+2000-11-11
+SELECT TO_CHAR('2000-11-11', c4) FROM t_b;
+TO_CHAR('2000-11-11', c4)
+2000-11-11
+DROP TABLE t_b;
+EXPLAIN EXTENDED SELECT TO_CHAR(c1, 'YYYY-MM-DD') FROM t_to_char1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	SIMPLE	t_to_char1	ALL	NULL	NULL	NULL	NULL	4	100.00	
+Warnings:
+Note	1003	select to_char(`test`.`t_to_char1`.`c1`,'YYYY-MM-DD') AS `TO_CHAR(c1, 'YYYY-MM-DD')` from `test`.`t_to_char1`
+#
+# test for time type with date format string
+#
+SELECT TO_CHAR(c2, 'YYYY-MM-DD HH:MI:SS') from t_to_char1;
+TO_CHAR(c2, 'YYYY-MM-DD HH:MI:SS')
+0000-00-00 12:00:00
+0000-00-00 11:59:59
+0000-00-00 08:30:00
+0000-00-00 06:30:00
+SELECT TO_CHAR(c2, 'YYYY-MON-DY HH:MI:SS') from t_to_char1;
+TO_CHAR(c2, 'YYYY-MON-DY HH:MI:SS')
+0000-00-00 12:00:00
+0000-00-00 11:59:59
+0000-00-00 08:30:00
+0000-00-00 06:30:00
+SELECT TO_CHAR(c2, 'MON-YYYY-DY HH:MI:SS') from t_to_char1;
+TO_CHAR(c2, 'MON-YYYY-DY HH:MI:SS')
+00-0000-00 12:00:00
+00-0000-00 11:59:59
+00-0000-00 08:30:00
+00-0000-00 06:30:00
+SELECT TO_CHAR(c2, 'YYYY-MONTH-DAY HH:MI:SS') from t_to_char1;
+TO_CHAR(c2, 'YYYY-MONTH-DAY HH:MI:SS')
+0000-00-00 12:00:00
+0000-00-00 11:59:59
+0000-00-00 08:30:00
+0000-00-00 06:30:00
+DROP TABLE t_to_char1;
+DROP TABLE t_to_char2;
diff --git a/mysql-test/suite/compat/oracle/t/func_to_char.test b/mysql-test/suite/compat/oracle/t/func_to_char.test
new file mode 100644
index 00000000000..a85745e0d28
--- /dev/null
+++ b/mysql-test/suite/compat/oracle/t/func_to_char.test
@@ -0,0 +1,183 @@
+##############################################################
+# testcase for TO_CHAR() function for oracle
+##############################################################
+
+--echo #
+--echo # test for datetime
+--echo #
+
+CREATE TABLE t_to_char1(c0 int, c1 date, c2 time, c3 datetime);
+
+INSERT INTO t_to_char1 VALUES (1, '1000-1-1', '00:00:00', '1000-1-1 00:00:00');
+INSERT INTO t_to_char1 VALUES (2, '9999-12-31', '23:59:59', '9999-12-31 23:59:59');
+INSERT INTO t_to_char1 VALUES (3, '2021-01-03', '08:30:00', '2021-01-03 08:30:00');
+INSERT INTO t_to_char1 VALUES (4, '2021-07-03', '18:30:00', '2021-07-03 18:30:00');
+
+CREATE TABLE t_to_char2(c1 timestamp);
+INSERT INTO t_to_char2 VALUES ('1980-01-11 04:50:39');
+INSERT INTO t_to_char2 VALUES ('2000-11-11 12:50:00');
+INSERT INTO t_to_char2 VALUES ('2030-11-11 18:20:10');
+
+# test for timestamp
+SELECT TO_CHAR(c1, 'YYYY-MM-DD') FROM t_to_char2;
+SELECT TO_CHAR(c1, 'HH24-MI-SS') FROM t_to_char2;
+
+# test full output format
+--echo #
+--echo # test YYYY/YY/MM/DD/HH/HH24/MI/SS
+--echo #
+SELECT TO_CHAR(c1, 'YYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'YY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'yyyy-mm-dd') AS C1, TO_CHAR(c2, 'hh:mi:ss') AS C2, TO_CHAR(c3, 'yy-mm-dd hh24:mi:ss') AS C3 FROM t_to_char1;
+
+--echo #
+--echo # test YYY/Y/MON/DD/DY/HH/HH12/MI/SS
+--echo #
+SELECT TO_CHAR(c1, 'YYY-MON-DD') AS C1, TO_CHAR(c2, 'HH12:MI:SS') AS C2, TO_CHAR(c3, 'Y-MONTH-DY HH:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'yyy-Mon-Dd') AS C1, TO_CHAR(c2, 'Hh12:mi:Ss') AS C2, TO_CHAR(c3, 'y-Month-Dy Hh:Mi:Ss') AS C3 FROM t_to_char1;
+
+--echo #
+--echo # test RRRR/RR/DAY
+--echo #
+SELECT TO_CHAR(c1, 'RRRR-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'RRRR-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'RR-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'YY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'Rrrr-Mm-Dd') AS C1, TO_CHAR(c2, 'hh:mi:ss') AS C2, TO_CHAR(c3, 'Rrrr-mm-dd Hh24:mi:ss') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'rr-mm-dd') AS C1, TO_CHAR(c2, 'hh:mi:ss') AS C2, TO_CHAR(c3, 'yy-mm-dd hh24:Mi:ss') AS C3 FROM t_to_char1;
+
+--echo #
+--echo # test AD/A.D./BC/B.C./AM/A.M./PM/P.M.
+--echo #
+SELECT TO_CHAR(c1, 'ADYYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'AD.YYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'A.D.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'ADYYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'AD.YYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'A.D.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'BCYYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'BCYYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'B.C.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'B.C.YYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'bcyyyy-mm-dd') AS C1, TO_CHAR(c2, 'hh:mi:ss') AS C2, TO_CHAR(c3, 'BcYYyy-MM-DD Hh24:mi:sS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'b.c.yyyy-mm-dd') AS C1, TO_CHAR(c2, 'hh:mI:Ss') AS C2, TO_CHAR(c3, 'b.C.Yyyy-Mm-dd hH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'A.D.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'PMHH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD P.M.HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'A.D.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'pmHH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD p.m.HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'A.D.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'AMHH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD A.m.HH24:MI:SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'A.D.YYYY-MM-DD') AS C1, TO_CHAR(c2, 'amHH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD a.M.HH24:MI:SS') AS C3 FROM t_to_char1;
+
+--echo #
+--echo # test format without order
+--echo #
+SELECT TO_CHAR(c1, 'MM-YYYY-DD') AS C1, TO_CHAR(c2, 'HH:SS:MI') AS C2, TO_CHAR(c3, 'DD-YY-MM MI:SS:HH24') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'yyy-Dd-Mon') AS C1, TO_CHAR(c2, 'mi:Hh12:Ss') AS C2, TO_CHAR(c3, 'Ss:Hh:Mi Dy-y-Month') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'Dd-Mm-Rrrr') AS C1, TO_CHAR(c2, 'ss:hh:mi') AS C2, TO_CHAR(c3, 'ss:Rrrr-hh24-dd mon:mi') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'YYYYA.D.-MM-DD') AS C1, TO_CHAR(c2, 'HH:MI:SS') AS C2, TO_CHAR(c3, 'A.D..YYYY-MM-DD HH24:MI:SS') AS C3 FROM t_to_char1;
+
+--echo #
+--echo # test for special characters
+--echo #
+SELECT TO_CHAR(c1, 'YYYYMMDD') AS C1, TO_CHAR(c2, 'HHMISS') AS C2, TO_CHAR(c3, 'YYMMDDHH24MISS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'YYYY!!MM@DD') AS C1, TO_CHAR(c2, 'HH#MI$SS') AS C2, TO_CHAR(c3, 'YY%MM^DD*HH24(MI)SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'YYYY_MM+DD') AS C1, TO_CHAR(c2, 'HH=MI{SS') AS C2, TO_CHAR(c3, 'YY}MMDDHH24MISS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'YYYY,MM.DD') AS C1, TO_CHAR(c2, 'HH/MI;SS') AS C2, TO_CHAR(c3, 'YY>MM<DD]HH24[MI\SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'YYYY||||MM|DD') AS C1, TO_CHAR(c2, 'HH&|MI|&|SS') AS C2, TO_CHAR(c3, 'YY&&&\\MM|&&|DD HH24|| MI&||"abx"|SS') AS C3 FROM t_to_char1;
+SELECT TO_CHAR(c1, 'YYYY&MM-DD') FROM t_to_char1 where c0=1;
+SELECT TO_CHAR(c1, 'YYYY"abx"MM"bsz"DD') AS C1 FROM t_to_char1;
+
+--echo #
+--echo # test for other locale
+--echo #
+SET character_set_client='utf8';
+SET character_set_connection='utf8';
+SET character_set_results='utf8';
+SET lc_time_names='zh_TW';
+SELECT TO_CHAR(c1, 'YYYY-MON-DAY') FROM t_to_char1;
+SET lc_time_names='de_DE';
+SELECT TO_CHAR(c1, 'YYYY-MON-DAY') FROM t_to_char1;
+SET lc_time_names='en_US';
+SELECT TO_CHAR(c1, 'YYYY-MON-DAY') FROM t_to_char1;
+SET lc_time_names='zh_CN';
+SELECT TO_CHAR(c1, 'YYYY-MON-DAY') FROM t_to_char1;
+
+--echo #
+--echo # test for invalid format
+--echo #
+SELECT TO_CHAR(c1, 'YYYYaxMON-DAY') FROM t_to_char1 where c0 = 1;
+SELECT TO_CHAR(c1, 'YYYY\nMON-DAY') FROM t_to_char1 where c0 = 1;
+SELECT TO_CHAR(c1, 'YYYY\rMON-DAY') FROM t_to_char1 where c0 = 1;
+SELECT TO_CHAR(c1, 'YYYY分隔MON-DAY') FROM t_to_char1 where c0 = 1;
+SELECT TO_CHAR(c1, 'YYYY-分隔MON-DAY') FROM t_to_char1 where c0 = 1;
+select to_char(c3, 'YYYYxDDD') from t_to_char1 where c0 = 1;
+select to_char(c3, 'YYYY&DDD') from t_to_char1 where c0 = 1;
+select to_char(c3, 'xxYYYY-DD') from t_to_char1 where c0 = 1;
+
+SET character_set_client='latin1';
+SET character_set_connection='latin1';
+SET character_set_results='latin1';
+--echo #
+--echo # test for unusual format
+--echo #
+select to_char(c3, 'YYYYYYYYYYYYYYY') from t_to_char1;
+select to_char(c3, 'YYYYYYYYYYYYYYYDDDDDD') from t_to_char1;
+
+--echo #
+--echo # oracle max length is 144
+--echo #
+select to_char(c3, 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY') from t_to_char1 where c0 = 1;
+CREATE TABLE t_f(c1 varchar(150));
+insert into t_f values('YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY-DD');
+select to_char('2000-11-11', c1) from t_f;
+DROP TABLE t_f;
+select to_char(c3, 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY-DD-MM') from t_to_char1 where c0 = 1;
+
+--echo #
+--echo # now only support two parameter.
+--echo #
+select to_char(c3) from t_to_char1 where c0 =1;
+select to_char(c3, "YYYY-MM-DD HH:MI:SS") from t_to_char1 where c0 =1;
+--error 1582
+select to_char(c3, "YYYY-MM-DD HH:MI:SS", "zh_CN") from t_to_char1 where c0 = 1;
+--error 1582
+select to_char(c3, "YYYY-MM-DD HH:MI:SS", "NLS_DATE_LANGUAGE = zh_CN") from t_to_char1 where c0 = 1;
+
+--echo #
+--echo # oracle support format but mariadb dont support
+--echo #
+select to_char(c3, 'DDD') from t_to_char1 where c0 = 1;
+select to_char(c3, 'D') from t_to_char1 where c0 = 1;
+select to_char(c3, 'DS') from t_to_char1 where c0 = 1;
+select to_char(c3, 'IY') from t_to_char1 where c0 = 1;
+select to_char(c3, 'IYYY') from t_to_char1 where c0 = 1;
+
+--echo #
+--echo # test for first argument data type
+--echo # 
+select to_char(1, 'yyyy');
+select to_char(1.1, 'yyyy');
+CREATE TABLE t_a(c1 int, c2 float, c3 decimal, c4 char(20), c5 varchar(20), c6 nchar(20), c7 nvarchar(20));
+insert into t_a VALUES (1, 3.2, 2002.02, '2000-11-11', '2000-11-11', '2000-11-11', '2000-11-11');
+SELECT TO_CHAR(c1, 'YYYY') from t_a;
+SELECT TO_CHAR(c2, 'YYYY') from t_a;
+SELECT TO_CHAR(c3, 'YYYY') from t_a;
+SELECT TO_CHAR(c4, 'YYYY') from t_a;
+SELECT TO_CHAR(c5, 'YYYY') from t_a;
+SELECT TO_CHAR(c6, 'YYYY') from t_a;
+SELECT TO_CHAR(c7, 'YYYY') from t_a;
+DROP TABLE t_a;
+
+CREATE TABLE t_b(c0 int, c1 char(20), c2 varchar(20), c3 nchar(20), c4 nvarchar(20));
+INSERT INTO t_b VALUES (1111, 'YYYY-MM-DD', 'YYYY-MM-DD', 'YYYY-MM-DD', 'YYYY-MM-DD');
+SELECT TO_CHAR('2000-11-11', c0) FROM t_b;
+SELECT TO_CHAR('2000-11-11', c1) FROM t_b;
+SELECT TO_CHAR('2000-11-11', c2) FROM t_b;
+SELECT TO_CHAR('2000-11-11', c3) FROM t_b;
+SELECT TO_CHAR('2000-11-11', c4) FROM t_b;
+DROP TABLE t_b;
+
+EXPLAIN EXTENDED SELECT TO_CHAR(c1, 'YYYY-MM-DD') FROM t_to_char1;
+
+--echo #
+--echo # test for time type with date format string
+--echo #
+SELECT TO_CHAR(c2, 'YYYY-MM-DD HH:MI:SS') from t_to_char1;
+SELECT TO_CHAR(c2, 'YYYY-MON-DY HH:MI:SS') from t_to_char1;
+SELECT TO_CHAR(c2, 'MON-YYYY-DY HH:MI:SS') from t_to_char1;
+SELECT TO_CHAR(c2, 'YYYY-MONTH-DAY HH:MI:SS') from t_to_char1;
+
+DROP TABLE t_to_char1;
+DROP TABLE t_to_char2;
+
diff --git a/sql/item_create.cc b/sql/item_create.cc
index 62f4d9f9fee..1a089cdbe67 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -2131,6 +2131,19 @@ class Create_func_to_base64 : public Create_func_arg1
 };
 
 
+class Create_func_to_char : public Create_native_func
+{
+public:
+  virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
+
+  static Create_func_to_char s_singleton;
+
+protected:
+  Create_func_to_char() {}
+  virtual ~Create_func_to_char() {}
+};
+
+
 class Create_func_to_days : public Create_func_arg1
 {
 public:
@@ -5130,6 +5143,44 @@ Create_func_to_base64::create_1_arg(THD *thd, Item *arg1)
 }
 
 
+Create_func_to_char Create_func_to_char::s_singleton;
+
+Item*
+Create_func_to_char::create_native(THD *thd, LEX_CSTRING *name,
+				   List<Item> *item_list)
+{
+  Item *func= NULL;
+  int arg_count= 0;
+
+  if (item_list != NULL)
+    arg_count= item_list->elements;
+
+  switch (arg_count) {
+  case 1:
+  {
+    Item *param_1= item_list->pop();
+    Item *i0= new (thd->mem_root) Item_string_sys(thd, "YYYY-MM-DD HH24:MI:SS",  21);
+    func= new (thd->mem_root) Item_func_tochar(thd, param_1, i0);
+    break;
+  }
+  case 2:
+  {
+    Item *param_1= item_list->pop();
+    Item *param_2= item_list->pop();
+    func= new (thd->mem_root) Item_func_tochar(thd, param_1, param_2);
+    break;
+  }
+  default:
+  {
+    my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
+    break;
+  }
+  }
+
+  return func;
+}
+
+
 Create_func_to_days Create_func_to_days::s_singleton;
 
 Item*
@@ -5577,6 +5628,7 @@ static Native_func_registry func_array[] =
   { { STRING_WITH_LEN("TIME_FORMAT") }, BUILDER(Create_func_time_format)},
   { { STRING_WITH_LEN("TIME_TO_SEC") }, BUILDER(Create_func_time_to_sec)},
   { { STRING_WITH_LEN("TO_BASE64") }, BUILDER(Create_func_to_base64)},
+  { { STRING_WITH_LEN("TO_CHAR") }, BUILDER(Create_func_to_char)},
   { { STRING_WITH_LEN("TO_DAYS") }, BUILDER(Create_func_to_days)},
   { { STRING_WITH_LEN("TO_SECONDS") }, BUILDER(Create_func_to_seconds)},
   { { STRING_WITH_LEN("UCASE") }, BUILDER(Create_func_ucase)},
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index b4f706984a5..92184118bd3 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -54,6 +54,7 @@ C_MODE_END
 #include "sql_show.h"                           // append_identifier
 #include <sql_repl.h>
 #include "sql_statistics.h"
+#include "oracle_compat.h"
 
 size_t username_char_length= 80;
 
@@ -5371,3 +5372,716 @@ longlong Item_func_wsrep_sync_wait_upto::val_int()
 }
 
 #endif /* WITH_WSREP */
+
+/**
+  modify the quotation flag and check whether the subsequent process is skipped
+*/
+static bool check_quotation(const short cfmt, bool *quotation_flag)
+{
+  if (cfmt == '"')
+  {
+    if (*quotation_flag)
+      *quotation_flag= false;
+    else
+      *quotation_flag= true;
+    return true;
+  }
+
+  if (*quotation_flag)
+    return true;
+  return false;
+}
+
+#define INVALID_CHARACTER(x) (((x) >= 'A' && (x) <= 'Z') ||((x) >= '0' && (x) <= '9') || (x) >= 127 || ((x) < 32))
+
+/**
+  Special characters are directly output in the result
+*/
+static int parse_sepcial(char cfmt, const char *ptr, const char *end, const short *arr)
+{
+  int offset= 0;
+  char tmp1= 0;
+  char *beg= (char *)ptr;
+  short *array= (short *)arr;
+
+  /* Non-printable character and Multibyte encoded characters */
+  if (INVALID_CHARACTER(cfmt))
+    return 0;
+
+  /*
+   * '&' with text is used for variable input, but '&' with other
+   * special charaters like '|'. '*' is used as separator
+   */
+  tmp1= my_toupper(system_charset_info, *(beg+1));
+  if (cfmt == '&' && (tmp1 >= 'A' && tmp1 <= 'Z'))
+    return 0;
+
+  do {
+    /*
+     * Continuous store the special characters in fmt_array until non-special
+     * characters appear
+     */
+    tmp1= my_toupper(system_charset_info, *(beg+1));
+    if (INVALID_CHARACTER(tmp1) || tmp1 == '"')
+    {
+      if (tmp1 != '"')
+      {
+        *array= *beg;
+        offset++;
+      }
+      break;
+    }
+
+    *array= *beg;
+    offset++;
+    beg++;
+    array++;
+  } while (beg<end);
+
+  return offset;
+}
+
+/**
+  parse the format string, convert it to no array and calculate the length of output string
+*/
+
+bool Item_func_tochar::parse_format_string(const String *format, uint *fmt_len)
+{
+  char cfmt= 0;
+  uint tmp_len= 0, ulen= 0;
+  const char *ptr, *end;
+  short *tmp_fmt= fmt_array;
+  bool quotation_flag= false;
+  int offset= 0;
+
+  ptr= format->ptr();
+  end= ptr + format->length();
+
+  for (; ptr < end; ptr++, tmp_fmt++)
+  {
+    cfmt= my_toupper(system_charset_info, *ptr);
+
+    /* 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.
+     *
+     * NOTE: the quotation mark is not print in return value. for example:
+     * select TO_CHAR(sysdate, 'YYYY"abc"MM"xyzDD"') will return 2021abc01xyz11
+     */
+    if (check_quotation(cfmt, &quotation_flag))
+    {
+      *tmp_fmt= *ptr;
+      tmp_len+= 1;
+      continue;
+    }
+
+    switch (cfmt) {
+      /*
+       * AD/A.D./AM/A.M.
+       */
+    case 'A':
+      if (my_toupper(system_charset_info, *(ptr+1)) == 'D')
+      {
+        *tmp_fmt= FMT_AD;
+        ptr+= 1;
+        tmp_len+= 2;
+      }
+      else if (my_toupper(system_charset_info, *(ptr+1)) == 'M')
+      {
+        *tmp_fmt= FMT_AM;
+        ptr+= 1;
+        tmp_len+= 2;
+      }
+      else if (my_toupper(system_charset_info, *(ptr+1)) == '.' &&
+               my_toupper(system_charset_info, *(ptr+2)) == 'D' &&
+               my_toupper(system_charset_info, *(ptr+3)) == '.')
+      {
+        *tmp_fmt= FMT_AD_DOT;
+        ptr+= 3;
+        tmp_len+= 4;
+      }
+      else if (my_toupper(system_charset_info, *(ptr+1)) == '.' &&
+               my_toupper(system_charset_info, *(ptr+2)) == 'M' &&
+               my_toupper(system_charset_info, *(ptr+3)) == '.')
+      {
+        *tmp_fmt= FMT_AM_DOT;
+        ptr+= 3;
+        tmp_len+= 4;
+      }
+      else
+      {
+        return 1;
+      }
+      break;
+      /* BC/B.C./PM/P.M. */
+    case 'B':
+      if (my_toupper(system_charset_info, *(ptr+1)) == 'C')
+      {
+        *tmp_fmt= FMT_BC;
+        ptr+= 1;
+        tmp_len+= 2;
+      }
+      else if (my_toupper(system_charset_info, *(ptr+1)) == '.' &&
+               my_toupper(system_charset_info, *(ptr+2)) == 'C' &&
+               my_toupper(system_charset_info, *(ptr+3)) == '.')
+      {
+        *tmp_fmt= FMT_BC_DOT;
+        ptr+= 3;
+        tmp_len+= 4;
+      }
+      else
+      {
+        return 1;
+      }
+      break;
+
+    case 'P':
+      if (my_toupper(system_charset_info, *(ptr+1)) == 'M')
+      {
+        *tmp_fmt= FMT_PM;
+        ptr+= 1;
+        tmp_len+= 2;
+      }
+      else if (my_toupper(system_charset_info, *(ptr+1)) == '.' &&
+               my_toupper(system_charset_info, *(ptr+2)) == 'M' &&
+               my_toupper(system_charset_info, *(ptr+3)) == '.')
+      {
+        *tmp_fmt= FMT_PM_DOT;
+        ptr+= 3;
+        tmp_len+= 4;
+      }
+      else
+      {
+        return 1;
+      }
+      break;
+
+    case 'Y':
+      if (my_toupper(system_charset_info, *(ptr+1)) != 'Y')
+      {
+        *tmp_fmt= FMT_Y;
+        tmp_len+= 1;
+        break;
+      }
+
+      if (my_toupper(system_charset_info, *(ptr+2)) != 'Y') /* YY */
+      {
+        *tmp_fmt= FMT_YY;
+        ptr+= 1;
+        ulen= 2;
+      }
+      else
+      {
+        if (my_toupper(system_charset_info, *(ptr+3)) == 'Y')
+        {
+          *tmp_fmt= FMT_YYYY;
+          ptr+= 3;
+          ulen= 4;
+        }
+        else
+        {
+          *tmp_fmt= FMT_YYY;
+          ptr+= 2;
+          ulen= 3;
+        }
+      }
+
+      tmp_len+= ulen;
+      break;
+
+    case 'R':
+      if (my_toupper(system_charset_info, *(ptr+1)) != 'R')
+        return 1;
+
+      if (my_toupper(system_charset_info, *(ptr+2)) != 'R')
+      {
+        *tmp_fmt= FMT_RR;
+        ptr+= 1;
+        ulen= 2;
+      }
+      else
+      {
+        if (my_toupper(system_charset_info, *(ptr+3)) != 'R')
+          return 1;
+        *tmp_fmt= FMT_RRRR;
+        ptr+= 3;
+        ulen= 4;
+      }
+      tmp_len+= ulen;
+      break;
+
+    case 'M':
+      {
+        char tmp1= my_toupper(system_charset_info, *(ptr+1));
+        char tmp2= my_toupper(system_charset_info, *(ptr+2));
+        char tmp3= my_toupper(system_charset_info, *(ptr+3));
+        char tmp4= my_toupper(system_charset_info, *(ptr+4));
+
+        if (tmp1 != 'M' && tmp1 != 'I' && tmp1 != 'O')
+          return 1;
+
+        if (tmp1 == 'M')
+        {
+          *tmp_fmt= FMT_MM;
+          tmp_len+= 2;
+          ptr+= 1;
+        }
+        else if (tmp1 == 'I')
+        {
+          *tmp_fmt= FMT_MI;
+          tmp_len+= 2;
+          ptr+= 1;
+        }
+        else
+        {
+          if (tmp2 != 'N')
+            return 1;
+          if (tmp3 != 'T' || tmp4 != 'H')
+          {
+            *tmp_fmt= FMT_MON;
+            tmp_len+= 3;
+            ptr+= 2;
+          }
+          else
+          {
+            *tmp_fmt= FMT_MONTH;
+            tmp_len+= 9;
+            ptr+= 4;
+          }
+        }
+      }
+      break;
+
+    case 'D':
+      {
+        char tmp1= my_toupper(system_charset_info, *(ptr+1));
+
+        if (tmp1 != 'D' && tmp1 != 'Y' && tmp1 != 'A')
+          return 1;
+
+        if (tmp1 == 'D')
+        {
+          *tmp_fmt= FMT_DD;
+          tmp_len+= 2;
+
+        }
+        else if (tmp1 == 'Y')
+        {
+          *tmp_fmt= FMT_DY;
+          tmp_len+= 3;
+        }
+        else
+        {
+          if (my_toupper(system_charset_info, *(ptr+2)) != 'Y')
+            return 1;
+          *tmp_fmt= FMT_DAY;
+          tmp_len= 9;
+          ptr+= 1;
+        }
+        ptr+= 1;
+      }
+      break;
+
+    case 'H':
+      {
+        char tmp1= my_toupper(system_charset_info, *(ptr+1));
+        char tmp2= my_toupper(system_charset_info, *(ptr+2));
+        char tmp3= my_toupper(system_charset_info, *(ptr+3));
+
+        if (tmp1 != 'H')
+          return 1;
+
+        if (tmp2 == '1' && tmp3 == '2')
+        {
+          *tmp_fmt= FMT_HH12;
+          ptr+= 3;
+        }
+        else if (tmp2 == '2' && tmp3 == '4')
+        {
+          *tmp_fmt= FMT_HH24;
+          ptr+= 3;
+        }
+        else
+        {
+          *tmp_fmt= FMT_HH;
+          ptr+= 1;
+        }
+        tmp_len+= 4;
+      }
+      break;
+
+    case 'S':
+      if (my_toupper(system_charset_info, *(ptr+1)) != 'S')
+        return 1;
+
+      *tmp_fmt= FMT_SS;
+      tmp_len+= 2;
+      ptr+= 1;
+      break;
+
+    case '|':
+      /*
+       * if only one '|' just ignore it, or append others, for example:
+       * TO_CHAR('2000-11-05', 'YYYY|MM||||DD') --> 200011|||05
+       */
+      if (my_toupper(system_charset_info, *(ptr+1)) != '|')
+      {
+        tmp_fmt--;
+        break;
+      }
+
+      do
+      {
+        if (my_toupper(system_charset_info, *(ptr)) != '|')
+        {
+          ptr-= 2;
+          tmp_fmt-= 2;
+          break;
+        }
+        *tmp_fmt= *ptr;
+        ptr++;
+        tmp_fmt++;
+        tmp_len++;
+      }while (ptr<end);
+      break;
+
+    default:
+      offset= parse_sepcial(cfmt, ptr, end, tmp_fmt);
+      if (!offset)
+        return 1;
+      /* ptr++ in for loop, so we must move ptr to offset-1 */
+      ptr+= (offset-1);
+      tmp_fmt+= (offset-1);
+      tmp_len+= offset;
+      break;
+    }
+  }
+
+  *fmt_len= tmp_len;
+  *tmp_fmt= 0;
+  return 0;
+}
+
+static inline bool append_val(int val, int size, String *str)
+{
+  ulong len= 0;
+  char intbuff[15];
+
+  len= (ulong) (int10_to_str(val, intbuff, 10) - intbuff);
+  return str->append_with_prefill(intbuff, len, size, '0');
+}
+
+static bool make_date_time_oracle(const short *fmt_array,
+                                  const MYSQL_TIME *l_time,
+                                  const MY_LOCALE *locale,
+                                  String *str)
+{
+  bool quotation_flag= false;
+  short *ptr= (short *)fmt_array;
+  uint hours_i;
+  uint weekday;
+
+  str->length(0);
+
+  while (*ptr)
+  {
+    if (check_quotation(*ptr, &quotation_flag))
+    {
+      /* don't display '"' in the result, so if it is '"', skip it */
+      if (*ptr != '"')
+        str->append(*ptr);
+      ptr++;
+      continue;
+    }
+
+    switch (*ptr) {
+
+    case FMT_AM:
+    case FMT_PM:
+      if (l_time->hour > 11)
+        str->append("PM");
+      else
+        str->append("AM");
+      break;
+
+    case FMT_AM_DOT:
+    case FMT_PM_DOT:
+      if (l_time->hour > 11)
+        str->append("P.M.");
+      else
+        str->append("A.M.");
+      break;
+
+    case FMT_AD:
+    case FMT_BC:
+      if (l_time->year > 0)
+        str->append("AD");
+      else
+        str->append("BC");
+      break;
+
+    case FMT_AD_DOT:
+    case FMT_BC_DOT:
+      if (l_time->year > 0)
+        str->append("A.D.");
+      else
+        str->append("B.C.");
+      break;
+
+    case FMT_Y:
+      if (append_val(l_time->year%10, 1, str))
+        goto err_exit;
+      break;
+
+    case FMT_YY:
+    case FMT_RR:
+      if (append_val(l_time->year%100, 2, str))
+        goto err_exit;
+      break;
+
+    case FMT_YYY:
+      if (append_val(l_time->year%1000, 3, str))
+        goto err_exit;
+      break;
+
+    case FMT_YYYY:
+    case FMT_RRRR:
+      if (append_val(l_time->year, 4, str))
+        goto err_exit;
+      break;
+
+    case FMT_MM:
+      if (append_val(l_time->month, 2, str))
+        goto err_exit;
+      break;
+
+    case FMT_MON:
+      {
+        if (l_time->month == 0)
+        {
+          str->append("00", 2, system_charset_info);
+        }
+        else
+        {
+          const char *month_name= locale->ab_month_names->type_names[l_time->month-1];
+          uint m_len= (uint) strlen(month_name);
+          str->append(month_name, m_len, system_charset_info);
+        }
+      }
+      break;
+
+    case FMT_MONTH:
+      {
+        if (l_time->month == 0)
+        {
+          str->append("00", 2, system_charset_info);
+        }
+        else
+        {
+          const char *month_name= locale->month_names->type_names[l_time->month-1];
+          uint m_len= (uint) strlen(month_name);
+          str->append(month_name, m_len, system_charset_info);
+          for (uint i=0; i<(9-m_len); i++)
+            str->append(" ", 1, system_charset_info);
+        }
+      }
+      break;
+
+    case FMT_DD:
+      if (append_val(l_time->day, 2, str))
+        goto err_exit;
+      break;
+
+    case FMT_DY:
+      {
+        if (l_time->day == 0)
+        {
+          str->append("00", 2, system_charset_info);
+        }
+        else
+        {
+          weekday=calc_weekday(calc_daynr(l_time->year,l_time->month,
+                                          l_time->day), 0);
+
+          const char *day_name= locale->ab_day_names->type_names[weekday];
+          str->append(day_name, (uint) strlen(day_name), system_charset_info);
+        }
+      }
+      break;
+
+    case FMT_DAY:
+      {
+        if (l_time->day == 0)
+        {
+          str->append("00", 2, system_charset_info);
+        }
+        else
+        {
+          weekday=calc_weekday(calc_daynr(l_time->year,l_time->month,
+                                          l_time->day), 0);
+
+          const char *day_name= locale->day_names->type_names[weekday];
+          uint day_len= strlen(day_name);
+          str->append(day_name, day_len, system_charset_info);
+          for (uint i=0; i<(9-day_len); i++)
+            str->append(" ", 1, system_charset_info);
+        }
+      }
+      break;
+
+    case FMT_HH12:
+    case FMT_HH:
+      hours_i= (l_time->hour%24 + 11)%12+1;
+      if (append_val(hours_i, 2, str))
+        goto err_exit;
+      break;
+
+    case FMT_HH24:
+      if (append_val(l_time->hour, 2, str))
+        goto err_exit;
+      break;
+
+    case FMT_MI:
+      if (append_val(l_time->minute, 2, str))
+        goto err_exit;
+      break;
+
+    case FMT_SS:
+      if (append_val(l_time->second, 2, str))
+        goto err_exit;
+      break;
+
+    default:
+      str->append(*ptr);
+    }
+
+    ptr++;
+  };
+  return false;
+
+err_exit:
+  return true;
+}
+
+bool Item_func_tochar::fix_length_and_dec()
+{
+  THD* thd= current_thd;
+
+  locale= thd->variables.lc_time_names;
+  Item *arg1= args[1]->this_item();
+
+  CHARSET_INFO *cs= thd->variables.collation_connection;
+  my_repertoire_t repertoire= arg1->collation.repertoire;
+  if (!thd->variables.lc_time_names->is_ascii)
+    repertoire|= MY_REPERTOIRE_EXTENDED;
+  collation.set(cs, arg1->collation.derivation, repertoire);
+  StringBuffer<STRING_BUFFER_USUAL_SIZE> buffer;
+  String *str;
+
+  /* first argument must be datetime or string */
+  enum_field_types arg0_mysql_type= args[0]->field_type();
+
+  switch (arg0_mysql_type) {
+  case MYSQL_TYPE_TIME:
+  case MYSQL_TYPE_DATE:
+  case MYSQL_TYPE_DATETIME:
+  case MYSQL_TYPE_TIMESTAMP:
+  case MYSQL_TYPE_VARCHAR:
+  case MYSQL_TYPE_STRING:
+    break;
+
+  default:
+    warning_message.append("data type con not convert");
+    goto warning_exit;
+  }
+
+  if (args[1]->basic_const_item() && (str= args[1]->val_str(&buffer)))
+  {
+    uint ulen= 0;
+    fixed_length= 1;
+    if (str->length() > MAX_DATETIME_FORMAT_MODEL_LEN)
+    {
+      warning_message.append("datetime format string is too long");
+      goto warning_exit;
+    }
+
+    if (parse_format_string(str, &ulen))
+    {
+      warning_message.append("date format not recognized");
+      goto warning_exit;
+    }
+
+    max_length= ulen * collation.collation->mbmaxlen;
+  }
+  else
+  {
+    fixed_length= 0;
+    max_length= MY_MIN(arg1->max_length, MAX_BLOB_WIDTH) * 10 *
+      collation.collation->mbmaxlen;
+    set_if_smaller(max_length,MAX_BLOB_WIDTH);
+  }
+
+warning_exit:
+  maybe_null=1;
+  return FALSE;
+}
+
+String* Item_func_tochar::val_str(String* str)
+ {
+  StringBuffer<64> format_buffer;
+  String *format;
+  MYSQL_TIME l_time;
+  const MY_LOCALE *lc= locale;
+  date_conv_mode_t mode= TIME_CONV_NONE;
+  THD *thd= current_thd;
+
+  if (warning_message.length())
+    goto null_date;
+
+  if ((null_value= args[0]->get_date(thd, &l_time,
+                                     Temporal::Options(mode, thd))))
+      return 0;
+
+  if (!fixed_length)
+  {
+    uint ulen= 0;
+
+    if (!(format= args[1]->val_str(&format_buffer)) || !format->length())
+      goto null_date;
+
+    if (format->length() > MAX_DATETIME_FORMAT_MODEL_LEN)
+    {
+      warning_message.append("datetime format string is too long");
+      goto null_date;
+    }
+
+    if (parse_format_string(format, &ulen))
+    {
+      warning_message.append("date format not recognized");
+      goto null_date;
+    }
+  }
+
+  DBUG_ASSERT(format != str);
+  if (str->alloc(max_length))
+    goto null_date;
+
+  /* Create the result string */
+  str->set_charset(collation.collation);
+  if (!make_date_time_oracle(fmt_array, &l_time, lc, str))
+    return str;
+
+null_date:
+
+  if (warning_message.length())
+  {
+    push_warning_printf(thd,
+                        Sql_condition::WARN_LEVEL_WARN,
+                        ER_ORACLE_COMPAT_FUNCTION_ERROR,
+                        ER_THD(thd, ER_ORACLE_COMPAT_FUNCTION_ERROR),
+                        warning_message.ptr());
+  }
+
+  null_value= 1;
+  return 0;
+}
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index 1ae62a4a6e8..8becff887f6 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -25,6 +25,9 @@
 #pragma interface			/* gcc class implementation */
 #endif
 
+/* the max length of datetime format models string in oralce is 144 */
+#define MAX_DATETIME_FORMAT_MODEL_LEN 144
+
 extern size_t username_char_length;
 
 class Item_str_func :public Item_func
@@ -1868,4 +1871,46 @@ class Item_func_wsrep_sync_wait_upto: public Item_int_func
 };
 #endif /* WITH_WSREP */
 
+class Item_func_tochar :public Item_str_func
+{
+  bool check_arguments() const
+  {
+    return args[0]->check_type_can_return_date(func_name()) ||
+           check_argument_types_can_return_text(1, arg_count);
+  }
+
+  const MY_LOCALE *locale;
+  int fixed_length;
+  String warning_message;
+
+  /* when datetime format models is parsing, use short integers to
+   * represent the format models and store in fmt_array.
+   */
+  short fmt_array[MAX_DATETIME_FORMAT_MODEL_LEN+1];
+
+public:
+  Item_func_tochar(THD *thd, Item *a, Item *b):
+    Item_str_func(thd, a, b), locale(0)
+  {
+    /* NOTE: max length of waring message is 64 */
+    warning_message.alloc(64);
+    warning_message.length(0);
+  }
+  ~Item_func_tochar() { warning_message.free(); }
+  String *val_str(String *str);
+  const char *func_name() const { return "to_char"; }
+  bool fix_length_and_dec();
+  bool parse_format_string(const String *format, uint *fmt_len);
+
+  bool check_vcol_func_processor(void *arg)
+  {
+    if (arg_count > 2)
+      return false;
+    return mark_unsupported_function(func_name(), "()", arg, VCOL_SESSION_FUNC);
+  }
+
+  Item *get_copy(THD *thd)
+  { return get_item_copy<Item_func_tochar>(thd, this); }
+};
+
 #endif /* ITEM_STRFUNC_INCLUDED */
diff --git a/sql/oracle_compat.h b/sql/oracle_compat.h
new file mode 100644
index 00000000000..87027a8530c
--- /dev/null
+++ b/sql/oracle_compat.h
@@ -0,0 +1,86 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates.
+   Copyright (c) 2011, 2020, MariaDB
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1335  USA */
+
+#ifndef ORACLE_COMPAT_INCLUDE
+#define ORACLE_COMPAT_INCLUDE
+
+/*
+ * Oracle has many formatting models, we list all but only part of them are implemented,
+ * because some models depend on oracle functions which mariadb is not supported.
+ *
+ * moudels for datetime, used by TO_CHAR/TO_DATE
+ * To prevent conflict with the ascii code of the separator, such as: ':', '%', '?'
+ * the starting value of the formatting unit is 128
+ */
+#define FMT_BASE       128
+#define FMT_AD         FMT_BASE+1
+#define FMT_AD_DOT     FMT_BASE+2
+#define FMT_AM         FMT_BASE+3
+#define FMT_AM_DOT     FMT_BASE+4
+#define FMT_BC         FMT_BASE+5
+#define FMT_BC_DOT     FMT_BASE+6
+#define FMT_CC         FMT_BASE+7
+#define FMT_SCC        FMT_BASE+8
+#define FMT_D          FMT_BASE+9
+#define FMT_DAY        FMT_BASE+10
+#define FMT_DD         FMT_BASE+11
+#define FMT_DDD        FMT_BASE+12
+#define FMT_DL         FMT_BASE+13
+#define FMT_DS         FMT_BASE+14
+#define FMT_DY         FMT_BASE+15
+#define FMT_E          FMT_BASE+16
+#define FMT_EE         FMT_BASE+17
+#define FMT_FF         FMT_BASE+18
+#define FMT_FM         FMT_BASE+19
+#define FMT_FX         FMT_BASE+20
+#define FMT_HH         FMT_BASE+21
+#define FMT_HH12       FMT_BASE+22
+#define FMT_HH24       FMT_BASE+23
+#define FMT_IW         FMT_BASE+24
+#define FMT_I          FMT_BASE+25
+#define FMT_IY         FMT_BASE+26
+#define FMT_IYY        FMT_BASE+27
+#define FMT_IYYY       FMT_BASE+28
+#define FMT_J          FMT_BASE+29
+#define FMT_MI         FMT_BASE+30
+#define FMT_MM         FMT_BASE+31
+#define FMT_MON        FMT_BASE+32
+#define FMT_MONTH      FMT_BASE+33
+#define FMT_PM         FMT_BASE+34
+#define FMT_PM_DOT     FMT_BASE+35
+#define FMT_RM         FMT_BASE+37
+#define FMT_RR         FMT_BASE+38
+#define FMT_RRRR       FMT_BASE+39
+#define FMT_SS         FMT_BASE+40
+#define FMT_SSSSSS     FMT_BASE+41
+#define FMT_TS         FMT_BASE+42
+#define FMT_TZD        FMT_BASE+43
+#define FMT_TZH        FMT_BASE+44
+#define FMT_TZM        FMT_BASE+45
+#define FMT_TZR        FMT_BASE+46
+#define FMT_W          FMT_BASE+47
+#define FMT_WW         FMT_BASE+48
+#define FMT_X          FMT_BASE+49
+#define FMT_Y          FMT_BASE+50
+#define FMT_YY         FMT_BASE+51
+#define FMT_YYY        FMT_BASE+52
+#define FMT_YYYY       FMT_BASE+53
+#define FMT_YYYY_COMMA FMT_BASE+54
+#define FMT_YEAR       FMT_BASE+55
+#define FMT_SYYYY      FMT_BASE+56
+#define FMT_SYEAR      FMT_BASE+57
+
+#endif
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 2785a178e6e..361e94cacb2 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -7971,3 +7971,5 @@ ER_NOT_ALLOWED_IN_THIS_CONTEXT
         eng "'%-.128s' is not allowed in this context"
 ER_DATA_WAS_COMMITED_UNDER_ROLLBACK
         eng "Engine %s does not support rollback. Changes were committed during rollback call"
+ER_ORACLE_COMPAT_FUNCTION_ERROR
+        eng "Oracle compatibility function error: %s"
