MDEV-19632 Replication aborts with ER_SLAVE_CONVERSION_FAILED upon CREATE ... SELECT in ORACLE mode

- Adding optional qualifiers to data types:
    CREATE TABLE t1 (a schema.DATE);
  Qualifiers now work only for three pre-defined schemas:

    mariadb_schema
    oracle_schema
    maxdb_schema

  These schemas are virtual (hard-coded) for now, but may turn into real
  databases on disk in the future.

- mariadb_schema.TYPE now always resolves to a true MariaDB data
  type TYPE without sql_mode specific translations.

- oracle_schema.DATE translates to MariaDB DATETIME.

- maxdb_schema.TIMESTAMP translates to MariaDB DATETIME.

- Fixing SHOW CREATE TABLE to use a qualifier for a data type TYPE
  if the current sql_mode translates TYPE to something else.

The above changes fix the reported problem, so this script:

    SET sql_mode=ORACLE;
    CREATE TABLE t2 AS SELECT mariadb_date_column FROM t1;

is now replicated as:

    SET sql_mode=ORACLE;
    CREATE TABLE t2 (mariadb_date_column mariadb_schema.DATE);

and the slave can unambiguously treat DATE as the true MariaDB DATE
without ORACLE specific translation to DATETIME.

Similar,

    SET sql_mode=MAXDB;
    CREATE TABLE t2 AS SELECT mariadb_timestamp_column FROM t1;

is now replicated as:

    SET sql_mode=MAXDB;
    CREATE TABLE t2 (mariadb_timestamp_column mariadb_schema.TIMESTAMP);

so the slave treats TIMESTAMP as the true MariaDB TIMESTAMP
without MAXDB specific translation to DATETIME.
This commit is contained in:
Alexander Barkov 2020-07-08 08:31:32 +04:00
parent a8458a2345
commit d63631c3fa
28 changed files with 860 additions and 60 deletions

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -179,6 +179,7 @@ my @DEFAULT_SUITES= qw(
binlog_encryption-
csv-
compat/oracle-
compat/maxdb-
encryption-
federated-
funcs_1-

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 ;$$

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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; }

View File

@ -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;
}

View File

@ -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();
};

80
sql/sql_schema.cc Normal file
View File

@ -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;
}

70
sql/sql_schema.h Normal file
View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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(); }

View File

@ -1862,6 +1862,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <type_handler> int_type real_type
%type <Lex_field_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);
$<create_field>$= 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;
$$= $<create_field>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<const Type_handler*>(&type_handler_datetime2) :
static_cast<const Type_handler*>(&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<const Type_handler*>(&type_handler_timestamp2):
static_cast<const Type_handler*>(&type_handler_timestamp),
$2);
}
$$.set(opt_mysql56_temporal_format ?
static_cast<const Type_handler*>(&type_handler_timestamp2):
static_cast<const Type_handler*>(&type_handler_timestamp),
$2);
}
| DATETIME opt_field_length
{
$$.set(opt_mysql56_temporal_format ?
static_cast<const Type_handler*>(&type_handler_datetime2) :
static_cast<const Type_handler*>(&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);
}
;

View File

@ -1260,6 +1260,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <type_handler> int_type real_type
%type <Lex_field_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);
$<create_field>$= 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;
$$= $<create_field>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<const Type_handler*>(&type_handler_datetime2) :
static_cast<const Type_handler*>(&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<const Type_handler*>(&type_handler_timestamp2):
static_cast<const Type_handler*>(&type_handler_timestamp),
$2);
}
$$.set(opt_mysql56_temporal_format ?
static_cast<const Type_handler*>(&type_handler_timestamp2):
static_cast<const Type_handler*>(&type_handler_timestamp),
$2);
}
| DATETIME opt_field_length
{
$$.set(opt_mysql56_temporal_format ?
static_cast<const Type_handler*>(&type_handler_datetime2) :
static_cast<const Type_handler*>(&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);
}
;

View File

@ -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; }
};

View File

@ -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;