diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index d8d56832be9..445613eb579 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -108,6 +108,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ../sql/sql_explain.cc ../sql/sql_explain.h ../sql/sql_analyze_stmt.cc ../sql/sql_analyze_stmt.h ../sql/compat56.cc + ../sql/sql_schema.cc ../sql/sql_type.cc ../sql/sql_type.h ../sql/sql_mode.cc ../sql/table_cache.cc ../sql/mf_iocache_encr.cc diff --git a/mysql-test/main/lowercase_fs_off.result b/mysql-test/main/lowercase_fs_off.result index f2a8ec14641..3f0b08a78c4 100644 --- a/mysql-test/main/lowercase_fs_off.result +++ b/mysql-test/main/lowercase_fs_off.result @@ -158,3 +158,13 @@ show triggers like '%T1%'; Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation drop table t1; set GLOBAL sql_mode=default; +# +# MDEV-19632 Replication aborts with ER_SLAVE_CONVERSION_FAILED upon CREATE ... SELECT in ORACLE mode +# +# Compatibility schema names respect the filesystem case sensitivity +CREATE TABLE t1 (a MARIADB_SCHEMA.date); +ERROR HY000: Unknown data type: 'MARIADB_SCHEMA.date' +CREATE TABLE t1 (a Mariadb_schema.date); +ERROR HY000: Unknown data type: 'Mariadb_schema.date' +CREATE TABLE t1 (a mariadb_schema.date); +DROP TABLE t1; diff --git a/mysql-test/main/lowercase_fs_off.test b/mysql-test/main/lowercase_fs_off.test index 7c5811f9cc3..f2e5dafd9d4 100644 --- a/mysql-test/main/lowercase_fs_off.test +++ b/mysql-test/main/lowercase_fs_off.test @@ -129,3 +129,18 @@ let $datadir= `select @@datadir`; remove_file $datadir/mysql_upgrade_info; set GLOBAL sql_mode=default; + + +--echo # +--echo # MDEV-19632 Replication aborts with ER_SLAVE_CONVERSION_FAILED upon CREATE ... SELECT in ORACLE mode +--echo # + +--echo # Compatibility schema names respect the filesystem case sensitivity + +--error ER_UNKNOWN_ERROR +CREATE TABLE t1 (a MARIADB_SCHEMA.date); +--error ER_UNKNOWN_ERROR +CREATE TABLE t1 (a Mariadb_schema.date); + +CREATE TABLE t1 (a mariadb_schema.date); +DROP TABLE t1; diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 3e34a4a72a4..5a900f5108b 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -179,6 +179,7 @@ my @DEFAULT_SUITES= qw( binlog_encryption- csv- compat/oracle- + compat/maxdb- encryption- federated- funcs_1- diff --git a/mysql-test/suite/compat/maxdb/rpl_mariadb_timestamp.result b/mysql-test/suite/compat/maxdb/rpl_mariadb_timestamp.result new file mode 100644 index 00000000000..7c2012945c9 --- /dev/null +++ b/mysql-test/suite/compat/maxdb/rpl_mariadb_timestamp.result @@ -0,0 +1,65 @@ +include/master-slave.inc +[connection master] +# +# MDEV-19632 Replication aborts with ER_SLAVE_CONVERSION_FAILED upon CREATE ... SELECT in ORACLE mode +# +SET timestamp=UNIX_TIMESTAMP('2001-01-01 10:00:00'); +SET sql_mode=DEFAULT; +CREATE TABLE t1 (a TIMESTAMP); +INSERT INTO t1 VALUES (NULL); +INSERT INTO t1 VALUES ('2001-01-01 10:20:30'); +SET sql_mode=MAXDB; +CREATE TABLE t2 SELECT * FROM t1; +SET timestamp=DEFAULT; +include/show_binlog_events.inc +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a TIMESTAMP) +master-bin.000001 # Gtid # # BEGIN GTID #-#-# +master-bin.000001 # Annotate_rows # # INSERT INTO t1 VALUES (NULL) +master-bin.000001 # Table_map # # table_id: # (test.t1) +master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F +master-bin.000001 # Query # # COMMIT +master-bin.000001 # Gtid # # BEGIN GTID #-#-# +master-bin.000001 # Annotate_rows # # INSERT INTO t1 VALUES ('2001-01-01 10:20:30') +master-bin.000001 # Table_map # # table_id: # (test.t1) +master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F +master-bin.000001 # Query # # COMMIT +master-bin.000001 # Gtid # # BEGIN GTID #-#-# +master-bin.000001 # Query # # use `test`; CREATE TABLE "t2" ( + "a" mariadb_schema.timestamp NOT NULL DEFAULT current_timestamp() +) +master-bin.000001 # Annotate_rows # # CREATE TABLE t2 SELECT * FROM t1 +master-bin.000001 # Table_map # # table_id: # (test.t2) +master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F +master-bin.000001 # Query # # COMMIT +connection slave; +SELECT * FROM t1; +a +2001-01-01 10:00:00 +2001-01-01 10:20:30 +SET sql_mode=DEFAULT; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` timestamp NOT NULL DEFAULT current_timestamp() +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SET sql_mode=MAXDB; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE "t1" ( + "a" mariadb_schema.timestamp NOT NULL DEFAULT current_timestamp() +) +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE "t2" ( + "a" mariadb_schema.timestamp NOT NULL DEFAULT current_timestamp() +) +connection master; +DROP TABLE t1, t2; +include/rpl_end.inc diff --git a/mysql-test/suite/compat/maxdb/rpl_mariadb_timestamp.test b/mysql-test/suite/compat/maxdb/rpl_mariadb_timestamp.test new file mode 100644 index 00000000000..50663229937 --- /dev/null +++ b/mysql-test/suite/compat/maxdb/rpl_mariadb_timestamp.test @@ -0,0 +1,34 @@ +--source include/have_binlog_format_row.inc +--source include/master-slave.inc + +--echo # +--echo # MDEV-19632 Replication aborts with ER_SLAVE_CONVERSION_FAILED upon CREATE ... SELECT in ORACLE mode +--echo # + +SET timestamp=UNIX_TIMESTAMP('2001-01-01 10:00:00'); +SET sql_mode=DEFAULT; +CREATE TABLE t1 (a TIMESTAMP); +INSERT INTO t1 VALUES (NULL); +INSERT INTO t1 VALUES ('2001-01-01 10:20:30'); +SET sql_mode=MAXDB; +CREATE TABLE t2 SELECT * FROM t1; +SET timestamp=DEFAULT; + +--let $binlog_file = LAST +source include/show_binlog_events.inc; + + +--sync_slave_with_master +SELECT * FROM t1; +SET sql_mode=DEFAULT; +SHOW CREATE TABLE t1; +SHOW CREATE TABLE t2; + +SET sql_mode=MAXDB; +SHOW CREATE TABLE t1; +SHOW CREATE TABLE t2; + +--connection master +DROP TABLE t1, t2; + +--source include/rpl_end.inc diff --git a/mysql-test/suite/compat/maxdb/type_timestamp.result b/mysql-test/suite/compat/maxdb/type_timestamp.result new file mode 100644 index 00000000000..355a4e28414 --- /dev/null +++ b/mysql-test/suite/compat/maxdb/type_timestamp.result @@ -0,0 +1,53 @@ +# +# MDEV-19632 Replication aborts with ER_SLAVE_CONVERSION_FAILED upon CREATE ... SELECT in ORACLE mode +# +SET sql_mode=DEFAULT; +CREATE TABLE t1 ( +def_timestamp TIMESTAMP, +mdb_timestamp mariadb_schema.TIMESTAMP, +ora_timestamp oracle_schema.TIMESTAMP, +max_timestamp maxdb_schema.TIMESTAMP +); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `def_timestamp` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + `mdb_timestamp` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', + `ora_timestamp` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', + `max_timestamp` datetime DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SET sql_mode=MAXDB; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE "t1" ( + "def_timestamp" mariadb_schema.timestamp NOT NULL DEFAULT current_timestamp(), + "mdb_timestamp" mariadb_schema.timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', + "ora_timestamp" mariadb_schema.timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', + "max_timestamp" datetime DEFAULT NULL +) +DROP TABLE t1; +SET sql_mode=MAXDB; +CREATE TABLE t1 ( +def_timestamp TIMESTAMP, +mdb_timestamp mariadb_schema.TIMESTAMP, +ora_timestamp oracle_schema.TIMESTAMP, +max_timestamp maxdb_schema.TIMESTAMP +); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE "t1" ( + "def_timestamp" datetime DEFAULT NULL, + "mdb_timestamp" mariadb_schema.timestamp NOT NULL DEFAULT current_timestamp(), + "ora_timestamp" mariadb_schema.timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', + "max_timestamp" datetime DEFAULT NULL +) +SET sql_mode=DEFAULT; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `def_timestamp` datetime DEFAULT NULL, + `mdb_timestamp` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + `ora_timestamp` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', + `max_timestamp` datetime DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; diff --git a/mysql-test/suite/compat/maxdb/type_timestamp.test b/mysql-test/suite/compat/maxdb/type_timestamp.test new file mode 100644 index 00000000000..cd60ffc0795 --- /dev/null +++ b/mysql-test/suite/compat/maxdb/type_timestamp.test @@ -0,0 +1,29 @@ +--echo # +--echo # MDEV-19632 Replication aborts with ER_SLAVE_CONVERSION_FAILED upon CREATE ... SELECT in ORACLE mode +--echo # + + +SET sql_mode=DEFAULT; +CREATE TABLE t1 ( + def_timestamp TIMESTAMP, + mdb_timestamp mariadb_schema.TIMESTAMP, + ora_timestamp oracle_schema.TIMESTAMP, + max_timestamp maxdb_schema.TIMESTAMP +); +SHOW CREATE TABLE t1; +SET sql_mode=MAXDB; +SHOW CREATE TABLE t1; +DROP TABLE t1; + + +SET sql_mode=MAXDB; +CREATE TABLE t1 ( + def_timestamp TIMESTAMP, + mdb_timestamp mariadb_schema.TIMESTAMP, + ora_timestamp oracle_schema.TIMESTAMP, + max_timestamp maxdb_schema.TIMESTAMP +); +SHOW CREATE TABLE t1; +SET sql_mode=DEFAULT; +SHOW CREATE TABLE t1; +DROP TABLE t1; diff --git a/mysql-test/suite/compat/oracle/r/rpl_mariadb_date.result b/mysql-test/suite/compat/oracle/r/rpl_mariadb_date.result new file mode 100644 index 00000000000..9aca6fa3542 --- /dev/null +++ b/mysql-test/suite/compat/oracle/r/rpl_mariadb_date.result @@ -0,0 +1,86 @@ +include/master-slave.inc +[connection master] +SET SQL_MODE=DEFAULT; +CREATE TABLE t1 (a DATE); +INSERT INTO t1 VALUES (NULL); +INSERT INTO t1 VALUES ('2001-01-01'); +SET SQL_MODE= ORACLE; +CREATE TABLE t2 SELECT * FROM t1; +include/show_binlog_events.inc +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a DATE) +master-bin.000001 # Gtid # # BEGIN GTID #-#-# +master-bin.000001 # Annotate_rows # # INSERT INTO t1 VALUES (NULL) +master-bin.000001 # Table_map # # table_id: # (test.t1) +master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F +master-bin.000001 # Query # # COMMIT +master-bin.000001 # Gtid # # BEGIN GTID #-#-# +master-bin.000001 # Annotate_rows # # INSERT INTO t1 VALUES ('2001-01-01') +master-bin.000001 # Table_map # # table_id: # (test.t1) +master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F +master-bin.000001 # Query # # COMMIT +master-bin.000001 # Gtid # # BEGIN GTID #-#-# +master-bin.000001 # Query # # use `test`; CREATE TABLE "t2" ( + "a" mariadb_schema.date DEFAULT NULL +) +master-bin.000001 # Annotate_rows # # CREATE TABLE t2 SELECT * FROM t1 +master-bin.000001 # Table_map # # table_id: # (test.t2) +master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F +master-bin.000001 # Query # # COMMIT +SET SQL_MODE= DEFAULT; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` date DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` date DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SET SQL_MODE= ORACLE; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE "t1" ( + "a" mariadb_schema.date DEFAULT NULL +) +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE "t2" ( + "a" mariadb_schema.date DEFAULT NULL +) +connection slave; +SELECT * FROM t1; +a +NULL +2001-01-01 +SELECT * FROM t2; +a +NULL +2001-01-01 +SET SQL_MODE= DEFAULT; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` date DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` date DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SET SQL_MODE= ORACLE; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE "t1" ( + "a" mariadb_schema.date DEFAULT NULL +) +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE "t2" ( + "a" mariadb_schema.date DEFAULT NULL +) +connection master; +DROP TABLE t1, t2; +include/rpl_end.inc diff --git a/mysql-test/suite/compat/oracle/r/type_date.result b/mysql-test/suite/compat/oracle/r/type_date.result index 0989fc593d2..40d2a834056 100644 --- a/mysql-test/suite/compat/oracle/r/type_date.result +++ b/mysql-test/suite/compat/oracle/r/type_date.result @@ -6,3 +6,153 @@ t1 CREATE TABLE "t1" ( "a" datetime DEFAULT NULL ) DROP TABLE t1; +# +# MDEV-19632 Replication aborts with ER_SLAVE_CONVERSION_FAILED upon CREATE ... SELECT in ORACLE mode +# +SET sql_mode=DEFAULT; +CREATE TABLE t1 (a unknown.DATE); +ERROR HY000: Unknown data type: 'unknown.date' +SET sql_mode=DEFAULT; +CREATE TABLE t1 ( +def_date DATE, +mdb_date mariadb_schema.DATE, +ora_date oracle_schema.DATE, +max_date maxdb_schema.DATE +); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `def_date` date DEFAULT NULL, + `mdb_date` date DEFAULT NULL, + `ora_date` datetime DEFAULT NULL, + `max_date` date DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SET sql_mode=ORACLE; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE "t1" ( + "def_date" mariadb_schema.date DEFAULT NULL, + "mdb_date" mariadb_schema.date DEFAULT NULL, + "ora_date" datetime DEFAULT NULL, + "max_date" mariadb_schema.date DEFAULT NULL +) +DROP TABLE t1; +SET sql_mode=ORACLE; +CREATE TABLE t1 ( +def_date DATE, +mdb_date mariadb_schema.DATE, +ora_date oracle_schema.DATE, +max_date maxdb_schema.DATE +); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE "t1" ( + "def_date" datetime DEFAULT NULL, + "mdb_date" mariadb_schema.date DEFAULT NULL, + "ora_date" datetime DEFAULT NULL, + "max_date" mariadb_schema.date DEFAULT NULL +) +SET sql_mode=DEFAULT; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `def_date` datetime DEFAULT NULL, + `mdb_date` date DEFAULT NULL, + `ora_date` datetime DEFAULT NULL, + `max_date` date DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +# +# ALTER..MODIFY and ALTER..CHANGE understand qualifiers +# +SET sql_mode=DEFAULT; +CREATE TABLE t1 (a DATE); +INSERT INTO t1 VALUES ('2001-01-01'); +SET sql_mode=ORACLE; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE "t1" ( + "a" mariadb_schema.date DEFAULT NULL +) +SELECT * FROM t1; +a +2001-01-01 +ALTER TABLE t1 MODIFY a DATE; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE "t1" ( + "a" datetime DEFAULT NULL +) +SELECT * FROM t1; +a +2001-01-01 00:00:00 +ALTER TABLE t1 MODIFY a mariadb_schema.DATE; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE "t1" ( + "a" mariadb_schema.date DEFAULT NULL +) +SELECT * FROM t1; +a +2001-01-01 +ALTER TABLE t1 MODIFY a oracle_schema.DATE; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE "t1" ( + "a" datetime DEFAULT NULL +) +SELECT * FROM t1; +a +2001-01-01 00:00:00 +ALTER TABLE t1 CHANGE a b mariadb_schema.DATE; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE "t1" ( + "b" mariadb_schema.date DEFAULT NULL +) +SELECT * FROM t1; +b +2001-01-01 +ALTER TABLE t1 CHANGE b a oracle_schema.DATE; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE "t1" ( + "a" datetime DEFAULT NULL +) +SELECT * FROM t1; +a +2001-01-01 00:00:00 +DROP TABLE t1; +# +# Qualified syntax is not supported yet in SP +# See MDEV-23353 Qualified data types in SP +# +SET sql_mode=ORACLE; +CREATE FUNCTION f1() RETURN mariadb_schema.DATE AS +BEGIN +RETURN CURRENT_DATE; +END; +$$ +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'mariadb_schema.DATE AS +BEGIN +RETURN CURRENT_DATE; +END' at line 1 +CREATE PROCEDURE p1(a mariadb_schema.DATE) AS +BEGIN +NULL; +END; +$$ +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ') AS +BEGIN +NULL; +END' at line 1 +CREATE PROCEDURE p1() AS +a mariadb_schema.DATE; +BEGIN +NULL; +END; +$$ +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '; +BEGIN +NULL; +END' at line 2 diff --git a/mysql-test/suite/compat/oracle/t/rpl_mariadb_date.test b/mysql-test/suite/compat/oracle/t/rpl_mariadb_date.test new file mode 100644 index 00000000000..b2aff23333b --- /dev/null +++ b/mysql-test/suite/compat/oracle/t/rpl_mariadb_date.test @@ -0,0 +1,38 @@ +--source include/have_binlog_format_row.inc +--source include/master-slave.inc + +SET SQL_MODE=DEFAULT; +CREATE TABLE t1 (a DATE); +INSERT INTO t1 VALUES (NULL); +INSERT INTO t1 VALUES ('2001-01-01'); + +SET SQL_MODE= ORACLE; +CREATE TABLE t2 SELECT * FROM t1; + +--let $binlog_file = LAST +source include/show_binlog_events.inc; + +SET SQL_MODE= DEFAULT; +SHOW CREATE TABLE t1; +SHOW CREATE TABLE t2; + +SET SQL_MODE= ORACLE; +SHOW CREATE TABLE t1; +SHOW CREATE TABLE t2; + +--sync_slave_with_master +SELECT * FROM t1; +SELECT * FROM t2; + +SET SQL_MODE= DEFAULT; +SHOW CREATE TABLE t1; +SHOW CREATE TABLE t2; + +SET SQL_MODE= ORACLE; +SHOW CREATE TABLE t1; +SHOW CREATE TABLE t2; + +# Cleanup +--connection master +DROP TABLE t1, t2; +--source include/rpl_end.inc diff --git a/mysql-test/suite/compat/oracle/t/type_date.test b/mysql-test/suite/compat/oracle/t/type_date.test index 61f7aa53944..36a5e99795f 100644 --- a/mysql-test/suite/compat/oracle/t/type_date.test +++ b/mysql-test/suite/compat/oracle/t/type_date.test @@ -2,3 +2,102 @@ SET sql_mode=ORACLE; CREATE TABLE t1 (a DATE); SHOW CREATE TABLE t1; DROP TABLE t1; + + +--echo # +--echo # MDEV-19632 Replication aborts with ER_SLAVE_CONVERSION_FAILED upon CREATE ... SELECT in ORACLE mode +--echo # + +SET sql_mode=DEFAULT; +--error ER_UNKNOWN_ERROR +CREATE TABLE t1 (a unknown.DATE); + + +SET sql_mode=DEFAULT; +CREATE TABLE t1 ( + def_date DATE, + mdb_date mariadb_schema.DATE, + ora_date oracle_schema.DATE, + max_date maxdb_schema.DATE +); +SHOW CREATE TABLE t1; +SET sql_mode=ORACLE; +SHOW CREATE TABLE t1; +DROP TABLE t1; + + +SET sql_mode=ORACLE; +CREATE TABLE t1 ( + def_date DATE, + mdb_date mariadb_schema.DATE, + ora_date oracle_schema.DATE, + max_date maxdb_schema.DATE +); +SHOW CREATE TABLE t1; +SET sql_mode=DEFAULT; +SHOW CREATE TABLE t1; +DROP TABLE t1; + + +--echo # +--echo # ALTER..MODIFY and ALTER..CHANGE understand qualifiers +--echo # + +SET sql_mode=DEFAULT; +CREATE TABLE t1 (a DATE); +INSERT INTO t1 VALUES ('2001-01-01'); +SET sql_mode=ORACLE; +SHOW CREATE TABLE t1; +SELECT * FROM t1; + +ALTER TABLE t1 MODIFY a DATE; +SHOW CREATE TABLE t1; +SELECT * FROM t1; + +ALTER TABLE t1 MODIFY a mariadb_schema.DATE; +SHOW CREATE TABLE t1; +SELECT * FROM t1; + +ALTER TABLE t1 MODIFY a oracle_schema.DATE; +SHOW CREATE TABLE t1; +SELECT * FROM t1; + +ALTER TABLE t1 CHANGE a b mariadb_schema.DATE; +SHOW CREATE TABLE t1; +SELECT * FROM t1; + +ALTER TABLE t1 CHANGE b a oracle_schema.DATE; +SHOW CREATE TABLE t1; +SELECT * FROM t1; + +DROP TABLE t1; + +--echo # +--echo # Qualified syntax is not supported yet in SP +--echo # See MDEV-23353 Qualified data types in SP +--echo # + +SET sql_mode=ORACLE; +DELIMITER $$; +# Change to this when merging to 10.5: +#--error ER_UNKNOWN_DATA_TYPE +--error ER_PARSE_ERROR +CREATE FUNCTION f1() RETURN mariadb_schema.DATE AS +BEGIN + RETURN CURRENT_DATE; +END; +$$ +--error ER_PARSE_ERROR +CREATE PROCEDURE p1(a mariadb_schema.DATE) AS +BEGIN + NULL; +END; +$$ +--error ER_PARSE_ERROR +CREATE PROCEDURE p1() AS + a mariadb_schema.DATE; +BEGIN + NULL; +END; +$$ +DELIMITER ;$$ diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index e7e0c055f8e..8ed75f86067 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -127,6 +127,7 @@ SET (SQL_SOURCE rpl_gtid.cc rpl_parallel.cc semisync.cc semisync_master.cc semisync_slave.cc semisync_master_ack_receiver.cc + sql_schema.cc sql_type.cc sql_mode.cc item_windowfunc.cc sql_window.cc sql_cte.cc diff --git a/sql/field.cc b/sql/field.cc index f5daab00670..e011e01dce7 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -10407,6 +10407,19 @@ void Column_definition::set_attributes(const Lex_field_type_st &type, set_handler(type.type_handler()); charset= cs; +#if MYSQL_VERSION_ID > 100500 +#error When merging to 10.5, please move the code below to +#error Type_handler_timestamp_common::Column_definition_set_attributes() +#else + /* + Unlike other types TIMESTAMP fields are NOT NULL by default. + Unless --explicit-defaults-for-timestamp is given. + */ + if (!opt_explicit_defaults_for_timestamp && + type.type_handler()->field_type() == MYSQL_TYPE_TIMESTAMP) + flags|= NOT_NULL_FLAG; +#endif + if (type.length()) { int err; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 46c099dc8e2..0c0a1ec98ae 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1157,10 +1157,8 @@ extern "C" my_thread_id next_thread_id_noinline() #endif -const Type_handler *THD::type_handler_for_date() const +const Type_handler *THD::type_handler_for_datetime() const { - if (!(variables.sql_mode & MODE_ORACLE)) - return &type_handler_newdate; if (opt_mysql56_temporal_format) return &type_handler_datetime2; return &type_handler_datetime; diff --git a/sql/sql_class.h b/sql/sql_class.h index aa24f44dab1..5ced820a34d 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -3422,7 +3422,7 @@ public: { return !MY_TEST(variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES); } - const Type_handler *type_handler_for_date() const; + const Type_handler *type_handler_for_datetime() const; bool timestamp_to_TIME(MYSQL_TIME *ltime, my_time_t ts, ulong sec_part, ulonglong fuzzydate); inline my_time_t query_start() { return start_time; } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index e2e80cb21f6..89f74b6537b 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -237,6 +237,7 @@ void st_parsing_options::reset() { allows_variable= TRUE; + lookup_keywords_after_qualifier= false; } @@ -1539,7 +1540,10 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd) yylval->lex_str.str= (char*) get_ptr(); yylval->lex_str.length= 1; c= yyGet(); // should be '.' - next_state= MY_LEX_IDENT_START; // Next is ident (not keyword) + if (lex->parsing_options.lookup_keywords_after_qualifier) + next_state= MY_LEX_IDENT_OR_KEYWORD; + else + next_state= MY_LEX_IDENT_START; // Next is ident (not keyword) if (!ident_map[(uchar) yyPeek()]) // Probably ` or " next_state= MY_LEX_START; return((int) c); @@ -8286,3 +8290,31 @@ bool LEX::tvc_finalize_derived() current_select->linkage= DERIVED_TABLE_TYPE; return tvc_finalize(); } + + +bool LEX::map_data_type(const Lex_ident_sys_st &schema_name, + Lex_field_type_st *type) const +{ + const Schema *schema= schema_name.str ? + Schema::find_by_name(schema_name) : + Schema::find_implied(thd); + if (!schema) + { + char buf[128]; + const Name type_name= type->type_handler()->name(); + my_snprintf(buf, sizeof(buf), "%.*s.%.*s", + (int) schema_name.length, schema_name.str, + (int) type_name.length(), type_name.ptr()); +#if MYSQL_VERSION_ID > 100500 +#error Please remove the old code + my_error(ER_UNKNOWN_DATA_TYPE, MYF(0), buf); +#else + my_printf_error(ER_UNKNOWN_ERROR, "Unknown data type: '%-.64s'", + MYF(0), buf); +#endif + return true; + } + const Type_handler *mapped= schema->map_data_type(thd, type->type_handler()); + type->set_handler(mapped); + return false; +} diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 8b8a55e0c96..55929ed7df6 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -32,6 +32,7 @@ #include "sp.h" // enum stored_procedure_type #include "sql_tvc.h" #include "item.h" +#include "sql_schema.h" /* Used for flags of nesting constructs */ #define SELECT_NESTING_MAP_SIZE 64 @@ -2144,6 +2145,7 @@ private: struct st_parsing_options { bool allows_variable; + bool lookup_keywords_after_qualifier; st_parsing_options() { reset(); } void reset(); @@ -4052,6 +4054,9 @@ public: bool tvc_finalize(); bool tvc_finalize_derived(); + bool map_data_type(const Lex_ident_sys_st &schema, + Lex_field_type_st *type) const; + void mark_first_table_as_inserting(); }; diff --git a/sql/sql_schema.cc b/sql/sql_schema.cc new file mode 100644 index 00000000000..0bf4a63c2f8 --- /dev/null +++ b/sql/sql_schema.cc @@ -0,0 +1,80 @@ +/* + Copyright (c) 2020, MariaDB Corporation. + + 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 */ + +#include "mariadb.h" +#include "sql_type.h" +#include "sql_schema.h" +#include "sql_class.h" + +class Schema_oracle: public Schema +{ +public: + Schema_oracle(const LEX_CSTRING &name) + :Schema(name) + { } + const Type_handler *map_data_type(THD *thd, const Type_handler *src) + const + { + if (src == &type_handler_newdate) + return thd->type_handler_for_datetime(); + return src; + } +}; + + +class Schema_maxdb: public Schema +{ +public: + Schema_maxdb(const LEX_CSTRING &name) + :Schema(name) + { } + const Type_handler *map_data_type(THD *thd, const Type_handler *src) + const + { + if (src == &type_handler_timestamp || + src == &type_handler_timestamp2) + return thd->type_handler_for_datetime(); + return src; + } +}; + + +Schema mariadb_schema(Lex_cstring(STRING_WITH_LEN("mariadb_schema"))); +Schema_oracle oracle_schema(Lex_cstring(STRING_WITH_LEN("oracle_schema"))); +Schema_maxdb maxdb_schema(Lex_cstring(STRING_WITH_LEN("maxdb_schema"))); + + +Schema *Schema::find_by_name(const LEX_CSTRING &name) +{ + DBUG_ASSERT(name.str); + if (mariadb_schema.eq_name(name)) + return &mariadb_schema; + if (oracle_schema.eq_name(name)) + return &oracle_schema; + if (maxdb_schema.eq_name(name)) + return &maxdb_schema; + return NULL; +} + + +Schema *Schema::find_implied(THD *thd) +{ + if (thd->variables.sql_mode & MODE_ORACLE) + return &oracle_schema; + if (thd->variables.sql_mode & MODE_MAXDB) + return &maxdb_schema; + return &mariadb_schema; +} diff --git a/sql/sql_schema.h b/sql/sql_schema.h new file mode 100644 index 00000000000..7c8f284d526 --- /dev/null +++ b/sql/sql_schema.h @@ -0,0 +1,70 @@ +#ifndef SQL_SCHEMA_H_INCLUDED +#define SQL_SCHEMA_H_INCLUDED +/* + Copyright (c) 2020, MariaDB Corporation. + + 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 */ + +#include "mysqld.h" +#include "lex_string.h" + +class Schema +{ + LEX_CSTRING m_name; +public: + Schema(const LEX_CSTRING &name) + :m_name(name) + { } + virtual ~Schema() { } + const LEX_CSTRING &name() const { return m_name; } + virtual const Type_handler *map_data_type(THD *thd, const Type_handler *src) + const + { + return src; + } + /* + For now we have *hard-coded* compatibility schemas: + schema_mariadb, schema_oracle, schema_maxdb. + But eventually we'll turn then into real databases on disk. + So the code below compares names according to the filesystem + case sensitivity, like it is done for regular databases. + + Note, this is different to information_schema, whose name + is always case insensitive. This is intentional! + The assymetry will be gone when we'll implement SQL standard + regular and delimited identifiers. + */ + bool eq_name(const LEX_CSTRING &name) const + { +#if MYSQL_VERSION_ID > 100500 +#error Remove the old code + return !table_alias_charset->strnncoll(m_name.str, m_name.length, + name.str, name.length); +#else + // Please remove this when merging to 10.5 + return !table_alias_charset->coll->strnncoll(table_alias_charset, + (const uchar *) m_name.str, + m_name.length, + (const uchar *) name.str, + name.length, FALSE); +#endif + } + static Schema *find_by_name(const LEX_CSTRING &name); + static Schema *find_implied(THD *thd); +}; + + +extern Schema mariadb_schema; + +#endif // SQL_SCHEMA_H_INCLUDED diff --git a/sql/sql_show.cc b/sql/sql_show.cc index c6e801b7976..a108c607ba3 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2224,6 +2224,13 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, append_identifier(thd, packet, &field->field_name); packet->append(' '); + const Type_handler *th= field->type_handler(); + const Schema *implied_schema= Schema::find_implied(thd); + if (th != implied_schema->map_data_type(thd, th)) + { + packet->append(th->schema()->name(), system_charset_info); + packet->append(STRING_WITH_LEN("."), system_charset_info); + } type.set(tmp, sizeof(tmp), system_charset_info); field->sql_type(type); packet->append(type.ptr(), type.length(), system_charset_info); diff --git a/sql/sql_string.h b/sql/sql_string.h index 46b13dabbcc..f56540c2975 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -488,6 +488,10 @@ public: } bool append(const char *s, size_t size); bool append(const char *s, size_t arg_length, CHARSET_INFO *cs); + bool append(const LEX_CSTRING &s, CHARSET_INFO *cs) + { + return append(s.str, s.length, cs); + } bool append_ulonglong(ulonglong val); bool append_longlong(longlong val); bool append(IO_CACHE* file, uint32 arg_length); diff --git a/sql/sql_type.cc b/sql/sql_type.cc index fae4c1d8301..9d3a47adfa5 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -69,6 +69,12 @@ Type_handler_geometry type_handler_geometry; #endif +Schema *Type_handler::schema() const +{ + return &mariadb_schema; +} + + bool Type_handler_data::init() { #ifdef HAVE_SPATIAL diff --git a/sql/sql_type.h b/sql/sql_type.h index 75e0bac33c3..f20ccff0752 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -75,6 +75,7 @@ struct Schema_specification_st; struct TABLE; struct SORT_FIELD_ATTR; class Vers_history_point; +class Schema; /** @@ -1062,6 +1063,7 @@ public: Type_handler *aggregate_for_num_op_traditional(const Type_handler *h1, const Type_handler *h2); + virtual Schema *schema() const; virtual const Name name() const= 0; virtual enum_field_types field_type() const= 0; virtual enum_field_types real_field_type() const { return field_type(); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index edcd2171721..04a19d6922e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1862,6 +1862,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %type int_type real_type %type type_with_opt_collate field_type + qualified_field_type field_type_numeric field_type_string field_type_lob @@ -6729,10 +6730,12 @@ field_spec: lex->init_last_field(f, &$1, NULL); $$= f; + lex->parsing_options.lookup_keywords_after_qualifier= true; } field_type_or_serial opt_check_constraint { LEX *lex=Lex; + lex->parsing_options.lookup_keywords_after_qualifier= false; $$= $2; $$->check_constraint= $4; @@ -6751,7 +6754,7 @@ field_spec: ; field_type_or_serial: - field_type { Lex->last_field->set_attributes($1, Lex->charset); } + qualified_field_type { Lex->last_field->set_attributes($1, Lex->charset); } field_def | SERIAL_SYM { @@ -6917,6 +6920,18 @@ column_default_expr: } ; +qualified_field_type: + field_type + { + Lex->map_data_type(Lex_ident_sys(), &($$= $1)); + } + | sp_decl_ident '.' field_type + { + if (Lex->map_data_type($1, &($$= $3))) + MYSQL_YYABORT; + } + ; + field_type: field_type_numeric | field_type_temporal @@ -7038,7 +7053,7 @@ field_type_temporal: } $$.set(&type_handler_year, $2); } - | DATE_SYM { $$.set(thd->type_handler_for_date()); } + | DATE_SYM { $$.set(&type_handler_newdate); } | TIME_SYM opt_field_length { $$.set(opt_mysql56_temporal_format ? @@ -7048,31 +7063,14 @@ field_type_temporal: } | TIMESTAMP opt_field_length { - if (thd->variables.sql_mode & MODE_MAXDB) - $$.set(opt_mysql56_temporal_format ? - static_cast(&type_handler_datetime2) : - static_cast(&type_handler_datetime), - $2); - else - { - /* - Unlike other types TIMESTAMP fields are NOT NULL by default. - Unless --explicit-defaults-for-timestamp is given. - */ - if (!opt_explicit_defaults_for_timestamp) - Lex->last_field->flags|= NOT_NULL_FLAG; - $$.set(opt_mysql56_temporal_format ? - static_cast(&type_handler_timestamp2): - static_cast(&type_handler_timestamp), - $2); - } + $$.set(opt_mysql56_temporal_format ? + static_cast(&type_handler_timestamp2): + static_cast(&type_handler_timestamp), + $2); } | DATETIME opt_field_length { - $$.set(opt_mysql56_temporal_format ? - static_cast(&type_handler_datetime2) : - static_cast(&type_handler_datetime), - $2); + $$.set(thd->type_handler_for_datetime(), $2); } ; @@ -7400,14 +7398,14 @@ with_or_without_system: type_with_opt_collate: field_type opt_collate { - $$= $1; + Lex->map_data_type(Lex_ident_sys(), &($$= $1)); if ($2) { if (unlikely(!(Lex->charset= merge_charset_and_collation(Lex->charset, $2)))) MYSQL_YYABORT; } - Lex->last_field->set_attributes($1, Lex->charset); + Lex->last_field->set_attributes($$, Lex->charset); } ; diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 942f7892f04..a1eb74771ef 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -1260,6 +1260,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %type int_type real_type %type type_with_opt_collate field_type + qualified_field_type sp_param_type_with_opt_collate sp_param_field_type sp_param_field_type_string @@ -6573,10 +6574,12 @@ field_spec: lex->init_last_field(f, &$1, NULL); $$= f; + lex->parsing_options.lookup_keywords_after_qualifier= true; } field_type_or_serial opt_check_constraint { LEX *lex=Lex; + lex->parsing_options.lookup_keywords_after_qualifier= false; $$= $2; $$->check_constraint= $4; @@ -6595,7 +6598,7 @@ field_spec: ; field_type_or_serial: - field_type { Lex->last_field->set_attributes($1, Lex->charset); } + qualified_field_type { Lex->last_field->set_attributes($1, Lex->charset); } field_def | SERIAL_SYM { @@ -6761,6 +6764,18 @@ column_default_expr: } ; +qualified_field_type: + field_type + { + Lex->map_data_type(Lex_ident_sys(), &($$= $1)); + } + | sp_decl_ident '.' field_type + { + if (Lex->map_data_type($1, &($$= $3))) + MYSQL_YYABORT; + } + ; + field_type: field_type_numeric | field_type_temporal @@ -6934,7 +6949,7 @@ field_type_temporal: } $$.set(&type_handler_year, $2); } - | DATE_SYM { $$.set(thd->type_handler_for_date()); } + | DATE_SYM { $$.set(&type_handler_newdate); } | TIME_SYM opt_field_length { $$.set(opt_mysql56_temporal_format ? @@ -6944,31 +6959,14 @@ field_type_temporal: } | TIMESTAMP opt_field_length { - if (thd->variables.sql_mode & MODE_MAXDB) - $$.set(opt_mysql56_temporal_format ? - static_cast(&type_handler_datetime2) : - static_cast(&type_handler_datetime), - $2); - else - { - /* - Unlike other types TIMESTAMP fields are NOT NULL by default. - Unless --explicit-defaults-for-timestamp is given. - */ - if (!opt_explicit_defaults_for_timestamp) - Lex->last_field->flags|= NOT_NULL_FLAG; - $$.set(opt_mysql56_temporal_format ? - static_cast(&type_handler_timestamp2): - static_cast(&type_handler_timestamp), - $2); - } + $$.set(opt_mysql56_temporal_format ? + static_cast(&type_handler_timestamp2): + static_cast(&type_handler_timestamp), + $2); } | DATETIME opt_field_length { - $$.set(opt_mysql56_temporal_format ? - static_cast(&type_handler_datetime2) : - static_cast(&type_handler_datetime), - $2); + $$.set(thd->type_handler_for_datetime(), $2); } ; @@ -7324,27 +7322,28 @@ with_or_without_system: type_with_opt_collate: field_type opt_collate { - $$= $1; + Lex->map_data_type(Lex_ident_sys(), &($$= $1)); if ($2) { if (unlikely(!(Lex->charset= merge_charset_and_collation(Lex->charset, $2)))) MYSQL_YYABORT; } - Lex->last_field->set_attributes($1, Lex->charset); + Lex->last_field->set_attributes($$, Lex->charset); } ; sp_param_type_with_opt_collate: sp_param_field_type opt_collate { - $$= $1; + Lex->map_data_type(Lex_ident_sys(), &($$= $1)); + if ($2) { if (unlikely(!(Lex->charset= merge_charset_and_collation(Lex->charset, $2)))) MYSQL_YYABORT; } - Lex->last_field->set_attributes($1, Lex->charset); + Lex->last_field->set_attributes($$, Lex->charset); } ; diff --git a/sql/structs.h b/sql/structs.h index 12bd568d7c5..351847d00ec 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -610,6 +610,10 @@ public: { set(handler, 0, 0); } + void set_handler(const Type_handler *handler) + { + m_handler= handler; + } const Type_handler *type_handler() const { return m_handler; } }; diff --git a/storage/test_sql_discovery/mysql-test/sql_discovery/simple.result b/storage/test_sql_discovery/mysql-test/sql_discovery/simple.result index 23b7804638f..1feea5e47ee 100644 --- a/storage/test_sql_discovery/mysql-test/sql_discovery/simple.result +++ b/storage/test_sql_discovery/mysql-test/sql_discovery/simple.result @@ -82,7 +82,7 @@ select * from t1; ERROR HY000: Engine TEST_SQL_DISCOVERY failed to discover table `test`.`t1` with 'create table t1 (a uint)' show warnings; Level Code Message -Error 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'uint)' at line 1 +Error 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ')' at line 1 Error 1939 Engine TEST_SQL_DISCOVERY failed to discover table `test`.`t1` with 'create table t1 (a uint)' set @@test_sql_discovery_statement='t1:create table t1 (a int)'; select * from t1;