New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MDEV-23485: Change table to merge engine may cause table data lost. #1651
base: 10.6
Are you sure you want to change the base?
Conversation
Nice simple case of preventing data loss. Well done.mysql-test/main/merge.test Can I request:
Keep up the good work. Much appreciated. |
@grooverdan I have retry this case on the 10.1 branch and have the same problem. the result is : Welcome to the MariaDB monitor. Commands end with ; or \g. Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MariaDB [(none)]> use zbdba MariaDB [zbdba]> insert into zbdba() values(1, 'aa'); MariaDB [zbdba]> insert into zbdba() values(2, 'bb'); MariaDB [zbdba]> insert into zbdba() values(3, 'cc'); MariaDB [zbdba]> select * from zbdba; MariaDB [zbdba]> ALTER TABLE zbdba ENGINE = MRG_MYISAM; MariaDB [zbdba]> select * from zbdba; MariaDB [zbdba]> When I add test case in the merge test suite, I have found there have another test suite rely on ALTER TABLE .... engine=MERGE or ALTER TABLE ..... UNION(t1, t2). this will make them falied. the failed test suite is: you can find the detail error log in the CI build. should I fix all of the problem test suite? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking firstly at rpl.rpl_row_merge_engine selecting all the output from the merge table at the end
CREATE TABLE t1 (a int) ENGINE=MyISAM;
CREATE TABLE t2 (a int) ENGINE=MyISAM;
INSERT INTO t1 VALUES (1), (2), (3);
INSERT INTO t2 VALUES (4), (5), (6);
# Changed a little to check also an issue reported on BUG#20574550
CREATE TEMPORARY TABLE IF NOT EXISTS tt1_merge LIKE t1;
ALTER TABLE tt1_merge ENGINE=MERGE UNION (t2, t1);
SELECT * FROM tt1_merge;
The result is
$ mysql-test/mtr --mem rpl.rpl_row_merge_engine
...
@@ -7,6 +7,14 @@
INSERT INTO t2 VALUES (4), (5), (6);
CREATE TEMPORARY TABLE IF NOT EXISTS tt1_merge LIKE t1;
ALTER TABLE tt1_merge ENGINE=MERGE UNION (t2, t1);
+SELECT * FROM tt1_merge;
+a
+4
+5
+6
+1
+2
+3
CREATE TABLE t1_merge LIKE tt1_merge;
Indicating that a myisam table originally can be ALTER TABLE
to a merge table correctly.
As such it seems this current patch is insufficient. Some detailed check in an existing ha_myisammrg::iXXXalter_table
function on the source table type and returning an existing cannot covert error if it cannot be converted.
@grooverdan ok, I will check it and fix it. |
Thinking a bit more as to what constitutes a valid alter table where
Alternate interpretations welcome. If this is getting too complicated I won't be offended I you drop it. |
@grooverdan If we want to change a table to merge engine, the original table should't be merge engine table and the table have no rows.I can't find a good way to confirm table have rows or not. and now I can just use the init_read_record method to get record from table to confirm it.Is there have some good way? |
Yes, handler api to check for a row is right. |
@grooverdan I have add this logic and run the test, I find threre have a test case which change a MyISAM engine to merge engine and the original table have rows.the test case is in the main.merge test suite. and the test statement is: --echo # shall we should change this test case? |
That test has a goal of showing that a previous myisam_mrg table can changed to an operational myisam tables. Followed |
@grooverdan I have commit, please help review it, thanks. |
sql/sql_table.cc
Outdated
* should't be merge engine table and the table have no rows, otherwise | ||
* may cause table data lost. */ | ||
if (table->file->ht->db_type != DB_TYPE_MRG_MYISAM && | ||
create_info->db_type->db_type == DB_TYPE_MRG_MYISAM) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please keep the comments in a consistent style to existing comments.
Syntax formatting of if
statements have a {
on the new line.
sql/sql_table.cc
Outdated
DBUG_RETURN(true); | ||
} | ||
|
||
while (likely(!(error= info.read_record()))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this is just an if
statement then? If so you don't need the variable have_rows
and can flow it to the error condition below.
Can you remove the likely
here. Its just harder to read and really isn't a performance critical path.
How are you doing to deal with errors here?
sql/sql_table.cc
Outdated
if (have_rows) { | ||
DBUG_PRINT("info", ("The original table is not merge table " | ||
"and have rows doesn't support alter")); | ||
my_error(ER_ILLEGAL_HA, MYF(0), hton_name(table->s->db_type())->str, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DBUG_PRINT statements are only in debug builds for helping developers out. We really want this error message to be for the user.
As we using such precise works in a error message like ER_ALTER_OPERATION_NOT_SUPPORTED. The arguements to it however should be syntax and not some readable string otherwise translation won't be possible
.
So please:
a) rebase on 10.6 - we can't add new error message in a GA release
b) create a new error message sql/share/errmsg-utf8.txt
The alter errors are pushed back in the context of the original alter table statement so the db/table name isn't that significant. The wording of error should be close to the DBUG_PRINT text however make it have have a similar read to the other ER_ALTER_OPERATION_NOT_SUPPORTED errors.
sql/sql_table.cc
Outdated
create_info->db_type->db_type == DB_TYPE_MRG_MYISAM) { | ||
READ_RECORD info; | ||
bool have_rows= false; | ||
if (init_read_record(&info, thd, table, NULL, NULL, 1, 1, FALSE)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
init_read_record
needs to be freed with end_read_record(&info)
sql/sql_table.cc
Outdated
@@ -10130,6 +10130,33 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, | |||
DBUG_RETURN(true); | |||
} | |||
|
|||
/* MDEV-23485: Change table to merge engine may cause table data lost. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any earlier hooks into the ha_myisammrg handler that are called so this addition can be in the handler rather than the main alter table implementation? I ask because I assume you've looked. I'll check back on this alter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have checked there have no hooks are called in the ha_myisammrg handler, except the inplace support check.
mysql-test/main/merge.result
Outdated
@@ -3445,6 +3447,8 @@ c1 c2 | |||
411 421 | |||
511 521 | |||
# | |||
# MDEV-23485 - Change table to merge engine may cause table data lost. | |||
truncate table m1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
its not necessary to comment this line or the above truncate table. Its covered in the commit message and could distract a reader to think this is part of the MDEV-23485 test .
The test block below is good but you'll notice in the results that the error message isn't that useful for a user (hence review requesting another error message).
@grooverdan I have update the commit, and how rebase to 10.6? I have try but will cause the older commit to merge together. |
I've changed the based of the PR. Try do |
630120d
to
0c9f3eb
Compare
@grooverdan thanks, I have done. |
Problem: If we want to change a table to merge engine, the original table is not merge engine table and have rows may cause table data lost. Solution: Add check in the mysql_alter_table method, check if the original table want to change to merge engine, it should't be merge engine and have no rows. closes #1651
I got some feedback from @montywi There is couple of issues with the patch:
How this should be done:
We do miss the case to alter a empty table to MERGE, but I don't think that is important. Better to be consistent. |
Okay, I was busy preparing a share before, now I have time to solve this problem. |
@grooverdan, instead of new It was originally created for MERGE and used with the comment "We do not copy data for MERGE tables. Only the children have data". It seems logical that if only children have data that one should not be able to alter InnoDB table (with data) into a MERGE table (without data) as it'll cause a data loss. Since its introduction (in 2005?) this flag Or, let's put it another way, altering InnoDB table into a MERGE table (into any other engine, really) would inevitably involve copying of the data. And |
Problem: If we want to change a table to merge engine, the original table is not merge engine table may cause table data lost. Solution: Add check in the mysql_alter_table method, check the other table engine can't change to merge engine.
@grooverdan @vuvova HA_NO_COPY_ON_ALTER seem to can be used here. Use HTON flag to control other table engine can't change to merge engine will cause some test case failed, I have fixed it and please help review it. |
When I create a Innodb storage engine table, and I change it to MRG_MYISAM engine, the table data will be lost.
MariaDB [zbdba]> show create table zbdba\G
*************************** 1. row ***************************
Table: zbdba
Create Table: CREATE TABLE
zbdba
(id
int(11) NOT NULL,name
varchar(20) DEFAULT NULL,PRIMARY KEY (
id
)) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.000 sec)
MariaDB [zbdba]>
MariaDB [zbdba]>
MariaDB [zbdba]> insert into zbdba() values(1, 'aa');
Query OK, 1 row affected (0.001 sec)
MariaDB [zbdba]> insert into zbdba() values(2, 'bb');
Query OK, 1 row affected (0.001 sec)
MariaDB [zbdba]> insert into zbdba() values(3, 'cc');
Query OK, 1 row affected (0.001 sec)
MariaDB [zbdba]> select * from zbdba;
+----+------+
| id | name |
+----+------+
| 1 | aa |
| 2 | bb |
| 3 | cc |
+----+------+
3 rows in set (0.001 sec)
MariaDB [zbdba]>
MariaDB [zbdba]>
MariaDB [zbdba]> ALTER TABLE zbdba ENGINE = MRG_MYISAM;
Query OK, 0 rows affected (0.005 sec)
Records: 0 Duplicates: 0 Warnings: 0
MariaDB [zbdba]> select * from zbdba;
Empty set (0.000 sec)