diff --git a/storage/innobase/data/data0type.cc b/storage/innobase/data/data0type.cc index a39c6d7408b6..b3975bd55dac 100644 --- a/storage/innobase/data/data0type.cc +++ b/storage/innobase/data/data0type.cc @@ -91,6 +91,23 @@ bool dtype_is_string_type( return false; } +#ifdef XTRABACKUP +/** Checks if the main data type is a real temporal type with fractional + * seconds +@param[in] type InnoDB main column type TINY, SHORT, +@return true if real temporal type */ +bool dtype_is_real_temporal_type(dd::enum_column_types type) { + switch (type) { + case dd::enum_column_types::TIME2: + case dd::enum_column_types::TIMESTAMP2: + case dd::enum_column_types::DATETIME2: + return true; + default: + return false; + } +} +#endif /* XTRABACKUP */ + /** Checks if a type is a binary string type. Note that for tables created with < 4.0.14, we do not know if a DATA_BLOB column is a BLOB or a TEXT column. For those DATA_BLOB columns this function currently returns false. diff --git a/storage/innobase/dict/dict0dd.cc b/storage/innobase/dict/dict0dd.cc index 3b7d2615a9c4..df01a301a7f7 100644 --- a/storage/innobase/dict/dict0dd.cc +++ b/storage/innobase/dict/dict0dd.cc @@ -809,7 +809,21 @@ dict_table_t *dd_table_create_on_dd_obj(const dd::Table *dd_table, charset_no = my_charset_bin.number; else if (dd_col->type() == dd::enum_column_types::JSON) charset_no = my_charset_utf8mb4_bin.number; - else if (dtype_is_string_type(mtype)) { + else if (dtype_is_real_temporal_type(dd_col->type())) { + /* + MySQL always stores real temporal fields in my_charset_numeric charset. + Refer to make_field() in sql/field.cc + + It is for the same reason, the .cfg file exported during FLUSH TABLE + FOR EXPORT always sets prtype using my_charset_numeric as the + collation. + + In order to remain consistent with server's behavior, we force temporal + datatypes to use my_charset_numeric until MySQL server provides support + for a separate charset/collation for temporal fields. + */ + charset_no = my_charset_numeric.number; + } else if (dtype_is_string_type(mtype)) { charset_no = static_cast(dd_col->collation_id()); } diff --git a/storage/innobase/include/data0type.h b/storage/innobase/include/data0type.h index 54b46f1103d3..8f9426dc8be8 100644 --- a/storage/innobase/include/data0type.h +++ b/storage/innobase/include/data0type.h @@ -35,6 +35,11 @@ this program; if not, write to the Free Software Foundation, Inc., #include "univ.i" +#ifdef XTRABACKUP +#include "sql/dd/types/column.h" // dd::enum_column_types + +#endif /* XTRABACKUP */ + extern ulint data_mysql_default_charset_coll; constexpr uint32_t DATA_MYSQL_BINARY_CHARSET_COLL = 63; @@ -315,6 +320,29 @@ ulint dtype_get_at_most_n_mbchars( @return true if string type */ bool dtype_is_string_type( ulint mtype); /*!< in: InnoDB main data type code: DATA_CHAR, ... */ + +#ifdef XTRABACKUP +/** Checks if the main data type is a real temporal type with fractional + seconds + + "real type" in MySQL has different meanings in different contexts. It can mean + + - a data type allowing fractional numbers for precision + - an underlying datatype used internally. + + For example binlog stores CHAR, VARCHAR, SET and ENUM as MYSQL_TYPE_STRING. + For such types, field->type() will be MYSQL_TYPE_STRING and + field->real_type() will return the actual underlying data type. + + However in the context of collation of temporal types, it is safe to assume + that real temporal type are the types which allow with fractional seconds + since ther are stored and displayed as DATA_FIXBINARY. + +@param[in] type InnoDB main column type TINY, SHORT, +@return true if real temporal type */ +bool dtype_is_real_temporal_type(dd::enum_column_types type); +#endif /* XTRABACKUP */ + /** Checks if a type is a binary string type. Note that for tables created with < 4.0.14, we do not know if a DATA_BLOB column is a BLOB or a TEXT column. For those DATA_BLOB columns this function currently returns false. diff --git a/storage/innobase/xtrabackup/test/bootstrap.sh b/storage/innobase/xtrabackup/test/bootstrap.sh index 87d062f6b3f8..5f8d4054a185 100755 --- a/storage/innobase/xtrabackup/test/bootstrap.sh +++ b/storage/innobase/xtrabackup/test/bootstrap.sh @@ -177,7 +177,7 @@ main () { TYPE="xtradb80" PXB_TYPE="release" -VERSION="8.0.34-26" +VERSION="8.0.35-27" DESTDIR="./server" parse_arguments PICK-ARGS-FROM-ARGV "$@" main diff --git a/storage/innobase/xtrabackup/test/t/xb_export.sh b/storage/innobase/xtrabackup/test/t/xb_export.sh index 55165cc344eb..0ac79d222baa 100644 --- a/storage/innobase/xtrabackup/test/t/xb_export.sh +++ b/storage/innobase/xtrabackup/test/t/xb_export.sh @@ -3,8 +3,8 @@ table_count=9 NUMBER_OF_CFG_FILES=11 if is_server_version_higher_than 8.0.28 then - NUMBER_OF_CFG_FILES=13 - table_count=11 + NUMBER_OF_CFG_FILES=15 + table_count=13 fi vlog "total table to test $table_count " @@ -64,12 +64,28 @@ function insert_data() { } function create_tables() { - ### check if we can import all the columns type in mysql - mysql -e 'create table test4(col1 bool, col2 int, col3 float, col4 double, - col5 timestamp, col6 long, col7 date, col8 time, col9 datetime, col10 year, - col11 varchar(20), col12 bit ,col13 decimal, col14 blob, col16 json, - col17 mediumtext, col18 enum("01","2"), col19 SET("0","1","2"), - col20 varchar(255) character set latin1 )' incremental_sample; + ### check if we can import all column types in mysql + mysql -e 'create table test4( + col1 bool, + col2 int, + col3 float, + col4 double, + col5 timestamp, + col6 long, + col7 date, + col8 time, + col9 datetime, + col10 year, + col11 varchar(20), + col12 bit, + col13 decimal, + col14 blob, + col16 json, + col17 mediumtext, + col18 enum("01","2"), + col19 SET("0","1","2"), + col20 varchar(255) character set latin1 + )' incremental_sample; mysql -e 'CREATE TABLE test5(a int, b int, PRIMARY KEY (a), KEY (b DESC))' incremental_sample mysql -e 'CREATE TABLE test6(a int)' incremental_sample @@ -93,6 +109,14 @@ function create_tables() { mysql -e 'ALTER TABLE test11 DROP COLUMN c3, ALGORITHM=INSTANT' incremental_sample fi + # To verify that import tablespace is successful for tables created using + # CREATE TABLE LIKE queries + mysql -e 'CREATE TABLE test12 LIKE test4' incremental_sample + + # To verify that import tablespace is successful for tables with a non + # default charset + mysql -e 'CREATE TABLE test13 LIKE test4' incremental_sample + mysql -e 'ALTER TABLE test13 CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci' incremental_sample } @@ -163,7 +187,34 @@ xtrabackup --datadir=$mysql_datadir --prepare --export \ --target-dir=$backup_dir ls -tlr $backup_dir/incremental_sample/*cfg -cfg_count=`find $backup_dir/incremental_sample -name '*.cfg' | wc -l` +cfg_count1=`find $backup_dir/incremental_sample -name '*.cfg' | wc -l` + +# Get the timestamp of the .cfg file created in the first --export +timestamp_1=$(stat -c %Y "$backup_dir/incremental_sample/test1.cfg") + +# Run --prepare --export once again to verify that .cfg files are overwritten when it is run for the second time +xtrabackup --datadir=$mysql_datadir --prepare --export \ + --target-dir=$backup_dir + +ls -tlr $backup_dir/incremental_sample/*cfg +cfg_count2=`find $backup_dir/incremental_sample -name '*.cfg' | wc -l` + +# Get the timestamp of the .cfg file created in the second --export +timestamp_2=$(stat -c %Y "$backup_dir/incremental_sample/test1.cfg") + +# Compare the timestamps +if [ "$timestamp_2" -gt "$timestamp_1" ]; then + vlog "The .cfg generated by PXB in the second attempt has a newer timestamp." +else + vlog "The .cfg generated by PXB in the second attempt does not have a newer timestamp." + exit -1 +fi + +if [ $cfg_count1 != $cfg_count2 ]; then + vlog "Number of .cfg files created is different in the second attempt" +fi + +cfg_count=$cfg_count1 vlog "Verifying .cfg files in backup, expecting ${NUMBER_OF_CFG_FILES}." if [ $cfg_count -ne ${NUMBER_OF_CFG_FILES} ]