Skip to content

Commit

Permalink
MDEV-33442 REPAIR TABLE corrupts UUIDs
Browse files Browse the repository at this point in the history
Problem:
REPAIR TABLE executed for a pre-MDEV-29959 table (with the old UUID format)
updated the server version in the FRM file without rewriting the data,
so it created a new FRM for old UUIDs. After that MariaDB could not
read UUIDs correctly.

Fix:

- Adding a new virtual method in class Type_handler:

      virtual bool type_handler_for_implicit_upgrade() const;

  * For the up-to-date data types it returns "this".
  * For the data types which need to be implicitly upgraded
    during REPAIR TABLE or ALTER TABLE, it returns a pointer
    to a new replacement data type handler.

    Old VARCHAR and old UUID type handlers override this method.
    See more comments below.

- Changing the semantics of the method

    Type_handler::Column_definition_implicit_upgrade(Column_definition *c)

  to the opposite, so now:
    * c->type_handler() references the old data type (to upgrade from)
    * "this" references the new data type (to upgrade to).

  Before this change Column_definition_implicit_upgrade() was supposed
  to be called with the old data type handler (to upgrade from).

  Renaming the method to Column_definition_implicit_upgrade_to_this(),
  to avoid automatic merges in this method.

  Reflecting this change in Create_field::upgrade_data_types().

- Replacing the hard-coded data type tests inside handler::check_old_types()
  to a call for the new virtual method
  Type_handler::type_handler_for_implicit_upgrade()

- Overriding Type_handler_fbt::type_handler_for_implicit_upgrade()
  to call a new method FbtImpl::type_handler_for_implicit_upgrade().

  Reasoning:

  Type_handler_fbt is a template, so it has access only to "this".
  So in case of UUID data types, the type handler for old UUID
  knows nothing about the type handler of new UUID inside sql_type_fixedbin.h.
  So let's have Type_handler_fbt delegate type_handler_for_implicit_upgrade()
  to its Type_collection, which knows both new UUID and old UUID.

- Adding Type_collection_uuid::type_handler_for_implicit_upgrade().
  It returns a pointer to the new UUID type handler.

- Overriding Type_handler_var_string::type_handler_for_implicit_upgrade()
  to return a pointer to type_handler_varchar (true VARCHAR).

- Cleanup: these two methods:
    handler::check_old_types()
    handler::ha_check_for_upgrade()
  were always called consequently.
  So moving the call for check_old_types() inside ha_check_for_upgrade(),
  and making check_old_types() private.

- Cleanup: removing the "bool varchar" parameter from fill_alter_inplace_info(),
  as its not used any more.
  • Loading branch information
abarkov committed Feb 26, 2024
1 parent 74c97a4 commit 7246054
Show file tree
Hide file tree
Showing 17 changed files with 1,154 additions and 64 deletions.
173 changes: 173 additions & 0 deletions mysql-test/main/type_decimal.result
Original file line number Diff line number Diff line change
Expand Up @@ -1323,3 +1323,176 @@ SET sql_mode=DEFAULT;
#
# End of 10.4 tests
#
#
# Start of 10.11 tests
#
#
# MDEV-33442 REPAIR TABLE corrupts UUIDs
#
CREATE PROCEDURE show_table()
BEGIN
SHOW CREATE TABLE t1;
SELECT VERSION FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='t1' AND TABLE_SCHEMA='test';
SELECT * FROM t1 ORDER BY a;
END;
$$
# Upgrade using REPAIR
CALL show_table;
Table Create Table
t1 CREATE TABLE `t1` (
`a` decimal(10,2)/*old*/ DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
VERSION
9
a
123.45
123.46
123.47
CHECK TABLE t1 FOR UPGRADE;
Table Op Msg_type Msg_text
test.t1 check error Upgrade required. Please do "REPAIR TABLE `t1`" or dump/reload to fix it!
CALL show_table;
Table Create Table
t1 CREATE TABLE `t1` (
`a` decimal(10,2)/*old*/ DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
VERSION
9
a
123.45
123.46
123.47
CHECK TABLE t1 FOR UPGRADE;
Table Op Msg_type Msg_text
test.t1 check error Upgrade required. Please do "REPAIR TABLE `t1`" or dump/reload to fix it!
CALL show_table;
Table Create Table
t1 CREATE TABLE `t1` (
`a` decimal(10,2)/*old*/ DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
VERSION
9
a
123.45
123.46
123.47
REPAIR TABLE t1;
Table Op Msg_type Msg_text
test.t1 repair status OK
# Expect old decimal, as it does not implicitly upgrade to new decimal
CALL show_table;
Table Create Table
t1 CREATE TABLE `t1` (
`a` decimal(10,2)/*old*/ DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
VERSION
10
a
123.45
123.46
123.47
CHECK TABLE t1 FOR UPGRADE;
Table Op Msg_type Msg_text
test.t1 check status OK
CALL show_table;
Table Create Table
t1 CREATE TABLE `t1` (
`a` decimal(10,2)/*old*/ DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
VERSION
10
a
123.45
123.46
123.47
DROP TABLE t1;
# Upgrade using ALTER, adding a table COMMENT
# Upgrade a 10.11.4 table using ALTER, adding a table COMMENT
CALL show_table;
Table Create Table
t1 CREATE TABLE `t1` (
`a` decimal(10,2)/*old*/ DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
VERSION
9
a
123.45
123.46
123.47
# ALTER..INPLACE should fail - the FRM file is too old and needs upgrade
ALTER IGNORE TABLE t1 ALGORITHM=INPLACE, COMMENT 'test10';
ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY
ALTER IGNORE TABLE t1 COMMENT 'test11';
# Expect old decimal, as it does not implicitly upgrade to new decimal
CALL show_table;
Table Create Table
t1 CREATE TABLE `t1` (
`a` decimal(10,2)/*old*/ DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci COMMENT='test11'
VERSION
10
a
123.45
123.46
123.47
# Now ALTER..INPLACE should work
ALTER IGNORE TABLE t1 ALGORITHM=INPLACE, COMMENT 'test12';
CALL show_table;
Table Create Table
t1 CREATE TABLE `t1` (
`a` decimal(10,2)/*old*/ DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci COMMENT='test12'
VERSION
10
a
123.45
123.46
123.47
DROP TABLE t1;
# Upgrade using ALTER, adding a column DEFAULT
# Upgrade a 10.11.4 table using ALTER, adding a table COMMENT
CALL show_table;
Table Create Table
t1 CREATE TABLE `t1` (
`a` decimal(10,2)/*old*/ DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
VERSION
9
a
123.45
123.46
123.47
# ALTER..INPLACE should fail - the FRM file is too old and needs upgrade
ALTER IGNORE TABLE t1 ALGORITHM=INPLACE, MODIFY a DECIMAL(10,2) DEFAULT 10;
ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY
ALTER IGNORE TABLE t1 MODIFY a DECIMAL(10,2) DEFAULT 11;
# Expect new decimal, as we explicitly redefined the data type
CALL show_table;
Table Create Table
t1 CREATE TABLE `t1` (
`a` decimal(10,2) DEFAULT 11.00
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
VERSION
10
a
123.45
123.46
123.47
# Now ALTER..INPLACE should work
ALTER IGNORE TABLE t1 ALGORITHM=INPLACE, MODIFY a DECIMAL(10,2) DEFAULT 12;
CALL show_table;
Table Create Table
t1 CREATE TABLE `t1` (
`a` decimal(10,2) DEFAULT 12.00
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
VERSION
10
a
123.45
123.46
123.47
DROP TABLE t1;
DROP PROCEDURE show_table;
#
# End of 10.11 tests
#
90 changes: 90 additions & 0 deletions mysql-test/main/type_decimal.test
Original file line number Diff line number Diff line change
Expand Up @@ -826,3 +826,93 @@ SET sql_mode=DEFAULT;
--echo #
--echo # End of 10.4 tests
--echo #

--echo #
--echo # Start of 10.11 tests
--echo #

--echo #
--echo # MDEV-33442 REPAIR TABLE corrupts UUIDs
--echo #

DELIMITER $$;
CREATE PROCEDURE show_table()
BEGIN
SHOW CREATE TABLE t1;
SELECT VERSION FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='t1' AND TABLE_SCHEMA='test';
SELECT * FROM t1 ORDER BY a;
END;
$$
DELIMITER ;$$

--echo # Upgrade using REPAIR

--copy_file std_data/old_decimal/t1dec102.frm $MYSQLD_DATADIR/test/t1.frm
--copy_file std_data/old_decimal/t1dec102.MYD $MYSQLD_DATADIR/test/t1.MYD
--copy_file std_data/old_decimal/t1dec102.MYI $MYSQLD_DATADIR/test/t1.MYI
CALL show_table;

CHECK TABLE t1 FOR UPGRADE;
CALL show_table;

CHECK TABLE t1 FOR UPGRADE;
CALL show_table;

REPAIR TABLE t1;
--echo # Expect old decimal, as it does not implicitly upgrade to new decimal
CALL show_table;

CHECK TABLE t1 FOR UPGRADE;
CALL show_table;
DROP TABLE t1;


--echo # Upgrade using ALTER, adding a table COMMENT

--echo # Upgrade a 10.11.4 table using ALTER, adding a table COMMENT
--copy_file std_data/old_decimal/t1dec102.frm $MYSQLD_DATADIR/test/t1.frm
--copy_file std_data/old_decimal/t1dec102.MYD $MYSQLD_DATADIR/test/t1.MYD
--copy_file std_data/old_decimal/t1dec102.MYI $MYSQLD_DATADIR/test/t1.MYI
CALL show_table;

--echo # ALTER..INPLACE should fail - the FRM file is too old and needs upgrade
--error ER_ALTER_OPERATION_NOT_SUPPORTED
ALTER IGNORE TABLE t1 ALGORITHM=INPLACE, COMMENT 'test10';
ALTER IGNORE TABLE t1 COMMENT 'test11';
-- echo # Expect old decimal, as it does not implicitly upgrade to new decimal
CALL show_table;

--echo # Now ALTER..INPLACE should work
ALTER IGNORE TABLE t1 ALGORITHM=INPLACE, COMMENT 'test12';
CALL show_table;

DROP TABLE t1;


--echo # Upgrade using ALTER, adding a column DEFAULT

--echo # Upgrade a 10.11.4 table using ALTER, adding a table COMMENT
--copy_file std_data/old_decimal/t1dec102.frm $MYSQLD_DATADIR/test/t1.frm
--copy_file std_data/old_decimal/t1dec102.MYD $MYSQLD_DATADIR/test/t1.MYD
--copy_file std_data/old_decimal/t1dec102.MYI $MYSQLD_DATADIR/test/t1.MYI
CALL show_table;

--echo # ALTER..INPLACE should fail - the FRM file is too old and needs upgrade
--error ER_ALTER_OPERATION_NOT_SUPPORTED
ALTER IGNORE TABLE t1 ALGORITHM=INPLACE, MODIFY a DECIMAL(10,2) DEFAULT 10;
ALTER IGNORE TABLE t1 MODIFY a DECIMAL(10,2) DEFAULT 11;
--echo # Expect new decimal, as we explicitly redefined the data type
CALL show_table;

--echo # Now ALTER..INPLACE should work
ALTER IGNORE TABLE t1 ALGORITHM=INPLACE, MODIFY a DECIMAL(10,2) DEFAULT 12;
CALL show_table;

DROP TABLE t1;

DROP PROCEDURE show_table;


--echo #
--echo # End of 10.11 tests
--echo #
Loading

0 comments on commit 7246054

Please sign in to comment.